diff --git a/core/includes/mail.inc b/core/includes/mail.inc
index c4ba3566b463d74bea3b64be81dd49e4c4e29bcb..c9ff601952fa481ff58b5b735850fa1a673e8b84 100644
--- a/core/includes/mail.inc
+++ b/core/includes/mail.inc
@@ -8,7 +8,7 @@
 /**
  * Auto-detect appropriate line endings for e-mails.
  *
- * $conf['mail_line_endings'] will override this setting.
+ * $settings['mail_line_endings'] will override this setting.
  */
 define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER_SOFTWARE'], 'Win32') !== FALSE ? "\r\n" : "\n");
 
@@ -216,14 +216,14 @@ function drupal_mail($module, $key, $to, $langcode, $params = array(), $from = N
  *   By default, all messages are sent via PHP's mail() function by the
  *   Drupal\Core\Mail\PhpMail implementation.
  *
- * The selection of a particular implementation is controlled via the variable
- * 'mail_system', which is a keyed array.  The default implementation
- * is the class whose name is the value of 'default-system' key. A more specific
- * match first to key and then to module will be used in preference to the
- * default. To specify a different class for all mail sent by one module, set
- * the class name as the value for the key corresponding to the module name. To
- * specify a class for a particular message sent by one module, set the class
- * name as the value for the array key that is the message id, which is
+ * The selection of a particular implementation is controlled via the config
+ * 'system.mail.interface', which is a keyed array.  The default implementation
+ * is the class whose name is the value of 'default' key. A more specific match
+ * first to key and then to module will be used in preference to the default. To
+ * specify a different class for all mail sent by one module, set the class
+ * name as the value for the key corresponding to the module name. To specify
+ * a class for a particular message sent by one module, set the class name as
+ * the value for the array key that is the message id, which is
  * "${module}_${key}".
  *
  * For example to debug all mail sent by the user module by logging it to a
@@ -231,7 +231,7 @@ function drupal_mail($module, $key, $to, $langcode, $params = array(), $from = N
  *
  * @code
  * array(
- *   'default-system' => 'Drupal\Core\Mail\PhpMail',
+ *   'default' => 'Drupal\Core\Mail\PhpMail',
  *   'user' => 'DevelMailLog',
  * );
  * @endcode
@@ -241,7 +241,7 @@ function drupal_mail($module, $key, $to, $langcode, $params = array(), $from = N
  *
  * @code
  * array(
- *   'default-system' => 'Drupal\Core\Mail\PhpMail',
+ *   'default' => 'Drupal\Core\Mail\PhpMail',
  *   'user' => 'DevelMailLog',
  *   'contact_page_autoreply' => 'DrupalDevNullMailSend',
  * );
@@ -260,13 +260,15 @@ function drupal_mail($module, $key, $to, $langcode, $params = array(), $from = N
  *   alter hook in drupal_mail() would have been {$module}_{$key}.
  *
  * @return Drupal\Core\Mail\MailInterface
+ *
+ * @throws \Exception
  */
 function drupal_mail_system($module, $key) {
   $instances = &drupal_static(__FUNCTION__, array());
 
   $id = $module . '_' . $key;
 
-  $configuration = variable_get('mail_system', array('default-system' => 'Drupal\Core\Mail\PhpMail'));
+  $configuration = config('system.mail')->get('interface');
 
   // Look for overrides for the default class, starting from the most specific
   // id, and falling back to the module name.
@@ -277,7 +279,7 @@ function drupal_mail_system($module, $key) {
     $class = $configuration[$module];
   }
   else {
-    $class = $configuration['default-system'];
+    $class = $configuration['default'];
   }
 
   if (empty($instances[$class])) {
@@ -506,8 +508,9 @@ function drupal_html_to_text($string, $allowed_tags = NULL) {
       if (isset($casing)) {
         $chunk = $casing($chunk);
       }
+      $line_endings = settings()->get('mail_line_endings', MAIL_LINE_ENDINGS);
       // Format it and apply the current indentation.
-      $output .= drupal_wrap_mail($chunk, implode('', $indent)) . MAIL_LINE_ENDINGS;
+      $output .= drupal_wrap_mail($chunk, implode('', $indent)) . $line_endings;
       // Remove non-quotation markers from indentation.
       $indent = array_map('_drupal_html_to_text_clean', $indent);
     }
diff --git a/core/lib/Drupal/Core/Mail/PhpMail.php b/core/lib/Drupal/Core/Mail/PhpMail.php
index f5ec995609e368f0079f07d10d030dd21f26f571..580a82415a31e3ed98a8c8919cef139c354d3762 100644
--- a/core/lib/Drupal/Core/Mail/PhpMail.php
+++ b/core/lib/Drupal/Core/Mail/PhpMail.php
@@ -59,13 +59,13 @@ public function mail(array $message) {
     foreach ($message['headers'] as $name => $value) {
       $mimeheaders[] = $name . ': ' . mime_header_encode($value);
     }
-    $line_endings = variable_get('mail_line_endings', MAIL_LINE_ENDINGS);
+    $line_endings = settings()->get('mail_line_endings', MAIL_LINE_ENDINGS);
     // Prepare mail commands.
     $mail_subject = mime_header_encode($message['subject']);
     // Note: e-mail uses CRLF for line-endings. PHP's API requires LF
     // on Unix and CRLF on Windows. Drupal automatically guesses the
     // line-ending format appropriate for your system. If you need to
-    // override this, adjust $conf['mail_line_endings'] in settings.php.
+    // override this, adjust $settings['mail_line_endings'] in settings.php.
     $mail_body = preg_replace('@\r?\n@', $line_endings, $message['body']);
     // For headers, PHP's API suggests that we use CRLF normally,
     // but some MTAs incorrectly replace LF with CRLF. See #234403.
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 7626952f80c397117b320fb0c99225a1a5276b57..44e29c2a7a0e179d9ad9f7a1dd2d28a5c63bee82 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -854,7 +854,7 @@ protected function setUp() {
     variable_set('file_public_path', $this->public_files_directory);
 
     // Use the test mail class instead of the default mail handler class.
-    variable_set('mail_system', array('default-system' => 'Drupal\Core\Mail\VariableLog'));
+    config('system.mail')->set('interface.default', 'Drupal\Core\Mail\VariableLog')->save();
 
     drupal_set_time_limit($this->timeLimit);
     // Temporary fix so that when running from run-tests.sh we don't get an
diff --git a/core/modules/system/config/system.mail.yml b/core/modules/system/config/system.mail.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e3eb7685187a5da626b48739485d144e84e45c8c
--- /dev/null
+++ b/core/modules/system/config/system.mail.yml
@@ -0,0 +1,2 @@
+interface:
+ default: 'Drupal\Core\Mail\PhpMail'
diff --git a/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php b/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php
index 5302c8dbd4f26fb00832c2ec2fea3db03ccfa6e9..f93b40fef5b9bbbed08211e84557d891e7b5702e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Mail/HtmlToTextTest.php
@@ -348,11 +348,8 @@ public function testDrupalHtmlToTextParagraphs() {
   public function testVeryLongLineWrap() {
     $input = 'Drupal<br /><p>' . str_repeat('x', 2100) . '</><br />Drupal';
     $output = drupal_html_to_text($input);
-    // This awkward construct comes from includes/mail.inc lines 8-13.
-    $eol = variable_get('mail_line_endings', MAIL_LINE_ENDINGS);
-    // We must use strlen() rather than drupal_strlen() in order to count
-    // octets rather than characters.
-    $line_length_limit = 1000 - drupal_strlen($eol);
+    $eol = settings()->get('mail_line_endings', MAIL_LINE_ENDINGS);
+
     $maximum_line_length = 0;
     foreach (explode($eol, $output) as $line) {
       // We must use strlen() rather than drupal_strlen() in order to count
diff --git a/core/modules/system/lib/Drupal/system/Tests/Mail/MailTest.php b/core/modules/system/lib/Drupal/system/Tests/Mail/MailTest.php
index bb72d293075ea1634b3d9850612482d2f3fa9033..155fdb859accc01999a070175bde4c2ecb520b1f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Mail/MailTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Mail/MailTest.php
@@ -42,7 +42,7 @@ function setUp() {
     parent::setUp();
 
     // Set MailTestCase (i.e. this class) as the SMTP library
-    variable_set('mail_system', array('default-system' => 'Drupal\system\Tests\Mail\MailTest'));
+    config('system.mail')->set('interface.default', 'Drupal\system\Tests\Mail\MailTest')->save();
   }
 
   /**
@@ -119,4 +119,3 @@ public function mail(array $message) {
     self::$sent_message = $message;
   }
 }
-
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php
index f6784b93a7e9436c8eab978f8a4daa5edc75aaa5..9c51bee19953a000bab4ef3d7e819d7a38ff068d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/BareMinimalUpgradePathTest.php
@@ -90,6 +90,9 @@ public function testBasicMinimalUpgrade() {
 
     // Verify that image.module was correctly installed.
     $this->assertEqual('thumbnail', config('image.style.thumbnail')->get('name'));
+
+    // Make sure that the default mail configuration has been converted.
+    $this->assertEqual(array('default' => 'Drupal\Core\Mail\PhpMail'), config('system.mail')->get('interface'), 'Default mail configuration set.');
   }
 
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/MailUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/MailUpgradePathTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a77c6b7141b0b7654dac50dfa0abe525016d3dbd
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/MailUpgradePathTest.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Upgrade\MailUpgradePathTest.
+ */
+
+namespace Drupal\system\Tests\Upgrade;
+
+/**
+ * Tests upgrade of mail backend system variables.
+ */
+class MailUpgradePathTest extends UpgradePathTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Mail upgrade test',
+      'description' => 'Tests upgrade of Mail backend configuration.',
+      'group' => 'Upgrade path',
+    );
+  }
+
+  public function setUp() {
+    $this->databaseDumpFiles = array(
+      drupal_get_path('module', 'system') . '/tests/upgrade/drupal-7.bare.standard_all.database.php.gz',
+      drupal_get_path('module', 'system') . '/tests/upgrade/drupal-7.system.database.php',
+    );
+    parent::setUp();
+  }
+
+  /**
+   * Tests that mail backends are upgraded to their Drupal 8 equivalents.
+   */
+  public function testMailSystemUpgrade() {
+    $this->performUpgrade(TRUE);
+
+    // Get the new mailer definitions.
+    $mail_system = config('system.mail')->get('interface');
+
+    // Check that the default mailer has been changed to a PSR-0 definition.
+    $this->assertEqual($mail_system['default'], 'Drupal\Core\Mail\PhpMail', 'Default mailer upgraded to Drupal 8 syntax.');
+
+    // Check that a custom mailer has been preserved through the upgrade.
+    $this->assertEqual($mail_system['maillog'], 'MaillogMailSystem', 'Custom mail backend preserved during upgrade.');
+  }
+
+  /**
+   * Overrides UpgradePathTestBase::checkCompletionPage().
+   */
+  protected function checkCompletionPage() {
+    // Ensure the user is informed about mail backends that need updating.
+    $this->assertText('The following mail backends need to be re-configured: MaillogMailSystem.', 'User notified about outdated mail backends.');
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
index 3ee5a9302fce59d44749bc387d8df863f354bf27..74c9923000774e9fad7c727b8f751a5d7cb1f961 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
@@ -264,6 +264,9 @@ protected function performUpgrade($register_errors = TRUE) {
       throw new Exception('Errors during update process.');
     }
 
+    // Allow tests to check the completion page.
+    $this->checkCompletionPage();
+
     // Check if there still are pending updates.
     $this->getUpdatePhp();
     $this->drupalPost(NULL, array(), t('Continue'));
@@ -316,4 +319,11 @@ protected function getUpdatePhp() {
     return $out;
   }
 
+  /**
+   * Checks the update.php completion page.
+   *
+   * Invoked by UpgradePathTestBase::performUpgrade() to allow upgrade tests to
+   * check messages and other output on the final confirmation page.
+   */
+  protected function checkCompletionPage() { }
 }
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index c21d62a5019ee58b02562b9a89748a58a8cabe47..9d986789c14e60f6e9342ec39b511c70948e6812 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -2140,6 +2140,45 @@ function system_update_8049() {
   db_create_table('config_snapshot', $table);
 }
 
+/**
+ * Converts mail settings to config.
+ *
+ * @ingroup config_upgrade
+ */
+function system_update_8050() {
+  // Update any mail interfaces to their Drupal 8 equivalents.
+  $old_mail_system_settings = update_variable_get('mail_system');
+
+  if ($old_mail_system_settings) {
+    $new_mail_system_settings = array();
+
+    foreach ($old_mail_system_settings as $key => $mailer_class) {
+      // Update old class name to PSR-0.
+      if ($mailer_class == 'DefaultMailSystem') {
+        $mailer_class = 'Drupal\Core\Mail\PhpMail';
+      }
+      // New default key.
+      if ($key == 'default-system') {
+        $new_mail_system_settings['default'] = $mailer_class;
+        unset($old_mail_system_settings[$key]);
+      }
+    }
+    if (count($old_mail_system_settings)) {
+      // Warn about non-core classes which may need to be updated.
+      drupal_set_message(format_string('The following mail backends need to be re-configured: @list.', array('@list' => implode(', ', $old_mail_system_settings))), 'warning');
+    }
+
+    $new_mail_system_settings += $old_mail_system_settings;
+    // Save the updated variable, and let update_variables_to_config convert it.
+    if ($new_mail_system_settings) {
+      update_variable_set('mail_system', $new_mail_system_settings);
+    }
+  }
+  update_variables_to_config('system.mail', array(
+    'mail_system' => 'interface',
+  ));
+}
+
 /**
  * @} End of "defgroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
diff --git a/core/modules/system/tests/upgrade/drupal-7.system.database.php b/core/modules/system/tests/upgrade/drupal-7.system.database.php
index be9f54cd9e2e1f289e1163bac6dd004fbc57b891..5bf6f296d0c524236769291bf43c15689c440a9f 100644
--- a/core/modules/system/tests/upgrade/drupal-7.system.database.php
+++ b/core/modules/system/tests/upgrade/drupal-7.system.database.php
@@ -12,123 +12,127 @@
 
 // Add non-default system settings.
 db_insert('variable')->fields(array(
-  'name',
-  'value',
-))
-->values(array(
-  'name' => 'cache',
-  'value'=> 'i:1;',
-))
-->values(array(
+    'name',
+    'value',
+  ))
+  ->values(array(
+    'name' => 'cache',
+    'value'=> 'i:1;',
+  ))
+  ->values(array(
     'name' => 'cache_lifetime',
     'value' => 's:5:"10800";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'page_cache_maximum_age',
     'value' => 's:4:"1800";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'page_compression',
     'value' => 'i:1;',
   ))
-->values(array(
+  ->values(array(
     'name' => 'preprocess_css',
     'value' => 'i:1;',
   ))
-->values(array(
+  ->values(array(
     'name' => 'preprocess_js',
     'value' => 'i:1;',
   ))
-->values(array(
+  ->values(array(
     'name' => 'cron_safe_threshold',
     'value' => 's:5:"86400";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'cron_threshold_warning',
     'value' => 's:5:"86400";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'cron_threshold_error',
     'value' => 's:6:"172800";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'error_level',
     'value' => 's:1:"1";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'maintenance_mode',
     'value' => 'i:1;',
   ))
-->values(array(
+  ->values(array(
     'name' => 'maintenance_mode_message',
     'value' => 's:22:"Testing config upgrade"',
   ))
-->values(array(
+  ->values(array(
     'name' => 'feed_default_items',
     'value' => 's:2:"20";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'feed_description',
     'value' => 's:22:"Testing config upgrade";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'feed_item_length',
     'value' => 's:6:"teaser";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'site_403',
     'value' => 's:3:"403";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'site_404',
     'value' => 's:3:"404";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'site_frontpage',
     'value' => 's:4:"node";',
   ))
-->values(array(
+  ->values(array(
     'name' => 'site_slogan',
     'value' => 's:31:"CMI makes Drupal 8 drush cex -y";',
   ))
-->values(array(
-  'name' => 'user_cancel_method',
-  'value' => 's:20:"user_cancel_reassign"',
+  ->values(array(
+    'name' => 'user_cancel_method',
+    'value' => 's:20:"user_cancel_reassign"',
   ))
-->values(array(
+  ->values(array(
     'name' => 'taxonomy_override_selector',
     'value' => 'i:1;',
   ))
-->values(array(
+  ->values(array(
     'name' => 'taxonomy_terms_per_page_admin',
     'value' => 'i:32;',
   ))
-->values(array(
+  ->values(array(
     'name' => 'taxonomy_maintain_index_table',
     'value' => 'i:0;',
- ))
-->values(array(
-  'name' => 'filter_allowed_protocols',
-  'value' => 'a:4:{i:0;s:4:"http";i:1;s:5:"https";i:2;s:3:"ftp";i:3;s:6:"mailto";}',
   ))
-->values(array(
+  ->values(array(
+    'name' => 'filter_allowed_protocols',
+    'value' => 'a:4:{i:0;s:4:"http";i:1;s:5:"https";i:2;s:3:"ftp";i:3;s:6:"mailto";}',
+  ))
+  ->values(array(
     'name' => 'password_count_log2',
     'value' => 'i:42;',
-))
-->values(array(
+  ))
+  ->values(array(
     'name' => 'actions_max_stack',
     'value' => 'i:42;',
   ))
-->execute();
+  ->values(array(
+    'name' => 'mail_system',
+    'value' => 'a:2:{s:14:"default-system";s:17:"DefaultMailSystem";s:7:"maillog";s:17:"MaillogMailSystem";}',
+  ))
+  ->execute();
 
 db_update('variable')
   ->fields(array('value' => 's:18:"config@example.com";'))
   ->condition('name', 'site_mail')
   ->execute();
 db_update('variable')
-    ->fields(array('value' => 's:22:"Testing config upgrade";'))
-    ->condition('name', 'site_name')
-    ->execute();
+  ->fields(array('value' => 's:22:"Testing config upgrade";'))
+  ->condition('name', 'site_name')
+  ->execute();
 db_update('variable')
   ->fields(array('value' => 's:10:"plain_text";'))
   ->condition('name', 'filter_fallback_format')
@@ -152,50 +156,50 @@
 );
 
 db_insert('variable')->fields(array(
-  'name',
-  'value',
-))
+    'name',
+    'value',
+  ))
   ->values(array(
-  'name' => 'color_bartik_files',
-  'value' => serialize(array('public://color/bartik-09696463/logo.png', 'public://color/bartik-09696463/colors.css')),
-))
+    'name' => 'color_bartik_files',
+    'value' => serialize(array('public://color/bartik-09696463/logo.png', 'public://color/bartik-09696463/colors.css')),
+  ))
   ->values(array(
-  'name' => 'color_bartik_logo',
-  'value' => serialize('public://color/bartik-09696463/logo.png'),
-))
+    'name' => 'color_bartik_logo',
+    'value' => serialize('public://color/bartik-09696463/logo.png'),
+  ))
   ->values(array(
-  'name' => 'color_bartik_palette',
-  'value' => serialize($palette),
-))
+    'name' => 'color_bartik_palette',
+    'value' => serialize($palette),
+  ))
   ->values(array(
-  'name' => 'color_bartik_stylesheets',
-  'value' => serialize('public://color/bartik-09696463/colors.css'),
-))
+    'name' => 'color_bartik_stylesheets',
+    'value' => serialize('public://color/bartik-09696463/colors.css'),
+  ))
   ->execute();
 
 // color module with faked seven upgrade path to test screenshot option
 db_insert('variable')->fields(array(
-  'name',
-  'value',
-))
+    'name',
+    'value',
+  ))
   ->values(array(
-  'name' => 'color_seven_files',
-  'value' => serialize(array('public://color/seven-09696463/logo.png', 'public://color/seven-09696463/colors.css')),
-))
+    'name' => 'color_seven_files',
+    'value' => serialize(array('public://color/seven-09696463/logo.png', 'public://color/seven-09696463/colors.css')),
+  ))
   ->values(array(
-  'name' => 'color_seven_logo',
-  'value' => serialize('public://color/seven-09696463/logo.png'),
-))
+    'name' => 'color_seven_logo',
+    'value' => serialize('public://color/seven-09696463/logo.png'),
+  ))
   ->values(array(
-  'name' => 'color_seven_palette',
-  'value' => serialize($palette),
-))
+    'name' => 'color_seven_palette',
+    'value' => serialize($palette),
+  ))
   ->values(array(
-  'name' => 'color_seven_stylesheets',
-  'value' => serialize('public://color/seven-09696463/colors.css'),
-))
+    'name' => 'color_seven_stylesheets',
+    'value' => serialize('public://color/seven-09696463/colors.css'),
+  ))
   ->values(array(
-  'name' => 'color_seven_screenshot',
-  'value' => serialize('public://color/seven-09696463/dummy-screenshot.png'),
-))
+    'name' => 'color_seven_screenshot',
+    'value' => serialize('public://color/seven-09696463/dummy-screenshot.png'),
+  ))
   ->execute();