diff --git a/core/modules/book/book.install b/core/modules/book/book.install
index 2293c46..c15377f 100644
--- a/core/modules/book/book.install
+++ b/core/modules/book/book.install
@@ -43,7 +43,7 @@ function _book_install_type_create() {
   node_type_save($book_node_type);
   node_add_body_field($book_node_type);
   // Default to not promoted.
-  variable_set('node_options_book', array('status'));
+  config('node.settings.book')->set('options', array('status'));
 }
 
 /**
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index b73857c..e329c89 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -113,8 +113,8 @@ function comment_entity_info(&$info) {
         // of local tasks. Note that the paths use a different placeholder name
         // and thus a different menu loader callback, so that Field UI page
         // callbacks get a comment bundle name from the node type in the URL.
-        // See comment_node_type_load() and comment_menu_alter().
-        'path' => 'admin/structure/types/manage/%comment_node_type/comment',
+        // See comment_menu_node_type_load() and comment_menu_alter().
+        'path' => 'admin/structure/types/manage/%comment_menu_node_type/comment',
         'bundle argument' => 4,
         'real path' => 'admin/structure/types/manage/' . $type . '/comment',
         'access arguments' => array('administer content types'),
@@ -136,7 +136,7 @@ function comment_entity_info(&$info) {
  *
  * @see comment_menu_alter()
  */
