diff --git a/automatic_updates.module b/automatic_updates.module
index 48b88b1898383f4e46d7d34199827d1d91c9adb4..f5dbe493088b4e350a7e2ef2e0e30f46c31626e7 100644
--- a/automatic_updates.module
+++ b/automatic_updates.module
@@ -6,9 +6,12 @@
  */
 
 use Drupal\automatic_updates\CronUpdater;
+use Drupal\automatic_updates\UpdateRecommender;
 use Drupal\automatic_updates\Validation\AdminReadinessMessages;
+use Drupal\Core\Extension\ExtensionVersion;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
+use Drupal\update\ProjectSecurityData;
 
 /**
  * Implements hook_page_top().
@@ -85,6 +88,51 @@ function automatic_updates_form_update_manager_update_form_alter(&$form, FormSta
   }
 }
 
+/**
+ * Implements hook_form_FORM_ID_alter() for 'update_settings' form.
+ */
+function automatic_updates_form_update_settings_alter(array &$form, FormStateInterface $form_state, string $form_id) {
+  $recommender = new UpdateRecommender();
+  $drupal_project = $recommender->getProjectInfo();
+  $version = ExtensionVersion::createFromVersionString($drupal_project['existing_version']);
+  $current_minor = $version->getMajorVersion() . '.' . $version->getMinorVersion();
+  $supported_until_version = $version->getMajorVersion() . '.'
+      . ((int) $version->getMinorVersion() + ProjectSecurityData::CORE_MINORS_WITH_SECURITY_COVERAGE)
+      . '.0';
+
+  $form['automatic_updates_cron'] = [
+    '#type' => 'radios',
+    '#title' => t('Automatically update Drupal core'),
+    '#options' => [
+      CronUpdater::DISABLED => t('Disabled'),
+      CronUpdater::ALL => t('All supported updates'),
+      CronUpdater::SECURITY => t('Security updates only'),
+    ],
+    '#default_value' => \Drupal::config('automatic_updates.settings')->get('cron'),
+    '#description' => t(
+      'If enabled, Drupal core will be automatically updated when an update is available. Automatic updates are only supported for @current_minor.x versions of Drupal core. Drupal @current_minor will receive security updates until @supported_until_version is released.',
+      [
+        '@current_minor' => $current_minor,
+        '@supported_until_version' => $supported_until_version,
+      ]
+    ),
+  ];
+  $form += [
+    '#submit' => ['::submitForm'],
+  ];
+  $form['#submit'][] = '_automatic_updates_update_settings_form_submit';
+}
+
+/**
+ * Submit function for the 'update_settings' form.
+ */
+function _automatic_updates_update_settings_form_submit(array &$form, FormStateInterface $form_state) {
+  \Drupal::configFactory()
+    ->getEditable('automatic_updates.settings')
+    ->set('cron', $form_state->getValue('automatic_updates_cron'))
+    ->save();
+}
+
 /**
  * Implements hook_local_tasks_alter().
  */
diff --git a/config/install/automatic_updates.settings.yml b/config/install/automatic_updates.settings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..207c746331023223ff9ccc8c31f663e512e3a2d0
--- /dev/null
+++ b/config/install/automatic_updates.settings.yml
@@ -0,0 +1 @@
+cron: security
diff --git a/config/schema/automatic_updates.schema.yml b/config/schema/automatic_updates.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b3047d9987415b075f83988bc03a5952f3a92bf8
--- /dev/null
+++ b/config/schema/automatic_updates.schema.yml
@@ -0,0 +1,7 @@
+automatic_updates.settings:
+  type: config_object
+  label: 'Automatic Updates settings'
+  mapping:
+    cron:
+      type: string
+      label: 'Enable automatic updates during cron'
diff --git a/src/CronUpdater.php b/src/CronUpdater.php
index 19b0d26105a84fcec1becb5923e59874ac5946d8..c9392e5af2b9df90435cba7effd3e120fe2ed271 100644
--- a/src/CronUpdater.php
+++ b/src/CronUpdater.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\automatic_updates;
 
