diff --git a/core/INSTALL.txt b/core/INSTALL.txt
index 3ae63b882f..18df90ff3e 100644
--- a/core/INSTALL.txt
+++ b/core/INSTALL.txt
@@ -15,7 +15,7 @@ QUICKSTART
 ----------------------
 
 Prerequisites:
-- PHP 7.3.0 (or greater) (https://php.net).
+- PHP 7.4.0 (or greater) (https://php.net).
 
 In the instructions below, replace the version x.y.z with the specific version
 you wish to download. Example: 8.6.0.zip. You can find the latest stable version
@@ -48,8 +48,10 @@ Drupal requires:
 - A web server with PHP support, for example:
   - Apache 2.4.7 (or greater) (http://httpd.apache.org/).
   - Nginx 1.1 (or greater) (http://nginx.com/).
-- PHP 7.3.0 (or greater) (http://php.net/). For better security support it is
-  recommended to update to at least 7.3.13.
+- PHP 7.4.0 (or greater) (http://php.net/). (For backwards compatibility, it is
+  technically possible to install Drupal on PHP 7.3, but this is not supported
+  and may fail in a future release.) For better security support, update to at
+  least PHP 8.0.2.
 - One of the following databases:
   - MySQL 5.7.8 (or greater) (http://www.mysql.com/).
   - MariaDB 10.3.7 (or greater) (https://mariadb.org/). MariaDB is a fully
diff --git a/core/drupalci.yml b/core/drupalci.yml
index 413272eff3..6946525c3e 100644
--- a/core/drupalci.yml
+++ b/core/drupalci.yml
@@ -7,11 +7,6 @@
 build:
   assessment:
     testing:
-      # Run code quality checks.
-      container_command.commit-checks:
-        commands:
-          - "core/scripts/dev/commit-code-check.sh --drupalci"
-        halt-on-fail: true
       # run_tests task is executed several times in order of performance speeds.
       # halt-on-fail can be set on the run_tests tasks in order to fail fast.
       # suppress-deprecations is false in order to be alerted to usages of
@@ -21,29 +16,3 @@ build:
         testgroups: '--all'
         suppress-deprecations: false
         halt-on-fail: false
-      run_tests.kernel:
-        types: 'PHPUnit-Kernel'
-        testgroups: '--all'
-        suppress-deprecations: false
-        halt-on-fail: false
-      run_tests.build:
-        # Limit concurrency due to disk space concerns.
-        concurrency: 15
-        types: 'PHPUnit-Build'
-        testgroups: '--all'
-        suppress-deprecations: false
-        halt-on-fail: false
-      run_tests.functional:
-        types: 'PHPUnit-Functional'
-        testgroups: '--all'
-        suppress-deprecations: false
-        halt-on-fail: false
-      run_tests.javascript:
-        concurrency: 15
-        types: 'PHPUnit-FunctionalJavascript'
-        testgroups: '--all'
-        suppress-deprecations: false
-        halt-on-fail: false
-      # Run nightwatch testing.
-      # @see https://www.drupal.org/project/drupal/issues/2869825
-      nightwatchjs: {}
diff --git a/core/install.php b/core/install.php
index 422cc099c5..52cf865c4f 100644
--- a/core/install.php
+++ b/core/install.php
@@ -30,7 +30,7 @@
 // in the printed message to the user) whenever \Drupal::MINIMUM_PHP is
 // updated.
 if (version_compare(PHP_VERSION, '7.3.0') < 0) {
-  print 'Your PHP installation is too old. Drupal requires at least PHP 7.3.0. See the <a href="https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9#s-php-version-requirement">Environment requirements of Drupal 9</a> page for more information.';
+  print 'Your PHP installation is too old. Drupal installation requires at least PHP 7.3.0, and only PHP 7.4.0 and higher are fully supported. See the <a href="https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9#s-php-version-requirement">Environment requirements of Drupal 9</a> page for more information.';
   exit;
 }
 
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 6ab16c58dc..5317d46bd9 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -96,7 +96,7 @@ class Drupal {
    *   that Drupal no longer supports that PHP version.
    * - An error is shown in the status report that the PHP version is too old.
    */
-  const MINIMUM_SUPPORTED_PHP = '7.3.0';
+  const MINIMUM_SUPPORTED_PHP = '7.4.0';
 
   /**
    * Minimum allowed version of PHP for Drupal to be bootstrapped.
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 2e1bab4caa..6c1ff864a6 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -294,7 +294,7 @@ function system_requirements($phase) {
         ':php_requirements' => 'https://www.drupal.org/docs/9/how-drupal-9-is-made-and-what-is-included/environment-requirements-of-drupal-9#s-php-version-requirement',
       ]
     );
-    $requirements['php']['severity'] = REQUIREMENT_ERROR;
+    $requirements['php']['severity'] = REQUIREMENT_WARNING;
 
     // If the PHP version is also below the absolute minimum allowed, it's not
     // safe to continue with the requirements check.
diff --git a/core/tests/Drupal/Tests/Core/Command/QuickStartTest.php b/core/tests/Drupal/Tests/Core/Command/QuickStartTest.php
deleted file mode 100644
index 9da1c86b54..0000000000
--- a/core/tests/Drupal/Tests/Core/Command/QuickStartTest.php
+++ /dev/null
@@ -1,333 +0,0 @@
-<?php
-
-namespace Drupal\Tests\Core\Command;
-
-use Drupal\sqlite\Driver\Database\sqlite\Install\Tasks;
-use Drupal\Core\Test\TestDatabase;
-use Drupal\Tests\BrowserTestBase;
-use GuzzleHttp\Client;
-use GuzzleHttp\Cookie\CookieJar;
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\Process\PhpExecutableFinder;
-use Symfony\Component\Process\Process;
-
-/**
- * Tests the quick-start commands.
- *
- * These tests are run in a separate process because they load Drupal code via
- * an include.
- *
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
- * @requires extension pdo_sqlite
- *
- * @group Command
- */
-class QuickStartTest extends TestCase {
-
-  /**
-   * The PHP executable path.
-   *
-   * @var string
-   */
-  protected $php;
-
-  /**
-   * A test database object.
-   *
-   * @var \Drupal\Core\Test\TestDatabase
-   */
-  protected $testDb;
-
-  /**
-   * The Drupal root directory.
-   *
-   * @var string
-   */
-  protected $root;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setUp(): void {
-    parent::setUp();
-    $php_executable_finder = new PhpExecutableFinder();
-    $this->php = $php_executable_finder->find();
-    $this->root = dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__)), 2);
-    chdir($this->root);
-    if (!is_writable("{$this->root}/sites/simpletest")) {
-      $this->markTestSkipped('This test requires a writable sites/simpletest directory');
-    }
-    // Get a lock and a valid site path.
-    $this->testDb = new TestDatabase();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function tearDown(): void {
-    if ($this->testDb) {
-      $test_site_directory = $this->root . DIRECTORY_SEPARATOR . $this->testDb->getTestSitePath();
-      if (file_exists($test_site_directory)) {
-        // @todo use the tear down command from
-        //   https://www.drupal.org/project/drupal/issues/2926633
-        // Delete test site directory.
-        $this->fileUnmanagedDeleteRecursive($test_site_directory, [
-          BrowserTestBase::class,
-          'filePreDeleteCallback',
-        ]);
-      }
-    }
-    parent::tearDown();
-  }
-
-  /**
-   * Tests the quick-start command.
-   */
-  public function testQuickStartCommand() {
-    if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
-      $this->markTestSkipped();
-    }
-    if (version_compare(\SQLite3::version()['versionString'], Tasks::SQLITE_MINIMUM_VERSION) < 0) {
-      $this->markTestSkipped();
-    }
-
-    // Install a site using the standard profile to ensure the one time login
-    // link generation works.
-
-    $install_command = [
-      $this->php,
-      'core/scripts/drupal',
-      'quick-start',
-      'standard',
-      "--site-name='Test site {$this->testDb->getDatabasePrefix()}'",
-      '--suppress-login',
-    ];
-    $process = new Process($install_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
-    $process->setTimeout(500);
-    $process->start();
-    $guzzle = new Client();
-    $port = FALSE;
-    while ($process->isRunning()) {
-      if (preg_match('/127.0.0.1:(\d+)/', $process->getOutput(), $match)) {
-        $port = $match[1];
-        break;
-      }
-      // Wait for more output.
-      sleep(1);
-    }
-    // The progress bar uses STDERR to write messages.
-    $this->assertStringContainsString('Congratulations, you installed Drupal!', $process->getErrorOutput());
-    $this->assertNotFalse($port, "Web server running on port $port");
-
-    // Give the server a couple of seconds to be ready.
-    sleep(2);
-    $this->assertStringContainsString("127.0.0.1:$port/user/reset/1/", $process->getOutput());
-
-    // Generate a cookie so we can make a request against the installed site.
-    define('DRUPAL_TEST_IN_CHILD_SITE', FALSE);
-    chmod($this->testDb->getTestSitePath(), 0755);
-    $cookieJar = CookieJar::fromArray([
-      'SIMPLETEST_USER_AGENT' => drupal_generate_test_ua($this->testDb->getDatabasePrefix()),
-    ], '127.0.0.1');
-
-    $response = $guzzle->get('http://127.0.0.1:' . $port, ['cookies' => $cookieJar]);
-    $content = (string) $response->getBody();
-    $this->assertStringContainsString('Test site ' . $this->testDb->getDatabasePrefix(), $content);
-
-    // Stop the web server.
-    $process->stop();
-  }
-
-  /**
-   * Tests that the installer throws a requirement error on older PHP versions.
-   */
-  public function testPhpRequirement() {
-    if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) >= 0) {
-      $this->markTestSkipped();
-    }
-
-    $install_command = [
-      $this->php,
-      'core/scripts/drupal',
-      'quick-start',
-      'standard',
-      "--site-name='Test site {$this->testDb->getDatabasePrefix()}'",
-      '--suppress-login',
-    ];
-    $process = new Process($install_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
-    $process->setTimeout(500);
-    $process->start();
-    while ($process->isRunning()) {
-      // Wait for more output.
-      sleep(1);
-    }
-
-    $error_output = $process->getErrorOutput();
-    $this->assertStringContainsString('Your PHP installation is too old.', $error_output);
-    $this->assertStringContainsString('Drupal requires at least PHP', $error_output);
-    $this->assertStringContainsString(\Drupal::MINIMUM_SUPPORTED_PHP, $error_output);
-
-    // Stop the web server.
-    $process->stop();
-  }
-
-  /**
-   * Tests the quick-start commands.
-   */
-  public function testQuickStartInstallAndServerCommands() {
-    if (version_compare(phpversion(), \Drupal::MINIMUM_SUPPORTED_PHP) < 0) {
-      $this->markTestSkipped();
-    }
-    if (version_compare(\SQLite3::version()['versionString'], Tasks::SQLITE_MINIMUM_VERSION) < 0) {
-      $this->markTestSkipped();
-    }
-
-    // Install a site.
-    $install_command = [
-      $this->php,
-      'core/scripts/drupal',
-      'install',
-      'testing',
-      "--site-name='Test site {$this->testDb->getDatabasePrefix()}'",
-    ];
-    $install_process = new Process($install_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
-    $install_process->setTimeout(500);
-    $result = $install_process->run();
-    // The progress bar uses STDERR to write messages.
-    $this->assertStringContainsString('Congratulations, you installed Drupal!', $install_process->getErrorOutput());
-    $this->assertSame(0, $result);
-
-    // Run the PHP built-in webserver.
-    $server_command = [
-      $this->php,
-      'core/scripts/drupal',
-      'server',
-      '--suppress-login',
-    ];
-    $server_process = new Process($server_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
-    $server_process->start();
-    $guzzle = new Client();
-    $port = FALSE;
-    while ($server_process->isRunning()) {
-      if (preg_match('/127.0.0.1:(\d+)/', $server_process->getOutput(), $match)) {
-        $port = $match[1];
-        break;
-      }
-      // Wait for more output.
-      sleep(1);
-    }
-    $this->assertEquals('', $server_process->getErrorOutput());
-    $this->assertStringContainsString("127.0.0.1:$port/user/reset/1/", $server_process->getOutput());
-    $this->assertNotFalse($port, "Web server running on port $port");
-
-    // Give the server a couple of seconds to be ready.
-    sleep(2);
-
-    // Generate a cookie so we can make a request against the installed site.
-    define('DRUPAL_TEST_IN_CHILD_SITE', FALSE);
-    chmod($this->testDb->getTestSitePath(), 0755);
-    $cookieJar = CookieJar::fromArray([
-      'SIMPLETEST_USER_AGENT' => drupal_generate_test_ua($this->testDb->getDatabasePrefix()),
-    ], '127.0.0.1');
-
-    $response = $guzzle->get('http://127.0.0.1:' . $port, ['cookies' => $cookieJar]);
-    $content = (string) $response->getBody();
-    $this->assertStringContainsString('Test site ' . $this->testDb->getDatabasePrefix(), $content);
-
-    // Try to re-install over the top of an existing site.
-    $install_command = [
-      $this->php,
-      'core/scripts/drupal',
-      'install',
-      'testing',
-      "--site-name='Test another site {$this->testDb->getDatabasePrefix()}'",
-    ];
-    $install_process = new Process($install_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
-    $install_process->setTimeout(500);
-    $result = $install_process->run();
-    $this->assertStringContainsString('Drupal is already installed.', $install_process->getOutput());
-    $this->assertSame(0, $result);
-
-    // Ensure the site name has not changed.
-    $response = $guzzle->get('http://127.0.0.1:' . $port, ['cookies' => $cookieJar]);
-    $content = (string) $response->getBody();
-    $this->assertStringContainsString('Test site ' . $this->testDb->getDatabasePrefix(), $content);
-
-    // Stop the web server.
-    $server_process->stop();
-  }
-
-  /**
-   * Tests the install command with an invalid profile.
-   */
-  public function testQuickStartCommandProfileValidation() {
-    // Install a site using the standard profile to ensure the one time login
-    // link generation works.
-    $install_command = [
-      $this->php,
-      'core/scripts/drupal',
-      'quick-start',
-      'umami',
-      "--site-name='Test site {$this->testDb->getDatabasePrefix()}' --suppress-login",
-    ];
-    $process = new Process($install_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
-    $process->run();
-    $this->assertStringContainsString('\'umami\' is not a valid install profile. Did you mean \'demo_umami\'?', $process->getErrorOutput());
-  }
-
-  /**
-   * Tests the server command when there is no installation.
-   */
-  public function testServerWithNoInstall() {
-    $server_command = [
-      $this->php,
-      'core/scripts/drupal',
-      'server',
-      '--suppress-login',
-    ];
-    $server_process = new Process($server_command, NULL, ['DRUPAL_DEV_SITE_PATH' => $this->testDb->getTestSitePath()]);
-    $server_process->run();
-    $this->assertStringContainsString('No installation found. Use the \'install\' command.', $server_process->getErrorOutput());
-  }
-
-  /**
-   * Deletes all files and directories in the specified path recursively.
-   *
-   * Note this method has no dependencies on Drupal core to ensure that the
-   * test site can be torn down even if something in the test site is broken.
-   *
-   * @param string $path
-   *   A string containing either a URI or a file or directory path.
-   * @param callable $callback
-   *   (optional) Callback function to run on each file prior to deleting it and
-   *   on each directory prior to traversing it. For example, can be used to
-   *   modify permissions.
-   *
-   * @return bool
-   *   TRUE for success or if path does not exist, FALSE in the event of an
-   *   error.
-   *
-   * @see \Drupal\Core\File\FileSystemInterface::deleteRecursive()
-   */
-  protected function fileUnmanagedDeleteRecursive($path, $callback = NULL) {
-    if (isset($callback)) {
-      call_user_func($callback, $path);
-    }
-    if (is_dir($path)) {
-      $dir = dir($path);
-      while (($entry = $dir->read()) !== FALSE) {
-        if ($entry == '.' || $entry == '..') {
-          continue;
-        }
-        $entry_path = $path . '/' . $entry;
-        $this->fileUnmanagedDeleteRecursive($entry_path, $callback);
-      }
-      $dir->close();
-
-      return rmdir($path);
-    }
-    return unlink($path);
-  }
-
-}
