diff --git a/composer.json b/composer.json
index 8612c77ac2..b7fcf0b50d 100644
--- a/composer.json
+++ b/composer.json
@@ -55,6 +55,9 @@
         "phpcs": "phpcs --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --",
         "phpcbf": "phpcbf --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --"
     },
+    "bin": [
+        "core/scripts/console"
+    ],
     "repositories": [
         {
             "type": "composer",
diff --git a/core/composer.json b/core/composer.json
index 4fee977c23..cdf777b88f 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -211,7 +211,14 @@
             "recurse": false,
             "replace": false,
             "merge-extra": false
-        }
+        },
+        "commands": [
+            "Drupal\\Core\\Command\\ClearCacheCommand",
+            "Drupal\\Core\\Command\\InstallCommand",
+            "Drupal\\Core\\Command\\QuickStartCommand",
+            "Drupal\\Core\\Command\\RunCronCommand",
+            "Drupal\\Core\\Command\\ServerCommand"
+        ]
     },
     "minimum-stability": "dev",
     "prefer-stable": true,
diff --git a/core/lib/Drupal/Core/Command/ClearCacheCommand.php b/core/lib/Drupal/Core/Command/ClearCacheCommand.php
new file mode 100644
index 0000000000..d1909bfa90
--- /dev/null
+++ b/core/lib/Drupal/Core/Command/ClearCacheCommand.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\Core\Command;
+
+use Drupal\Core\Console\DrupalAwareInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+/**
+ * Implements clear-cache console command.
+ */
+class ClearCacheCommand extends Command implements DrupalAwareInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this
+      ->setName('clear-cache')
+      ->setDescription('Clear all caches.')
+      ->setAliases(['cc']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    drupal_flush_all_caches();
+    $io = new SymfonyStyle($input, $output);
+    $io->success('Cache rebuild complete.');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Command/InstallCommand.php b/core/lib/Drupal/Core/Command/InstallCommand.php
index bff4bc471d..0a29654938 100644
--- a/core/lib/Drupal/Core/Command/InstallCommand.php
+++ b/core/lib/Drupal/Core/Command/InstallCommand.php
@@ -3,6 +3,8 @@
 namespace Drupal\Core\Command;
 
 use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Console\AutoLoaderAwareInterface;
+use Drupal\Core\Console\AutoLoaderAwareTrait;
 use Drupal\Core\Database\ConnectionNotDefinedException;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DrupalKernel;
@@ -22,31 +24,16 @@
  * @internal
  *   This command makes no guarantee of an API for Drupal extensions.
  */
-class InstallCommand extends Command {
+class InstallCommand extends Command implements AutoLoaderAwareInterface {
 
-  /**
-   * The class loader.
-   *
-   * @var object
-   */
-  protected $classLoader;
-
-  /**
-   * Constructs a new InstallCommand command.
-   *
-   * @param object $class_loader
-   *   The class loader.
-   */
-  public function __construct($class_loader) {
-    parent::__construct('install');
-    $this->classLoader = $class_loader;
-  }
+  use AutoLoaderAwareTrait;
 
   /**
    * {@inheritdoc}
    */
   protected function configure() {
-    $this->setName('install')
+    $this
+      ->setName('install')
       ->setDescription('Installs a Drupal demo site. This is not meant for production and might be too simple for custom development. It is a quick and easy way to get Drupal running.')
       ->addArgument('install-profile', InputArgument::OPTIONAL, 'Install profile to install the site in.')
       ->addOption('langcode', NULL, InputOption::VALUE_OPTIONAL, 'The language to install the site in.', 'en')
@@ -86,7 +73,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
       $install_profile = $this->selectProfile($io);
     }
 
-    return $this->install($this->classLoader, $io, $install_profile, $input->getOption('langcode'), $this->getSitePath(), $input->getOption('site-name'));
+    return $this->install($this->autoLoader, $io, $install_profile, $input->getOption('langcode'), $this->getSitePath(), $input->getOption('site-name'));
   }
 
   /**
@@ -96,10 +83,10 @@ protected function execute(InputInterface $input, OutputInterface $output) {
    */
   protected function isDrupalInstalled() {
     try {
-      $kernel = new DrupalKernel('prod', $this->classLoader, FALSE);
+      $kernel = new DrupalKernel('prod', $this->autoLoader, FALSE);
       $kernel::bootEnvironment();
       $kernel->setSitePath($this->getSitePath());
-      Settings::initialize($kernel->getAppRoot(), $kernel->getSitePath(), $this->classLoader);
+      Settings::initialize($kernel->getAppRoot(), $kernel->getSitePath(), $this->autoLoader);
       $kernel->boot();
     }
     catch (ConnectionNotDefinedException $e) {
diff --git a/core/lib/Drupal/Core/Command/RunCronCommand.php b/core/lib/Drupal/Core/Command/RunCronCommand.php
new file mode 100644
index 0000000000..f87698106c
--- /dev/null
+++ b/core/lib/Drupal/Core/Command/RunCronCommand.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\Core\Command;
+
+use Drupal\Core\Console\DrupalAwareInterface;
+use Drupal\Core\CronInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Implements run-cron console command.
+ */
+class RunCronCommand extends Command implements DrupalAwareInterface, ContainerInjectionInterface {
+
+  /**
+   * The Drupal core Cron service.
+   *
+   * @var \Drupal\Core\CronInterface
+   */
+  protected $cron;
+
+  /**
+   * Constructs RunCronDrupalCommand object.
+   *
+   * @param \Drupal\Core\CronInterface $cron
+   *   The Drupal Cron service.
+   */
+  public function __construct(CronInterface $cron) {
+    parent::__construct();
+    $this->cron = $cron;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('cron'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this
+      ->setName('run-cron')
+      ->setAliases(['cron'])
+      ->setDescription('Run cron.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $io = new SymfonyStyle($input, $output);
+    if ($this->cron->run()) {
+      $io->success('Cron run completed.');
+    }
+    else {
+      $io->getErrorStyle()->error('Cron run failed.');
+      return 1;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Command/ServerCommand.php b/core/lib/Drupal/Core/Command/ServerCommand.php
index 182167a4c4..01b3aa717c 100644
--- a/core/lib/Drupal/Core/Command/ServerCommand.php
+++ b/core/lib/Drupal/Core/Command/ServerCommand.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Core\Command;
 
+use Drupal\Core\Console\AutoLoaderAwareInterface;
+use Drupal\Core\Console\AutoLoaderAwareTrait;
 use Drupal\Core\Database\ConnectionNotDefinedException;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\DrupalKernelInterface;
@@ -23,31 +25,17 @@
  * @internal
  *   This command makes no guarantee of an API for Drupal extensions.
  */
-class ServerCommand extends Command {
+class ServerCommand extends Command implements AutoLoaderAwareInterface {
 
-  /**
-   * The class loader.
-   *
-   * @var object
-   */
-  protected $classLoader;
-
-  /**
-   * Constructs a new ServerCommand command.
-   *
-   * @param object $class_loader
-   *   The class loader.
-   */
-  public function __construct($class_loader) {
-    parent::__construct('server');
-    $this->classLoader = $class_loader;
-  }
+  use AutoLoaderAwareTrait;
 
   /**
    * {@inheritdoc}
    */
   protected function configure() {
-    $this->setDescription('Starts up a webserver for a site.')
+    $this
+      ->setName('server')
+      ->setDescription('Starts up a webserver for a site.')
       ->addOption('host', NULL, InputOption::VALUE_OPTIONAL, 'Provide a host for the server to run on.', '127.0.0.1')
       ->addOption('port', NULL, InputOption::VALUE_OPTIONAL, 'Provide a port for the server to run on. Will be determined automatically if none supplied.')
       ->addOption('suppress-login', 's', InputOption::VALUE_NONE, 'Disable opening a login URL in a browser.')
@@ -90,10 +78,10 @@ protected function execute(InputInterface $input, OutputInterface $output) {
    *   Exception thrown if kernel does not boot.
    */
   protected function boot() {
-    $kernel = new DrupalKernel('prod', $this->classLoader, FALSE);
+    $kernel = new DrupalKernel('prod', $this->autoLoader, FALSE);
     $kernel::bootEnvironment();
     $kernel->setSitePath($this->getSitePath());
-    Settings::initialize($kernel->getAppRoot(), $kernel->getSitePath(), $this->classLoader);
+    Settings::initialize($kernel->getAppRoot(), $kernel->getSitePath(), $this->autoLoader);
     $kernel->boot();
     // Some services require a request to work. For example, CommentManager.
     // This is needed as generating the URL fires up entity load hooks.
diff --git a/core/lib/Drupal/Core/Console/AutoLoaderAwareInterface.php b/core/lib/Drupal/Core/Console/AutoLoaderAwareInterface.php
new file mode 100644
index 0000000000..b3510b0c16
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/AutoLoaderAwareInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Composer\Autoload\ClassLoader;
+
+/**
+ * Defines an interface for classes that implement console command.
+ */
+interface AutoLoaderAwareInterface {
+
+  /**
+   * @param \Composer\Autoload\ClassLoader $autoLoader
+   *   The class loader.
+   */
+  public function setAutoLoader(ClassLoader $autoLoader);
+
+}
diff --git a/core/lib/Drupal/Core/Console/AutoLoaderAwareTrait.php b/core/lib/Drupal/Core/Console/AutoLoaderAwareTrait.php
new file mode 100644
index 0000000000..417b2e4b5a
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/AutoLoaderAwareTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Composer\Autoload\ClassLoader;
+
+/**
+ * Basic Implementation of AutoLoaderAwareInterface.
+ */
+trait AutoLoaderAwareTrait {
+
+  /**
+   * Class loader.
+   *
+   * @var \Composer\Autoload\ClassLoader
+   */
+  private $autoLoader;
+
+  /**
+   * Sets an autoloader
+   *
+   * @param \Composer\Autoload\ClassLoader $autoLoader
+   *   The class loader.
+   */
+  public function setAutoLoader(ClassLoader $autoLoader) {
+    $this->autoLoader = $autoLoader;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/BootstrapHandler.php b/core/lib/Drupal/Core/Console/BootstrapHandler.php
new file mode 100644
index 0000000000..c5bca4a0fc
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/BootstrapHandler.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+use Composer\Autoload\ClassLoader;
+use Drupal\Core\DrupalKernel;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Bootstrap Drupal enough so that we can run a console command.
+ */
+class BootstrapHandler {
+
+  /**
+   * The class loader.
+   *
+   * @var \Composer\Autoload\ClassLoader
+   */
+  protected $classLoader;
+
+  /**
+   * Construct a BootstrapHandler object.
+   *
+   * @param \Composer\Autoload\ClassLoader $class_loader
+   *   The class loader.
+   */
+  public function __construct(ClassLoader $class_loader) {
+    $this->classLoader = $class_loader;
+  }
+
+  /**
+   * Bootstraps Drupal.
+   *
+   * @return \Symfony\Component\DependencyInjection\ContainerInterface|null
+   *   Current service container or null if bootstrap failed.
+   */
+  public function bootstrap() {
+    try {
+      $request = Request::createFromGlobals();
+      $kernel = DrupalKernel::createFromRequest($request, $this->classLoader, 'prod');
+      $kernel->boot();
+      $kernel->preHandle($request);
+      return $kernel->getContainer();
+    }
+    catch (\Exception $exception) {
+      return NULL;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/CommandFactory.php b/core/lib/Drupal/Core/Console/CommandFactory.php
new file mode 100644
index 0000000000..67d07b75e5
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/CommandFactory.php
@@ -0,0 +1,102 @@
+<?php
+
+
+namespace Drupal\Core\Console;
+
+use Composer\Autoload\ClassLoader;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Console command factory.
+ */
+class CommandFactory {
+
+  /**
+   * Constructs a new CommandFactory object.
+   *
+   * @param \Composer\Autoload\ClassLoader $autoLoader
+   *   The class loader.
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface|null $container
+   *   The service container or null if Drupal is not bootstrapped.
+   */
+  public function __construct(ClassLoader $autoLoader, ?ContainerInterface $container) {
+    $this->autoLoader = $autoLoader;
+    $this->container = $container;
+  }
+
+  /**
+   * Finds and instantiates console commands.
+   *
+   * @return \Symfony\Component\Console\Command\Command[]
+   *   Array of console commands.
+   */
+  public function getCommands(): array {
+    $commands = [];
+
+    foreach ($this->getComposerFiles() as $file) {
+      $data_encoded = file_get_contents($file);
+      $data = json_decode($data_encoded);
+      if (!isset($data->extra->commands)) {
+        continue;
+      }
+
+      foreach ($data->extra->commands as $command_class) {
+        if ($this->container) {
+          $command = $this->container
+            ->get('class_resolver')
+            ->getInstanceFromDefinition($command_class);
+        }
+        elseif (!is_subclass_of($command_class, DrupalAwareInterface::class)) {
+          $command = new $command_class;
+        }
+        else {
+          continue;
+        }
+
+        if ($command instanceof AutoLoaderAwareInterface) {
+          $command->setAutoLoader($this->autoLoader);
+        }
+
+        $commands[] = $command;
+      }
+    }
+
+    if ($this->container) {
+      $this->container
+        ->get('module_handler')
+        ->alter('commands', $commands);
+    }
+
+    return $commands;
+  }
+
+  /**
+   * Returns list of composer.json files to decode.
+   */
+  private function getComposerFiles(): array {
+    $drupal_root = __DIR__ . '/../../../../..';
+
+    $composer_root = file_exists($drupal_root . '/composer.json') ? $drupal_root : $drupal_root . '/..';
+    $composer_files[] = $composer_root . '/composer.json';
+    $composer_files[] = $drupal_root . '/core/composer.json';
+
+    // Use location of class loader to determine path to vendor directory.
+    $autoloader_file = $this->autoLoader->findFile(get_class($this->autoLoader));
+    list($vendor_dir) = explode('/composer/composer/', $autoloader_file);
+
+    $composer_files = array_merge($composer_files, glob($vendor_dir . '/*/*/composer.json'));
+    if ($this->container) {
+      $module_handler = $this->container->get('module_handler');
+      foreach ($module_handler->getModuleDirectories() as $directory) {
+        $composer_files[] = $directory . '/composer.json';
+      }
+      $theme_handler = $this->container->get('theme_handler');
+      foreach ($theme_handler->listInfo() as $theme) {
+        $composer_files[] = $drupal_root . '/' . $theme->getPath() . '/composer.json';
+      }
+    }
+
+    return array_filter($composer_files, 'file_exists');
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Console/DrupalAwareInterface.php b/core/lib/Drupal/Core/Console/DrupalAwareInterface.php
new file mode 100644
index 0000000000..1bf12bf19e
--- /dev/null
+++ b/core/lib/Drupal/Core/Console/DrupalAwareInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Drupal\Core\Console;
+
+/**
+ * An interface for commands that need a fully bootstrapped Drupal installation.
+ */
+interface DrupalAwareInterface {
+
+}
diff --git a/core/scripts/console b/core/scripts/console
new file mode 100755
index 0000000000..59d22114aa
--- /dev/null
+++ b/core/scripts/console
@@ -0,0 +1,26 @@
+#!/usr/bin/env php
+<?php
+
+/**
+ * @file
+ * Contains the Drupal console.
+ */
+
+use Drupal\Core\Console\BootstrapHandler;
+use Drupal\Core\Console\CommandFactory;
+use Symfony\Component\Console\Application;
+
+if (PHP_SAPI !== 'cli') {
+  return;
+}
+
+$autoLoader = require_once  __DIR__ . '/../../autoload.php';
+
+$bootstrapHandler = new BootstrapHandler($autoLoader);
+$container = $bootstrapHandler->bootstrap();
+$commandFactory = new CommandFactory($autoLoader, $container);
+$commands = $commandFactory->getCommands();
+
+$application = new Application('Drupal', \Drupal::VERSION);
+$application->addCommands($commands);
+$application->run();