+use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -15,6 +16,27 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  */
 class CronUpdater implements ContainerInjectionInterface {
 
+  /**
+   * All automatic updates are disabled.
+   *
+   * @var string
+   */
+  public const DISABLED = 'disable';
+
+  /**
+   * Only perform automatic security updates.
+   *
+   * @var string
+   */
+  public const SECURITY = 'security';
+
+  /**
+   * All automatic updates are enabled.
+   *
+   * @var string
+   */
+  public const ALL = 'patch';
+
   /**
    * The updater service.
    *
@@ -22,6 +44,13 @@ class CronUpdater implements ContainerInjectionInterface {
    */
   protected $updater;
 
+  /**
+   * The config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
   /**
    * The logger.
    *
@@ -34,11 +63,14 @@ class CronUpdater implements ContainerInjectionInterface {
    *
    * @param \Drupal\automatic_updates\Updater $updater
    *   The updater service.
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The config factory service.
    * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
    *   The logger channel factory.
    */
-  public function __construct(Updater $updater, LoggerChannelFactoryInterface $logger_factory) {
+  public function __construct(Updater $updater, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory) {
     $this->updater = $updater;
+    $this->configFactory = $config_factory;
     $this->logger = $logger_factory->get('automatic_updates');
   }
 
@@ -48,6 +80,7 @@ class CronUpdater implements ContainerInjectionInterface {
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('automatic_updates.updater'),
+      $container->get('config.factory'),
       $container->get('logger.factory')
     );
   }
@@ -56,6 +89,14 @@ class CronUpdater implements ContainerInjectionInterface {
    * Handles updates during cron.
    */
   public function handleCron(): void {
+    $level = $this->configFactory->get('automatic_updates.settings')
+      ->get('cron');
+
+    // If automatic updates are disabled, bail out.
+    if ($level === static::DISABLED) {
+      return;
+    }
+
     $recommender = new UpdateRecommender();
     try {
       $recommended_release = $recommender->getRecommendedRelease(TRUE);
@@ -76,6 +117,12 @@ class CronUpdater implements ContainerInjectionInterface {
       return;
     }
 
+    // If automatic updates are only enabled for security releases, bail out if
+    // the recommended release is not a security release.
+    if ($level === static::SECURITY && !$recommended_release->isSecurityRelease()) {
+      return;
+    }
+
     // @todo Use the queue to add update jobs allowing jobs to span multiple
     //   cron runs.
     $recommended_version = $recommended_release->getVersion();
diff --git a/tests/src/Kernel/CronUpdaterTest.php b/tests/src/Kernel/CronUpdaterTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6de07f4bbed81ce28a48c2387ed3d8141ddd02d3
--- /dev/null
+++ b/tests/src/Kernel/CronUpdaterTest.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace Drupal\Tests\automatic_updates\Kernel;
+
+use Drupal\automatic_updates\CronUpdater;
+use Drupal\Core\Form\FormState;
+use Drupal\update\UpdateSettingsForm;
+
+/**
+ * @covers \Drupal\automatic_updates\CronUpdater
+ * @covers \automatic_updates_form_update_settings_alter
+ *
+ * @group automatic_updates
+ */
+class CronUpdaterTest extends AutomaticUpdatesKernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'automatic_updates',
+    'package_manager',
+  ];
+
+  /**
+   * Data provider for ::testUpdaterCalled().
+   *
+   * @return array[]
+   *   Sets of arguments to pass to the test method.
+   */
+  public function providerUpdaterCalled(): array {
+    $fixture_dir = __DIR__ . '/../../fixtures/release-history';
+
+    return [
+      'disabled, normal release' => [
+        CronUpdater::DISABLED,
+        "$fixture_dir/drupal.9.8.1.xml",
+        FALSE,
+      ],
+      'disabled, security release' => [
+        CronUpdater::DISABLED,
+        "$fixture_dir/drupal.9.8.1-security.xml",
+        FALSE,
+      ],
+      'security only, security release' => [
+        CronUpdater::SECURITY,
+        "$fixture_dir/drupal.9.8.1-security.xml",
+        TRUE,
+      ],
+      'security only, normal release' => [
+        CronUpdater::SECURITY,
+        "$fixture_dir/drupal.9.8.1.xml",
+        FALSE,
+      ],
+      'enabled, normal release' => [
+        CronUpdater::ALL,
+        "$fixture_dir/drupal.9.8.1.xml",
+        TRUE,
+      ],
+      'enabled, security release' => [
+        CronUpdater::ALL,
+        "$fixture_dir/drupal.9.8.1-security.xml",
+        TRUE,
+      ],
+    ];
+  }
+
+  /**
+   * Tests that the cron handler calls the updater as expected.
+   *
+   * @param string $setting
+   *   Whether automatic updates should be enabled during cron. Possible values
+   *   are 'disable', 'security', and 'patch'.
+   * @param string $release_data
+   *   If automatic updates are enabled, the path of the fake release metadata
+   *   that should be served when fetching information on available updates.
+   * @param bool $will_update
+   *   Whether an update should be performed, given the previous two arguments.
+   *
+   * @dataProvider providerUpdaterCalled
+   */
+  public function testUpdaterCalled(string $setting, string $release_data, bool $will_update): void {
+    // Our form alter does not refresh information on available updates, so
+    // ensure that the appropriate update data is loaded beforehand.
+    $this->setReleaseMetadata($release_data);
+    update_get_available(TRUE);
+
+    // Submit the configuration form programmatically, to prove our alterations
+    // work as expected.
+    $form_builder = $this->container->get('form_builder');
+    $form_state = new FormState();
+    $form = $form_builder->buildForm(UpdateSettingsForm::class, $form_state);
+    // Ensure that the version ranges in the setting's description, which are
+    // computed dynamically, look correct.
+    $this->assertStringContainsString('Automatic updates are only supported for 9.8.x versions of Drupal core. Drupal 9.8 will receive security updates until 9.10.0 is released.', $form['automatic_updates_cron']['#description']);
+    $form_state->setValue('automatic_updates_cron', $setting);
+    $form_builder->submitForm(UpdateSettingsForm::class, $form_state);
+
+    // Mock the updater so we can assert that its methods are called or bypassed
+    // depending on configuration.
+    $will_update = (int) $will_update;
+    $updater = $this->prophesize('\Drupal\automatic_updates\Updater');
+    $updater->begin(['drupal' => '9.8.1'])->shouldBeCalledTimes($will_update);
+    $updater->stage()->shouldBeCalledTimes($will_update);
+    $updater->commit()->shouldBeCalledTimes($will_update);
+    $updater->clean()->shouldBeCalledTimes($will_update);
+    $this->container->set('automatic_updates.updater', $updater->reveal());
+
+    $this->container->get('cron')->run();
+  }
+
+}