-function comment_node_type_load($name) {
+function comment_menu_node_type_load($name) {
   if ($type = node_type_load($name)) {
     return 'comment_node_' . $type->type;
   }
@@ -290,10 +290,10 @@ function comment_menu_alter(&$items) {
 
   // Adjust the Field UI tabs on admin/structure/types/manage/[node-type].
   // See comment_entity_info().
-  $items['admin/structure/types/manage/%comment_node_type/comment/fields']['title'] = 'Comment fields';
-  $items['admin/structure/types/manage/%comment_node_type/comment/fields']['weight'] = 3;
-  $items['admin/structure/types/manage/%comment_node_type/comment/display']['title'] = 'Comment display';
-  $items['admin/structure/types/manage/%comment_node_type/comment/display']['weight'] = 4;
+  $items['admin/structure/types/manage/%comment_menu_node_type/comment/fields']['title'] = 'Comment fields';
+  $items['admin/structure/types/manage/%comment_menu_node_type/comment/fields']['weight'] = 3;
+  $items['admin/structure/types/manage/%comment_menu_node_type/comment/display']['title'] = 'Comment display';
+  $items['admin/structure/types/manage/%comment_menu_node_type/comment/display']['weight'] = 4;
 }
 
 /**
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
index ab03d41..caa3661 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php
@@ -107,6 +107,7 @@ function testCommentEnable() {
    * Tests that comment module works correctly with plain text format.
    */
   function testCommentFormat() {
+    menu_router_rebuild();
     // Disable text processing for comments.
     $this->drupalLogin($this->admin_user);
     $edit = array('instance[settings][text_processing]' => 0);
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index a04c0dd..eaff05b 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -76,7 +76,7 @@ function field_ui_menu() {
           // Extract path information from the bundle.
           $path = $bundle_info['admin']['path'];
           // Different bundles can appear on the same path (e.g. %node_type and
-          // %comment_node_type). To allow field_ui_menu_load() to extract the
+          // %comment_menu_node_type). To allow field_ui_menu_load() to extract the
           // actual bundle object from the translated menu router path
           // arguments, we need to identify the argument position of the bundle
           // name string ('bundle argument') and pass that position to the menu
@@ -216,7 +216,7 @@ function field_ui_menu_load($field_name, $entity_type, $bundle_name, $bundle_pos
   // The menu router path to manage fields of an entity can be shared among
   // multiple bundles. For example:
   // - admin/structure/types/manage/%node_type/fields/%field_ui_menu
-  // - admin/structure/types/manage/%comment_node_type/fields/%field_ui_menu
+  // - admin/structure/types/manage/%comment_menu_node_type/fields/%field_ui_menu
   // The menu system will automatically load the correct bundle depending on the
   // actual path arguments, but this menu loader function only receives the node
   // type string as $bundle_name, which is not the bundle name for comments.
diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install
index b1e63de..96cc914 100644
--- a/core/modules/forum/forum.install
+++ b/core/modules/forum/forum.install
@@ -15,7 +15,7 @@ function forum_install() {
   // options set (for example, they are not promoted to the front page).
   // @todo Convert to default module configuration, once Node module's content
   //   types are converted.
-  variable_set('node_options_forum', array('status'));
+  config('node.settings.forum')->set('options', array('status'));
 }
 
 /**
@@ -121,7 +121,7 @@ function forum_uninstall() {
   // Load the dependent Taxonomy module, in case it has been disabled.
   drupal_load('module', 'taxonomy');
 
-  variable_del('node_options_forum');
+  config('node.settings.forum')->delete();
 
   field_delete_field('taxonomy_forums');
   // Purge field data now to allow taxonomy module to be uninstalled
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
index 7b0bef7..b3d58bf 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumTest.php
@@ -195,7 +195,7 @@ function testForum() {
 
     // Test loading multiple forum nodes on the front page.
     $this->drupalLogin($this->drupalCreateUser(array('administer content types', 'create forum content', 'post comments')));
-    $this->drupalPost('admin/structure/types/manage/forum', array('node_options[promote]' => 'promote'), t('Save content type'));
+    $this->drupalPost('admin/structure/types/manage/forum', array('options[promote]' => 'promote'), t('Save content type'));
     $this->createForumTopic($this->forum, FALSE);
     $this->createForumTopic($this->forum, FALSE);
     $this->drupalGet('node');
diff --git a/core/modules/node/content_types.inc b/core/modules/node/content_types.inc
index 538b19f..fc3844e 100644
--- a/core/modules/node/content_types.inc
+++ b/core/modules/node/content_types.inc
@@ -113,6 +113,8 @@ function node_type_form($form, &$form_state, $type = NULL) {
     $type = node_type_set_defaults(array('custom' => 1, 'locked' => 0));
   }
 
+  $config = config('node.settings.' . $type->type);
+
   // Make the type object available to implementations of hook_form_alter.
   $form['#node_type'] = $type;
 
@@ -172,10 +174,10 @@ function node_type_form($form, &$form_state, $type = NULL) {
     $form['submission']['title_label']['#description'] = t('This content type does not have a title field.');
     $form['submission']['title_label']['#required'] = FALSE;
   }
-  $form['submission']['node_preview'] = array(
+  $form['submission']['preview'] = array(
     '#type' => 'radios',
     '#title' => t('Preview before submitting'),
-    '#default_value' => variable_get('node_preview_' . $type->type, DRUPAL_OPTIONAL),
+    '#default_value' => $config->get('preview') ?: DRUPAL_OPTIONAL,
     '#options' => array(
       DRUPAL_DISABLED => t('Disabled'),
       DRUPAL_OPTIONAL => t('Optional'),
@@ -195,9 +197,9 @@ function node_type_form($form, &$form_state, $type = NULL) {
     '#collapsed' => TRUE,
     '#group' => 'additional_settings',
   );
-  $form['workflow']['node_options'] = array('#type' => 'checkboxes',
+  $form['workflow']['options'] = array('#type' => 'checkboxes',
     '#title' => t('Default options'),
-    '#default_value' => variable_get('node_options_' . $type->type, array('status', 'promote')),
+    '#default_value' => $config->get('options') ?: array('status', 'promote'),
     '#options' => array(
       'status' => t('Published'),
       'promote' => t('Promoted to front page'),
@@ -234,10 +236,10 @@ function node_type_form($form, &$form_state, $type = NULL) {
     '#collapsed' => TRUE,
     '#group' => 'additional_settings',
   );
-  $form['display']['node_submitted'] = array(
+  $form['display']['submitted'] = array(
     '#type' => 'checkbox',
     '#title' => t('Display author and date information.'),
-    '#default_value' => variable_get('node_submitted_' . $type->type, TRUE),
+    '#default_value' => !is_null($config->get('submitted')) ? $config->get('submitted') : TRUE,
     '#description' => t('Author username and publish date will be displayed.'),
   );
   $form['old_type'] = array(
@@ -377,6 +379,7 @@ function node_type_form_submit($form, &$form_state) {
   unset($variables['form_token'], $variables['op'], $variables['submit'], $variables['delete'], $variables['reset'], $variables['form_id'], $variables['form_build_id']);
 
   // Save or reset persistent variable values.
+  $node_type_settings = config('node.settings.' . $type->type);
   foreach ($variables as $key => $value) {
     $variable_new = $key . '_' . $type->type;
     $variable_old = $key . '_' . $type->old_type;
@@ -384,12 +387,19 @@ function node_type_form_submit($form, &$form_state) {
     if (is_array($value)) {
       $value = array_keys(array_filter($value));
     }
+    $node_type_settings->set($key, $value);
+    // @todo remove when comment and menu variables are converted.
     variable_set($variable_new, $value);
-
     if ($variable_new != $variable_old) {
       variable_del($variable_old);
     }
   }
+  $node_type_settings->save();
+
+  // Remove old settings if needed.
+  if ($type->type != $type->old_type) {
+    config('node.settings.' . $type->old_type)->delete();
+  }
 
   // Saving the content type after saving the variables allows modules to act
   // on those variables via hook_node_type_insert().
diff --git a/core/modules/node/content_types.js b/core/modules/node/content_types.js
index 40d6422..75409a5 100644
--- a/core/modules/node/content_types.js
+++ b/core/modules/node/content_types.js
@@ -18,10 +18,10 @@
     });
     $context.find('#edit-workflow').drupalSetSummary(function(context) {
       var vals = [];
-      $(context).find("input[name^='node_options']:checked").parent().each(function() {
+      $(context).find("input[name^='options']:checked").parent().each(function() {
         vals.push(Drupal.checkPlain($(this).text()));
       });
-      if (!$(context).find('#edit-node-options-status').is(':checked')) {
+      if (!$(context).find('#edit-options-status').is(':checked')) {
         vals.unshift(Drupal.t('Not published'));
       }
       return vals.join(', ');
@@ -43,7 +43,7 @@
       $context.find('input:checked').next('label').each(function() {
         vals.push(Drupal.checkPlain($(this).text()));
       });
-      if (!$context.find('#edit-node-submitted').is(':checked')) {
+      if (!$context.find('#edit-submitted').is(':checked')) {
         vals.unshift(Drupal.t("Don't display post information"));
       }
       return vals.join(', ');
diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php
index 4f5315e..8eed7f0 100644
--- a/core/modules/node/lib/Drupal/node/NodeFormController.php
+++ b/core/modules/node/lib/Drupal/node/NodeFormController.php
@@ -26,7 +26,10 @@ class NodeFormController extends EntityFormController {
    */
   protected function prepareEntity(EntityInterface $node) {
     // Set up default values, if required.
-    $node_options = variable_get('node_options_' . $node->type, array('status', 'promote'));
+    $node_options = config('node.settings.' . $node->type)->get('options');
+    if (is_null($node_options)) {
+      $node_options = array('status', 'promote');
+    }
     // If this is a new node, fill in the default values.
     if (!isset($node->nid) || isset($node->is_new)) {
       foreach (array('status', 'promote', 'sticky') as $key) {
@@ -250,7 +253,7 @@ public function form(array $form, array &$form_state, EntityInterface $node) {
   protected function actions(array $form, array &$form_state) {
     $element = parent::actions($form, $form_state);
     $node = $this->getEntity($form_state);
-    $preview_mode = variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL);
+    $preview_mode = config('node.settings.' . $node->type)->get('preview') ?: DRUPAL_OPTIONAL;
 
     $element['preview'] = array(
       '#access' => $preview_mode != DRUPAL_DISABLED,
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/NodeType.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/NodeType.php
new file mode 100644
index 0000000..067e888
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/NodeType.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\node\Plugin\Core\Entity\NodeType.
+ */
+
+namespace Drupal\node\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the Node type configuration entity.
+ *
+ * @Plugin(
+ *   id = "node_type",
+ *   label = @Translation("Node type"),
+ *   module = "node",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "node.type",
+ *   entity_keys = {
+ *     "id" = "type",
+ *     "label" = "name",
+ *     "uuid" = "uuid"
+ *   }
+ * )
+ */
+class NodeType extends ConfigEntityBase {
+
+  /**
+   * The content type UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The content type name (machine name) as specified by theme or module.
+   *
+   * @var string
+   */
+  public $type;
+
+  /**
+   * The content type label.
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * The base string used to construct callbacks corresponding
+   * to this node type.
+   *
+   * @var string
+   */
+  public $base;
+
+  /**
+   * The module defining this node type.
+   *
+   * @var string
+   */
+  public $module;
+
+  /**
+   * A brief description of this type.
+   *
+   * @var string
+   */
+  public $description;
+
+  /**
+   * Help information shown to the user when creating a node of this type.
+   *
+   * @var string
+   */
+  public $help;
+
+  /**
+   * Boolean indicating whether this type uses the node.title field.
+   *
+   * @var string
+   */
+  public $has_title;
+
+  /**
+   * The label displayed for the title field on the edit form.
+   *
+   * @var string
+   */
+  public $title_label;
+
+  /**
+   * A boolean indicating whether this type is defined by a module
+   * (FALSE) or by a user via Add content type (TRUE).
+   *
+   * @var string
+   */
+  public $custom;
+
+  /**
+   * A boolean indicating whether this type has been modified by an
+   * administrator; currently not used in any way.
+   *
+   * @var string
+   */
+  public $modified;
+
+  /**
+   * A boolean indicating whether the administrator can change the
+   * machine name of this type.
+   *
+   * @var string
+   */
+  public $locked;
+
+  /**
+   * A boolean indicating whether the node type is disabled.
+   *
+   * @var string
+   */
+  public $disabled;
+
+  /**
+   * The original machine-readable name of this node type. This may be
+   * different from the current type name if the locked field is 0.
+   *
+   * @var string
+   */
+  public $orig_type;
+
+  /**
+   * Overrides Drupal\Core\Entity\Entity::id().
+   */
+  public function id() {
+    return $this->type;
+  }
+}
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php
index 36baad2..2499aef 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php
@@ -107,7 +107,7 @@ function testUnpublishedNodeCreation() {
     config('system.site')->set('page.front', 'test-page')->save();
 
     // Set "Basic page" content type to be unpublished by default.
-    variable_set('node_options_page', array());
+    config('node.settings.page')->set('options', array())->save();
 
     // Create a node.
     $edit = array();
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodePostSettingsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodePostSettingsTest.php
index ce48614..2a8821e 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodePostSettingsTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodePostSettingsTest.php
@@ -34,7 +34,7 @@ function testPagePostInfo() {
 
     // Set "Basic page" content type to display post information.
     $edit = array();
-    $edit['node_submitted'] = TRUE;
+    $edit['submitted'] = TRUE;
     $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
 
     // Create a node.
@@ -57,7 +57,7 @@ function testPageNotPostInfo() {
 
     // Set "Basic page" content type to display post information.
     $edit = array();
-    $edit['node_submitted'] = FALSE;
+    $edit['submitted'] = FALSE;
     $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
 
     // Create a node.
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php
index c51b5a0..7e7d2a5 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTypePersistenceTest.php
@@ -32,9 +32,9 @@ function testNodeTypeCustomizationPersistence() {
     // Enable poll and verify that the node type is in the DB and is not
     // disabled.
     $this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
-    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
-    $this->assertNotIdentical($disabled, FALSE, 'Poll node type found in the database');
-    $this->assertEqual($disabled, 0, 'Poll node type is not disabled');
+    $poll = entity_load('node_type', 'poll');
+    $this->assertNotIdentical($poll->disabled, FALSE, 'Poll node type found in the database');
+    $this->assertEqual($poll->disabled, 0, 'Poll node type is not disabled');
 
     // Check that poll node type (uncustomized) shows up.
     $this->drupalGet('node/add');
@@ -51,17 +51,17 @@ function testNodeTypeCustomizationPersistence() {
 
     // Disable poll and check that the node type gets disabled.
     $this->drupalPost('admin/modules', $poll_disable, t('Save configuration'));
-    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
-    $this->assertEqual($disabled, 1, 'Poll node type is disabled');
+    $poll = entity_load('node_type', 'poll');
+    $this->assertEqual($poll->disabled, 1, 'Poll node type is disabled');
     $this->drupalGet('node/add');
     $this->assertNoText('poll', 'poll type is not found on node/add');
 
     // Reenable poll and check that the customization survived the module
     // disable.
     $this->drupalPost('admin/modules', $poll_enable, t('Save configuration'));
-    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
-    $this->assertNotIdentical($disabled, FALSE, 'Poll node type found in the database');
-    $this->assertEqual($disabled, 0, 'Poll node type is not disabled');
+    $poll = entity_load('node_type', 'poll');
+    $this->assertNotIdentical($poll->disabled, FALSE, 'Poll node type found in the database');
+    $this->assertEqual($poll->disabled, 0, 'Poll node type is not disabled');
     $this->drupalGet('node/add');
     $this->assertText($description, 'Customized description found');
 
@@ -70,8 +70,8 @@ function testNodeTypeCustomizationPersistence() {
     $edit = array('uninstall[poll]' => 'poll');
     $this->drupalPost('admin/modules/uninstall', $edit, t('Uninstall'));
     $this->drupalPost(NULL, array(), t('Uninstall'));
-    $disabled = db_query('SELECT disabled FROM {node_type} WHERE type = :type', array(':type' => 'poll'))->fetchField();
-    $this->assertTrue($disabled, 'Poll node type is in the database and is disabled');
+    $poll = entity_load('node_type', 'poll');
+    $this->assertTrue($poll->disabled, 'Poll node type is in the database and is disabled');
     $this->drupalGet('node/add');
     $this->assertNoText('poll', 'poll type is no longer found on node/add');
 
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTypeTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTypeTest.php
index 944af15..7df2368 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTypeTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTypeTest.php
@@ -53,7 +53,7 @@ function testNodeTypeCreation() {
     // Create a content type programmaticaly.
     $type = $this->drupalCreateContentType();
 
-    $type_exists = db_query('SELECT 1 FROM {node_type} WHERE type = :type', array(':type' => $type->type))->fetchField();
+    $type_exists = (bool) entity_load('node_type', $type->type);
     $this->assertTrue($type_exists, 'The new content type has been created in the database.');
 
     // Login a test user.
@@ -72,7 +72,7 @@ function testNodeTypeCreation() {
       'type' => 'foo',
     );
     $this->drupalPost('admin/structure/types/add', $edit, t('Save content type'));
-    $type_exists = db_query('SELECT 1 FROM {node_type} WHERE type = :type', array(':type' => 'foo'))->fetchField();
+    $type_exists = (bool) entity_load('node_type', 'foo');
     $this->assertTrue($type_exists, 'The new content type has been created in the database.');
   }
 
diff --git a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php
index b429e07..6f0eafd 100644
--- a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php
@@ -58,7 +58,7 @@ function testPagePreviewWithRevisions() {
     $title_key = "title";
     $body_key = "body[$langcode][0][value]";
     // Force revision on "Basic page" content.
-    variable_set('node_options_page', array('status', 'revision'));
+    config('node.settings.page')->set('options', array('status', 'revision'));
 
     // Fill in node creation form and preview node.
     $edit = array();
diff --git a/core/modules/node/node.install b/core/modules/node/node.install
index 697d3a7..ba8dd62 100644
--- a/core/modules/node/node.install
+++ b/core/modules/node/node.install
@@ -34,7 +34,7 @@ function node_schema() {
         'default' => NULL,
       ),
       'type' => array(
-        'description' => 'The {node_type}.type of this node.',
+        'description' => 'The type of this node.',
         'type' => 'varchar',
         'length' => 32,
         'not null' => TRUE,
@@ -281,103 +281,6 @@ function node_schema() {
     ),
   );
 
-  $schema['node_type'] = array(
-    'description' => 'Stores information about all defined {node} types.',
-    'fields' => array(
-      'type' => array(
-        'description' => 'The machine-readable name of this type.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-      ),
-      'name' => array(
-        'description' => 'The human-readable name of this type.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-        'translatable' => TRUE,
-      ),
-      'base' => array(
-        'description' => 'The base string used to construct callbacks corresponding to this node type.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-      ),
-      'module' => array(
-        'description' => 'The module defining this node type.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-      ),
-      'description' => array(
-        'description' => 'A brief description of this type.',
-        'type' => 'text',
-        'not null' => TRUE,
-        'size' => 'medium',
-        'translatable' => TRUE,
-      ),
-      'help' => array(
-        'description' => 'Help information shown to the user when creating a {node} of this type.',
-        'type' => 'text',
-        'not null' => TRUE,
-        'size' => 'medium',
-        'translatable' => TRUE,
-      ),
-      'has_title' => array(
-        'description' => 'Boolean indicating whether this type uses the {node}.title field.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'size' => 'tiny',
-      ),
-      'title_label' => array(
-        'description' => 'The label displayed for the title field on the edit form.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-        'translatable' => TRUE,
-      ),
-      'custom' => array(
-        'description' => 'A boolean indicating whether this type is defined by a module (FALSE) or by a user via Add content type (TRUE).',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'tiny',
-      ),
-      'modified' => array(
-        'description' => 'A boolean indicating whether this type has been modified by an administrator; currently not used in any way.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'tiny',
-      ),
-      'locked' => array(
-        'description' => 'A boolean indicating whether the administrator can change the machine name of this type.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'tiny',
-      ),
-      'disabled' => array(
-        'description' => 'A boolean indicating whether the node type is disabled.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'tiny'
-      ),
-      'orig_type' => array(
-        'description' => 'The original machine-readable name of this node type. This may be different from the current type name if the locked field is 0.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'primary key' => array('type'),
-  );
-
   $schema['block_node_type'] = array(
     'description' => 'Sets up display criteria for blocks based on content types',
     'fields' => array(
@@ -397,7 +300,7 @@ function node_schema() {
         'type' => 'varchar',
         'length' => 32,
         'not null' => TRUE,
-        'description' => "The machine-readable name of this type from {node_type}.type.",
+        'description' => "The machine-readable name of this type.",
       ),
     ),
     'primary key' => array('module', 'delta', 'type'),
@@ -434,17 +337,10 @@ function node_install() {
  */
 function node_uninstall() {
   // Delete node type variables.
-  $types = db_query('SELECT type FROM {node_type}')->fetchCol();
-  foreach ($types as $type) {
-    db_delete('variable')
-      ->condition(db_or()
-        ->condition('name', 'node_preview_' . $type)
-        ->condition('name', 'node_options_' . $type)
-        ->condition('name', 'node_submitted_' . $type)
-        ->condition('name', 'node_permissions_' . $type)
-        ->condition('name', 'node_type_language_translation_enabled_' . $type)
-      )
-      ->execute();
+  $content_types = config_get_storage_names_with_prefix('node.type');
+  foreach ($content_types as $content_type) {
+    $type = config($content_type)->get('type');
+    config('node.settings.' . $type)->delete();
     config('language.settings')->clear('node. ' . $type . '.language.default_configuration')->save();
   }
 
@@ -734,6 +630,38 @@ function node_update_8013() {
 }
 
 /**
+ * Convert existing node types to the new config system.
+ *
+ * @ingroup config_upgrade
+ */
+function node_update_8014() {
+  $result = db_select('node_type', 'nt')
+    ->fields('nt')
+    ->execute()
+    ->fetchAllAssoc('name', PDO::FETCH_ASSOC);
+  foreach ($result as $name => $node_type) {
+
+    // Node type.
+    $config = config('node.type.' . $name);
+    $config->setData($node_type);
+    $config->save();
+    update_config_manifest_add('node.type', array($name));
+
+    // Node type settings.
+    $variables = db_select('variable', 'v')
+      ->fields('v')
+      ->condition('name', array('node_submitted_' . $name, 'node_preview_' . $name, 'node_options_' . $name))
+      ->execute();
+    $node_type_settings = config('node.settings.' . $name);
+    foreach ($variables as $variable) {
+      $node_type_settings->set(str_replace(array('node_', '_' . $name), '', $variable->name), unserialize($variable->value));
+      update_variable_del($variable->name);
+    }
+    $node_type_settings->save();
+  }
+}
+
+/**
  * @} End of "addtogroup updates-7.x-to-8.x"
  * The next series of updates should start at 9000.
  */
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index d56bee5..ecab4a6 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -486,10 +486,10 @@ function node_type_load($name) {
  */
 function node_type_save($info) {
   $existing_type = !empty($info->old_type) ? $info->old_type : $info->type;
-  $is_existing = (bool) db_query_range('SELECT 1 FROM {node_type} WHERE type = :type', 0, 1, array(':type' => $existing_type))->fetchField();
+  $is_existing = (bool) entity_load('node_type', $existing_type);
   $type = node_type_set_defaults($info);
 
-  $fields = array(
+  $content_type = entity_create('node_type', array(
     'type' => (string) $type->type,
     'name' => (string) $type->name,
     'base' => (string) $type->base,
@@ -502,13 +502,11 @@ function node_type_save($info) {
     'locked' => (int) $type->locked,
     'disabled' => (int) $type->disabled,
     'module' => $type->module,
-  );
+  ));
 
   if ($is_existing) {
-    db_update('node_type')
-      ->fields($fields)
-      ->condition('type', $existing_type)
-      ->execute();
+
+    $content_type->save();
 
     if (!empty($type->old_type) && $type->old_type != $type->type) {
       field_attach_rename_bundle('node', $type->old_type, $type->type);
@@ -517,10 +515,8 @@ function node_type_save($info) {
     $status = SAVED_UPDATED;
   }
   else {
-    $fields['orig_type'] = (string) $type->orig_type;
-    db_insert('node_type')
-      ->fields($fields)
-      ->execute();
+    $content_type->orig_type = (string) $type->orig_type;
+    $content_type->save();
 
     field_attach_create_bundle('node', $type->type);
 
@@ -636,9 +632,7 @@ function node_field_extra_fields() {
  */
 function node_type_delete($name) {
   $type = node_type_load($name);
-  db_delete('node_type')
-    ->condition('type', $name)
-    ->execute();
+  entity_delete_multiple('node_type', array($name));
   field_attach_delete_bundle('node', $name);
   module_invoke_all('node_type_delete', $type);
 
@@ -668,7 +662,7 @@ function node_type_update_nodes($old_type, $type) {
  * Builds and returns the list of available node types.
  *
  * The list of types is built by invoking hook_node_info() on all modules and
- * comparing this information with the node types in the {node_type} table.
+ * comparing this information with the node types stored in configuration.
  * These two information sources are not synchronized during module installation
  * until node_types_rebuild() is called.
  *
@@ -681,8 +675,8 @@ function node_type_update_nodes($old_type, $type) {
  *   - names: Associative array of the names of node types, keyed by the type.
  *   - types: Associative array of node type objects, keyed by the type.
  *   Both of these arrays will include new types that have been defined by
- *   hook_node_info() implementations but not yet saved in the {node_type}
- *   table. These are indicated in the type object by $type->is_new being set
+ *   hook_node_info() implementations but not yet saved as configuration.
+ *   These are indicated in the type object by $type->is_new being set
  *   to the value 1. These arrays will also include obsolete types: types that
  *   were previously defined by modules that have now been disabled, or for
  *   whatever reason are no longer being defined in hook_node_info()
@@ -714,15 +708,14 @@ function _node_types_build($rebuild = FALSE) {
       $_node_types->names[$type] = $info['name'];
     }
   }
-  $query = db_select('node_type', 'nt')
-    ->addTag('translatable')
-    ->addTag('node_type_access')
-    ->fields('nt')
-    ->orderBy('nt.type', 'ASC');
-  if (!$rebuild) {
-    $query->condition('disabled', 0);
-  }
-  foreach ($query->execute() as $type_object) {
+
+  // @todo Fix when http://drupal.org/node/1782460 is in.
+  $content_types = config_get_storage_names_with_prefix('node.type.');
+  foreach ($content_types as $config) {
+    $type_object = (object) config($config)->get();
+    if (!$rebuild && $type_object->disabled) {
+      continue;
+    }
     $type_db = $type_object->type;
     // Original disabled value.
     $disabled = $type_object->disabled;
@@ -1156,7 +1149,8 @@ function template_preprocess_node(&$variables) {
   field_attach_preprocess('node', $node, $variables['content'], $variables);
 
   // Display post information only on certain node types.
-  if (variable_get('node_submitted_' . $node->type, TRUE)) {
+  $submitted = config('node.settings.' . $node->type)->get('submitted');
+  if (is_null($submitted) || $submitted) {
     $variables['display_submitted'] = TRUE;
     $variables['submitted'] = t('Submitted by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
     if (theme_get_setting('toggle_node_user_picture')) {
@@ -2911,7 +2905,7 @@ function node_list_permissions($type) {
 function node_permissions_get_configured_types() {
   $configured_types = array();
   foreach (node_type_get_types() as $name => $type) {
-    if (variable_get('node_permissions_' . $name, 1)) {
+    if (config('node.settings.' . $name)->get('permissions') ?: TRUE) {
       $configured_types[$name] = $type;
     }
   }
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
index c1a4d24..b191ed0 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
@@ -145,6 +145,11 @@ protected function entityValues($entity_type) {
         );
       case 'node':
         return array('title' => $this->randomString());
+      case 'node_type':
+        return array(
+          'type' => 'article',
+          'name' => $this->randomName(),
+        );
       case 'user':
         return array('name' => $this->randomName());
       default:
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 07a5638..3386d42 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -2610,7 +2610,7 @@ function hook_schema() {
         'default' => 0,
       ),
       'type' => array(
-        'description' => 'The {node_type} of this node.',
+        'description' => 'The type of this node.',
         'type' => 'varchar',
         'length' => 32,
         'not null' => TRUE,
