diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 6492b10..3710eae 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -2641,7 +2641,6 @@ function menu_reset_static_cache() {
   drupal_static_reset('menu_tree');
   drupal_static_reset('menu_tree_all_data');
   drupal_static_reset('menu_tree_page_data');
-  drupal_static_reset('menu_load_all');
   drupal_static_reset('menu_link_get_preferred');
 }
 
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 26bcb25..a744f93 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -1039,11 +1039,11 @@ function block_user_role_delete($role) {
 function block_menu_delete($menu) {
   db_delete('block')
     ->condition('module', 'menu')
-    ->condition('delta', $menu['menu_name'])
+    ->condition('delta', $menu->id())
     ->execute();
   db_delete('block_role')
     ->condition('module', 'menu')
-    ->condition('delta', $menu['menu_name'])
+    ->condition('delta', $menu->id())
     ->execute();
 }
 
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index a04c0dd..dd2a1cc 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -76,8 +76,8 @@ 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
-          // actual bundle object from the translated menu router path
+          // %comment_node_type). To allow field_ui_instance_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
           // loader. The position needs to be casted into a string; otherwise it
@@ -90,7 +90,7 @@ function field_ui_menu() {
             $bundle_arg = $bundle_name;
             $bundle_pos = '0';
           }
-          // This is the position of the %field_ui_menu placeholder in the
+          // This is the position of the %field_ui_instance placeholder in the
           // items below.
           $field_position = count(explode('/', $path)) + 1;
 
@@ -109,15 +109,15 @@ function field_ui_menu() {
             'weight' => 1,
             'file' => 'field_ui.admin.inc',
           ) + $access;
-          $items["$path/fields/%field_ui_menu"] = array(
+          $items["$path/fields/%field_ui_instance"] = array(
             'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
-            'title callback' => 'field_ui_menu_title',
+            'title callback' => 'field_ui_instance_title',
             'title arguments' => array($field_position),
             'page callback' => 'drupal_get_form',
             'page arguments' => array('field_ui_field_edit_form', $field_position),
             'file' => 'field_ui.admin.inc',
           ) + $access;
-          $items["$path/fields/%field_ui_menu/edit"] = array(
+          $items["$path/fields/%field_ui_instance/edit"] = array(
             'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
             'title' => 'Edit',
             'page callback' => 'drupal_get_form',
@@ -125,7 +125,7 @@ function field_ui_menu() {
             'type' => MENU_DEFAULT_LOCAL_TASK,
             'file' => 'field_ui.admin.inc',
           ) + $access;
-          $items["$path/fields/%field_ui_menu/field-settings"] = array(
+          $items["$path/fields/%field_ui_instance/field-settings"] = array(
             'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
             'title' => 'Field settings',
             'page callback' => 'drupal_get_form',
@@ -133,7 +133,7 @@ function field_ui_menu() {
             'type' => MENU_LOCAL_TASK,
             'file' => 'field_ui.admin.inc',
           ) + $access;
-          $items["$path/fields/%field_ui_menu/widget-type"] = array(
+          $items["$path/fields/%field_ui_instance/widget-type"] = array(
             'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
             'title' => 'Widget type',
             'page callback' => 'drupal_get_form',
@@ -141,7 +141,7 @@ function field_ui_menu() {
             'type' => MENU_LOCAL_TASK,
             'file' => 'field_ui.admin.inc',
           ) + $access;
-          $items["$path/fields/%field_ui_menu/delete"] = array(
+          $items["$path/fields/%field_ui_instance/delete"] = array(
             'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
             'title' => 'Delete',
             'page callback' => 'drupal_get_form',
@@ -211,12 +211,12 @@ function field_ui_menu() {
  *
  * @ingroup field
  */
-function field_ui_menu_load($field_name, $entity_type, $bundle_name, $bundle_pos, $map) {
+function field_ui_instance_load($field_name, $entity_type, $bundle_name, $bundle_pos, $map) {
   // Extract the actual bundle name from the translated argument map.
   // 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/%node_type/fields/%field_ui_instance
+  // - admin/structure/types/manage/%comment_node_type/fields/%field_ui_instance
   // 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.
@@ -242,7 +242,7 @@ function field_ui_menu_load($field_name, $entity_type, $bundle_name, $bundle_pos
  *
  * @see field_ui_menu()
  */
-function field_ui_menu_title($instance) {
+function field_ui_instance_title($instance) {
   return $instance['label'];
 }
 
diff --git a/core/modules/menu/config/menu.menu.account.yml b/core/modules/menu/config/menu.menu.account.yml
new file mode 100644
index 0000000..6be2706
--- /dev/null
+++ b/core/modules/menu/config/menu.menu.account.yml
@@ -0,0 +1,3 @@
+id: account
+label: User account menu
+description: Links related to the user account.
diff --git a/core/modules/menu/config/menu.menu.admin.yml b/core/modules/menu/config/menu.menu.admin.yml
new file mode 100644
index 0000000..5435da5
--- /dev/null
+++ b/core/modules/menu/config/menu.menu.admin.yml
@@ -0,0 +1,3 @@
+id: admin
+label: Administration
+description: Contains links to administrative tasks.
diff --git a/core/modules/menu/config/menu.menu.main.yml b/core/modules/menu/config/menu.menu.main.yml
new file mode 100644
index 0000000..3dfe975
--- /dev/null
+++ b/core/modules/menu/config/menu.menu.main.yml
@@ -0,0 +1,3 @@
+id: main
+label: Main navigation
+description: Use this for linking to the main site sections.
diff --git a/core/modules/menu/config/menu.menu.tools.yml b/core/modules/menu/config/menu.menu.tools.yml
new file mode 100644
index 0000000..3f15287
--- /dev/null
+++ b/core/modules/menu/config/menu.menu.tools.yml
@@ -0,0 +1,3 @@
+id: tools
+label: Tools
+description: Contains links for site visitors. Some modules add their links here.
diff --git a/core/modules/menu/lib/Drupal/menu/MenuListController.php b/core/modules/menu/lib/Drupal/menu/MenuListController.php
new file mode 100644
index 0000000..881c3a1
--- /dev/null
+++ b/core/modules/menu/lib/Drupal/menu/MenuListController.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * Definition of Drupal\contact\MenuListController.
+ */
+
+namespace Drupal\menu;
+
+use Drupal\Core\Config\Entity\ConfigEntityListController;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides a listing of contact categories.
+ */
+class MenuListController extends ConfigEntityListController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::buildHeader().
+   */
+  public function buildHeader() {
+    $row['title'] = t('Title');
+    $row['operations'] = t('Operations');
+    return $row;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::buildRow().
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['title'] = array(
+      'data' => array(
+        '#theme' => 'menu_admin_overview',
+        '#label' => $entity->label(),
+        '#id' => $entity->id(),
+        '#description' => $entity->description,
+      ),
+    );
+    $row['operations']['data'] = $this->buildOperations($entity);
+    return $row;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListController::getOperations();
+   */
+  public function getOperations(EntityInterface $menu) {
+    $uri = $menu->uri();
+    $path = $uri['path'];
+
+    $operations['list'] = array(
+      'title' => t('list links'),
+      'href' => $path,
+      'weight' => 0,
+    );
+    $operations['edit'] = array(
+      'title' => t('edit menu'),
+      'href' => $path . '/edit',
+      'weight' => 5,
+    );
+    $operations['add'] = array(
+      'title' => t('add link'),
+      'href' => $path . '/add',
+      'weight' => 10,
+    );
+    return $operations;
+  }
+
+}
diff --git a/core/modules/menu/lib/Drupal/menu/Plugin/Core/Entity/Menu.php b/core/modules/menu/lib/Drupal/menu/Plugin/Core/Entity/Menu.php
new file mode 100644
index 0000000..6365073
--- /dev/null
+++ b/core/modules/menu/lib/Drupal/menu/Plugin/Core/Entity/Menu.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\menu\Plugin\Core\Entity\Menu.
+ */
+
+namespace Drupal\menu\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the Menu configuration entity class.
+ *
+ * @Plugin(
+ *   id = "menu",
+ *   label = @Translation("Menu"),
+ *   module = "menu",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   list_controller_class = "Drupal\menu\MenuListController",
+ *   uri_callback = "menu_uri",
+ *   config_prefix = "menu.menu",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "label" = "label",
+ *     "uuid" = "uuid"
+ *   }
+ * )
+ */
+class Menu extends ConfigEntityBase {
+
+  /**
+   * The menu machine name.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The file UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The human-readable name of the menu entity.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * The menu description.
+   *
+   * @var string
+   */
+  public $description;
+
+}
diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
index e0b9b92..caf8eaf 100644
--- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
+++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
@@ -102,8 +102,8 @@ function doStandardMenuTests() {
    */
   function doCustomMenuTests() {
     $this->menu = $this->addCustomMenu();
-    $this->doMenuTests($this->menu['menu_name']);
-    $this->addInvalidMenuLink($this->menu['menu_name']);
+    $this->doMenuTests($this->menu->id());
+    $this->addInvalidMenuLink($this->menu->id());
     $this->addCustomMenuCRUD();
   }
 
@@ -113,25 +113,25 @@ function doCustomMenuTests() {
   function addCustomMenuCRUD() {
     // Add a new custom menu.
     $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
-    $title = $this->randomName(16);
+    $label = $this->randomName(16);
 
     $menu = array(
-      'menu_name' => $menu_name,
-      'title' => $title,
+      'id' => $menu_name,
+      'label' => $label,
       'description' => 'Description text',
     );
     menu_save($menu);
 
     // Assert the new menu.
     $this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
-    $this->assertRaw($title, 'Custom menu was added.');
+    $this->assertRaw($label, 'Custom menu was added.');
 
     // Edit the menu.
-    $new_title = $this->randomName(16);
-    $menu['title'] = $new_title;
+    $new_label = $this->randomName(16);
+    $menu['label'] = $new_label;
     menu_save($menu);
     $this->drupalGet('admin/structure/menu/manage/' . $menu_name . '/edit');
-    $this->assertRaw($new_title, 'Custom menu was edited.');
+    $this->assertRaw($new_label, 'Custom menu was edited.');
   }
 
   /**
@@ -143,11 +143,11 @@ function addCustomMenu() {
     // Try adding a menu using a menu_name that is too long.
     $this->drupalGet('admin/structure/menu/add');
     $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI + 1);
-    $title = $this->randomName(16);
+    $label = $this->randomName(16);
     $edit = array(
-      'menu_name' => $menu_name,
+      'id' => $menu_name,
       'description' => '',
-      'title' =>  $title,
+      'label' =>  $label,
     );
     $this->drupalPost('admin/structure/menu/add', $edit, t('Save'));
 
@@ -160,7 +160,7 @@ function addCustomMenu() {
 
     // Change the menu_name so it no longer exceeds the maximum length.
     $menu_name = substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI);
-    $edit['menu_name'] = $menu_name;
+    $edit['id'] = $menu_name;
     $this->drupalPost('admin/structure/menu/add', $edit, t('Save'));
 
     // Verify that no validation error is given for menu_name length.
@@ -172,7 +172,7 @@ function addCustomMenu() {
     // Unlike most other modules, there is no confirmation message displayed.
 
     $this->drupalGet('admin/structure/menu');
-    $this->assertText($title, 'Menu created');
+    $this->assertText($label, 'Menu created');
 
     // Enable the custom menu block.
     $menu_name = 'menu-' . $menu_name; // Drupal prepends the name with 'menu-'.
@@ -191,13 +191,13 @@ function addCustomMenu() {
    * @param string $menu_name Custom menu name.
    */
   function deleteCustomMenu($menu) {
-    $menu_name = $this->menu['menu_name'];
-    $title = $this->menu['title'];
+    $menu_name = $this->menu->id();
+    $label = $this->menu->label();
 
     // Delete custom menu.
     $this->drupalPost("admin/structure/menu/manage/$menu_name/delete", array(), t('Delete'));
     $this->assertResponse(200);
-    $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $title)), 'Custom menu was deleted');
+    $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $label)), 'Custom menu was deleted');
     $this->assertFalse(menu_load($menu_name), 'Custom menu was deleted');
     // Test if all menu links associated to the menu were removed from database.
     $result = db_query("SELECT menu_name FROM {menu_links} WHERE menu_name = :menu_name", array(':menu_name' => $menu_name))->fetchField();
diff --git a/core/modules/menu/menu.admin.inc b/core/modules/menu/menu.admin.inc
index 4a78495..1339379 100644
--- a/core/modules/menu/menu.admin.inc
+++ b/core/modules/menu/menu.admin.inc
@@ -6,40 +6,13 @@
  */
 
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Drupal\menu\Plugin\Core\Entity\Menu;
 
 /**
  * Menu callback which shows an overview page of all the custom menus and their descriptions.
  */
 function menu_overview_page() {
-  $result = db_query("SELECT * FROM {menu_custom} ORDER BY title", array(), array('fetch' => PDO::FETCH_ASSOC));
-  $header = array(t('Title'), t('Operations'));
-  $rows = array();
-  foreach ($result as $menu) {
-    $row = array();
-    $row[] = theme('menu_admin_overview', array('title' => $menu['title'], 'name' => $menu['menu_name'], 'description' => $menu['description']));
-    $links = array();
-    $links['list'] = array(
-      'title' => t('list links'),
-      'href' => 'admin/structure/menu/manage/' . $menu['menu_name'],
-    );
-    $links['edit'] = array(
-      'title' => t('edit menu'),
-      'href' => 'admin/structure/menu/manage/' . $menu['menu_name'] . '/edit',
-    );
-    $links['add'] = array(
-      'title' => t('add link'),
-      'href' => 'admin/structure/menu/manage/' . $menu['menu_name'] . '/add',
-    );
-    $row[] = array(
-      'data' => array(
-        '#type' => 'operations',
-        '#links' => $links,
-      ),
-    );
-    $rows[] = $row;
-  }
-
-  return theme('table', array('header' => $header, 'rows' => $rows));
+  return entity_list_controller('menu')->render();
 }
 
 /**
@@ -47,16 +20,15 @@ function menu_overview_page() {
  *
  * @param $variables
  *   An associative array containing:
- *   - title: The menu's title.
+ *   - id: The menu's machine name.
+ *   - label: The menu's label.
  *   - description: The menu's description.
  *
  * @ingroup themeable
  */
 function theme_menu_admin_overview($variables) {
-  $output = check_plain($variables['title']);
-  $output .= '<div class="description">' . filter_xss_admin($variables['description']) . '</div>';
-
-  return $output;
+  return check_plain($variables['label']) .
+    '<div class="description">' . filter_xss_admin($variables['description']) . '</div>';
 }
 
 /**
@@ -73,7 +45,7 @@ function menu_overview_form($form, &$form_state, $menu) {
     FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path
     WHERE ml.menu_name = :menu
     ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC";
-  $result = db_query($sql, array(':menu' => $menu['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
+  $result = db_query($sql, array(':menu' => $menu->id()), array('fetch' => PDO::FETCH_ASSOC));
   $links = array();
   foreach ($result as $item) {
     $links[] = $item;
@@ -99,7 +71,7 @@ function menu_overview_form($form, &$form_state, $menu) {
     );
   }
   else {
-    $form['#empty_text'] = t('There are no menu links yet. <a href="@link">Add link</a>.', array('@link' => url('admin/structure/menu/manage/'. $form['#menu']['menu_name'] .'/add')));
+    $form['#empty_text'] = t('There are no menu links yet. <a href="@link">Add link</a>.', array('@link' => url('admin/structure/menu/manage/'. $form['#menu']->id() .'/add')));
   }
   return $form;
 }
@@ -296,7 +268,7 @@ function theme_menu_overview_form($variables) {
 function menu_edit_item($form, &$form_state, $type, $item, $menu) {
   if ($type == 'add' || empty($item)) {
     // This is an add form, initialize the menu link.
-    $item = array('link_title' => '', 'mlid' => 0, 'plid' => 0, 'menu_name' => $menu['menu_name'], 'weight' => 0, 'link_path' => '', 'options' => array(), 'module' => 'menu', 'expanded' => 0, 'hidden' => 0, 'has_children' => 0);
+    $item = array('link_title' => '', 'mlid' => 0, 'plid' => 0, 'menu_name' => $menu->id(), 'weight' => 0, 'link_path' => '', 'options' => array(), 'module' => 'menu', 'expanded' => 0, 'hidden' => 0, 'has_children' => 0);
   }
   else {
     // Get the human-readable menu title from the given menu name.
@@ -480,51 +452,55 @@ function menu_edit_item_submit($form, &$form_state) {
 /**
  * Menu callback; Build the form that handles the adding/editing of a custom menu.
  */
-function menu_edit_menu($form, &$form_state, $type, $menu = array()) {
+function menu_edit_menu($form, &$form_state, $type, $menu = NULL) {
   $system_menus = menu_list_system_menus();
+  if (!isset($menu)) {
+    $menu = entity_create('menu', array());
+  }
+  /*$menu_id = $menu->id();
   $menu += array(
-    'menu_name' => '',
-    'old_name' => !empty($menu['menu_name']) ? $menu['menu_name'] : '',
+    'id' => '',
+    'old_id' => !empty($menu_id) ? $menu_id : '',
     'title' => '',
     'description' => '',
-  );
+  );*/
   // Allow menu_edit_menu_submit() and other form submit handlers to determine
   // whether the menu already exists.
-  $form['#insert'] = empty($menu['old_name']);
+  $form['#insert'] = $menu->isNew();
   $form['old_name'] = array(
     '#type' => 'value',
-    '#value' => $menu['old_name'],
+    '#value' => $menu->getOriginalID(),
   );
 
-  $form['title'] = array(
+  $form['label'] = array(
     '#type' => 'textfield',
     '#title' => t('Title'),
-    '#default_value' => $menu['title'],
+    '#default_value' => $menu->label(),
     '#required' => TRUE,
     // The title of a system menu cannot be altered.
-    '#access' => !isset($system_menus[$menu['menu_name']]),
+    '#access' => !isset($system_menus[$menu->id()]),
   );
 
-  $form['menu_name'] = array(
+  $form['id'] = array(
     '#type' => 'machine_name',
     '#title' => t('Menu name'),
-    '#default_value' => $menu['menu_name'],
+    '#default_value' => $menu->id(),
     '#maxlength' => MENU_MAX_MENU_NAME_LENGTH_UI,
     '#description' => t('A unique name to construct the URL for the menu. It must only contain lowercase letters, numbers and hyphens.'),
     '#machine_name' => array(
       'exists' => 'menu_edit_menu_name_exists',
-      'source' => array('title'),
+      'source' => array('label'),
       'replace_pattern' => '[^a-z0-9-]+',
       'replace' => '-',
     ),
     // A menu's machine name cannot be changed.
-    '#disabled' => !empty($menu['old_name']) || isset($system_menus[$menu['menu_name']]),
+    '#disabled' => !$menu->isNew() || isset($system_menus[$menu->id()]),
   );
 
   $form['description'] = array(
     '#type' => 'textarea',
     '#title' => t('Description'),
-    '#default_value' => $menu['description'],
+    '#default_value' => $menu->description,
   );
   $form['actions'] = array('#type' => 'actions');
   $form['actions']['submit'] = array(
@@ -536,7 +512,7 @@ function menu_edit_menu($form, &$form_state, $type, $menu = array()) {
   $form['actions']['delete'] = array(
     '#type' => 'submit',
     '#value' => t('Delete'),
-    '#access' => $type == 'edit' && !isset($system_menus[$menu['menu_name']]),
+    '#access' => $type == 'edit' && !isset($system_menus[$menu->id()]),
     '#submit' => array('menu_custom_delete_submit'),
   );
 
@@ -547,7 +523,7 @@ function menu_edit_menu($form, &$form_state, $type, $menu = array()) {
  * Submit function for the 'Delete' button on the menu editing form.
  */
 function menu_custom_delete_submit($form, &$form_state) {
-  $form_state['redirect'] = 'admin/structure/menu/manage/' . $form_state['values']['menu_name'] . '/delete';
+  $form_state['redirect'] = 'admin/structure/menu/manage/' . $form_state['values']['id'] . '/delete';
 }
 
 /**
@@ -556,7 +532,7 @@ function menu_custom_delete_submit($form, &$form_state) {
 function menu_delete_menu_page($menu) {
   // System-defined menus may not be deleted.
   $system_menus = menu_list_system_menus();
-  if (isset($system_menus[$menu['menu_name']])) {
+  if (isset($system_menus[$menu->id()])) {
     throw new AccessDeniedHttpException();
   }
   return drupal_get_form('menu_delete_menu_confirm', $menu);
@@ -565,15 +541,15 @@ function menu_delete_menu_page($menu) {
 /**
  * Build a confirm form for deletion of a custom menu.
  */
-function menu_delete_menu_confirm($form, &$form_state, $menu) {
+function menu_delete_menu_confirm($form, &$form_state, Menu $menu) {
   $form['#menu'] = $menu;
   $caption = '';
-  $num_links = db_query("SELECT COUNT(*) FROM {menu_links} WHERE menu_name = :menu", array(':menu' => $menu['menu_name']))->fetchField();
+  $num_links = db_query("SELECT COUNT(*) FROM {menu_links} WHERE menu_name = :menu", array(':menu' => $menu->id()))->fetchField();
   if ($num_links) {
-    $caption .= '<p>' . format_plural($num_links, '<strong>Warning:</strong> There is currently 1 menu link in %title. It will be deleted (system-defined items will be reset).', '<strong>Warning:</strong> There are currently @count menu links in %title. They will be deleted (system-defined links will be reset).', array('%title' => $menu['title'])) . '</p>';
+    $caption .= '<p>' . format_plural($num_links, '<strong>Warning:</strong> There is currently 1 menu link in %title. It will be deleted (system-defined items will be reset).', '<strong>Warning:</strong> There are currently @count menu links in %title. They will be deleted (system-defined links will be reset).', array('%title' => $menu->label())) . '</p>';
   }
   $caption .= '<p>' . t('This action cannot be undone.') . '</p>';
-  return confirm_form($form, t('Are you sure you want to delete the custom menu %title?', array('%title' => $menu['title'])), 'admin/structure/menu/manage/' . $menu['menu_name'], $caption, t('Delete'));
+  return confirm_form($form, t('Are you sure you want to delete the custom menu %title?', array('%title' => $menu->label())), 'admin/structure/menu/manage/' . $menu->id(), $caption, t('Delete'));
 }
 
 /**
@@ -585,18 +561,18 @@ function menu_delete_menu_confirm_submit($form, &$form_state) {
 
   // System-defined menus may not be deleted - only menus defined by this module.
   $system_menus = menu_list_system_menus();
-  if (isset($system_menus[$menu['menu_name']])  || !(db_query("SELECT 1 FROM {menu_custom} WHERE menu_name = :menu", array(':menu' => $menu['menu_name']))->fetchField())) {
+  if (isset($system_menus[$menu->id()])) {
     return;
   }
 
   // Reset all the menu links defined by the system via hook_menu().
-  $result = db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = :menu AND ml.module = 'system' ORDER BY m.number_parts ASC", array(':menu' => $menu['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
+  $result = db_query("SELECT * FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = :menu AND ml.module = 'system' ORDER BY m.number_parts ASC", array(':menu' => $menu->id()), array('fetch' => PDO::FETCH_ASSOC));
   foreach ($result as $link) {
     menu_reset_item($link);
   }
 
   // Delete all links to the overview page for this menu.
-  $result = db_query("SELECT mlid FROM {menu_links} ml WHERE ml.link_path = :link", array(':link' => 'admin/structure/menu/manage/' . $menu['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
+  $result = db_query("SELECT mlid FROM {menu_links} ml WHERE ml.link_path = :link", array(':link' => 'admin/structure/menu/manage/' . $menu->id()), array('fetch' => PDO::FETCH_ASSOC));
   foreach ($result as $link) {
     menu_link_delete($link['mlid']);
   }
@@ -604,7 +580,7 @@ function menu_delete_menu_confirm_submit($form, &$form_state) {
   // Delete the custom menu and all its menu links.
   menu_delete($menu);
 
-  $t_args = array('%title' => $menu['title']);
+  $t_args = array('%title' => $menu->label());
   drupal_set_message(t('The custom menu %title has been deleted.', $t_args));
   watchdog('menu', 'Deleted custom menu %title and all its menu links.', $t_args, WATCHDOG_NOTICE);
 }
@@ -616,9 +592,9 @@ function menu_delete_menu_confirm_submit($form, &$form_state) {
  * @see form_validate_machine_name()
  */
 function menu_edit_menu_name_exists($value) {
+  $custom_exists = entity_load('menu', $value);
   // 'menu-' is added to the menu name to avoid name-space conflicts.
   $value = 'menu-' . $value;
-  $custom_exists = db_query_range('SELECT 1 FROM {menu_custom} WHERE menu_name = :menu', 0, 1, array(':menu' => $value))->fetchField();
   $link_exists = db_query_range("SELECT 1 FROM {menu_links} WHERE menu_name = :menu", 0, 1, array(':menu' => $value))->fetchField();
 
   return $custom_exists || $link_exists;
@@ -632,9 +608,9 @@ function menu_edit_menu_submit($form, &$form_state) {
   $path = 'admin/structure/menu/manage/';
   if ($form['#insert']) {
     // Add 'menu-' to the menu name to help avoid name-space conflicts.
-    $menu['menu_name'] = 'menu-' . $menu['menu_name'];
-    $link['link_title'] = $menu['title'];
-    $link['link_path'] = $path . $menu['menu_name'];
+    $menu['id'] = 'menu-' . $menu['id'];
+    $link['link_title'] = $menu['label'];
+    $link['link_path'] = $path . $menu['id'];
     $link['router_path'] = $path . '%';
     $link['module'] = 'menu';
     $link['plid'] = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :link AND module = :module", array(
@@ -648,15 +624,15 @@ function menu_edit_menu_submit($form, &$form_state) {
   }
   else {
     menu_save($menu);
-    $result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path", array(':path' => $path . $menu['menu_name']), array('fetch' => PDO::FETCH_ASSOC));
+    $result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path", array(':path' => $path . $menu['id']), array('fetch' => PDO::FETCH_ASSOC));
     foreach ($result as $m) {
       $link = menu_link_load($m['mlid']);
-      $link['link_title'] = $menu['title'];
+      $link['link_title'] = $menu['label'];
       menu_link_save($link);
     }
   }
   drupal_set_message(t('Your configuration has been saved.'));
-  $form_state['redirect'] = $path . $menu['menu_name'];
+  $form_state['redirect'] = $path . $menu['id'];
 }
 
 /**
diff --git a/core/modules/menu/menu.install b/core/modules/menu/menu.install
index f1a7b05..2fa8163 100644
--- a/core/modules/menu/menu.install
+++ b/core/modules/menu/menu.install
@@ -7,7 +7,7 @@
 
 /**
  * Implements hook_schema().
- */
+ *
 function menu_schema() {
   $schema['menu_custom'] = array(
     'description' => 'Holds definitions for top-level custom menus (for example, Main navigation menu).',
@@ -38,11 +38,11 @@ function menu_schema() {
   );
 
   return $schema;
-}
+}*/
 
 /**
  * Implements hook_install().
- */
+ *
 function menu_install() {
   $system_menus = menu_list_system_menus();
   $t = get_t();
@@ -61,7 +61,7 @@ function menu_install() {
     );
     menu_save($menu);
   }
-}
+}*/
 
 /**
  * Implements hook_uninstall().
@@ -126,3 +126,27 @@ function menu_update_8003() {
   ));
 }
 
+/**
+ * Migrate menus into configuration.
+ *
+ * @ingroup config_upgrade
+ */
+function menu_update_8004() {
+  $result = db_query('SELECT * FROM {menu_custom}');
+  foreach ($result as $menu) {
+    // Save the config object.
+    config('menu.menu.' . $menu->menu_name)
+      ->set('id', $menu->menu_name)
+      ->set('label', $menu->title)
+      ->set('description', $menu->description)
+      ->save();
+  }
+}
+
+/**
+ * Drop the {menu_custom} table.
+ */
+function menu_update_8005() {
+  db_drop_table('menu_custom');
+}
+
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index 94fb8ef..2423f1b 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -11,9 +11,10 @@
  * URLs to be added to the main site navigation menu.
  */
 
-use Drupal\node\Plugin\Core\Entity\Node;
-
 use Symfony\Component\HttpFoundation\JsonResponse;
+use Drupal\node\Plugin\Core\Entity\Node;
+use Drupal\menu\Plugin\Core\Entity\Menu;
+use Drupal\Core\Config\Entity\ConfigEntityBase;
 
 /**
  * Maximum length of menu name as entered by the user. Database length is 32
@@ -101,7 +102,7 @@ function menu_menu() {
     'title' => 'Customize menu',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('menu_overview_form', 4),
-    'title callback' => 'menu_overview_title',
+    'title callback' => 'entity_page_label',
     'title arguments' => array(4),
     'access arguments' => array('administer menu'),
     'file' => 'menu.admin.inc',
@@ -161,6 +162,69 @@ function menu_menu() {
 }
 
 /**
+ * Entity URI callback.
+ *
+ * @param Drupal\menu\Plugin\Core\Entity\Menu $menu
+ *   A Menu entity.
+ */
+function menu_uri(Menu $menu) {
+  return array(
+    'path' => 'admin/structure/menu/manage/' . $menu->id(),
+  );
+}
+
+/**
+ * Implements hook_config_import_create().
+ */
+function menu_config_import_create($name, $new_config, $old_config) {
+  if (strpos($name, 'menu.menu.') !== 0) {
+    return FALSE;
+  }
+
+  $menu = entity_create('menu', $new_config->get());
+  $menu->save();
+  return TRUE;
+}
+
+/**
+ * Implements hook_config_import_change().
+ */
+function menu_config_import_change($name, $new_config, $old_config) {
+  if (strpos($name, 'menu.menu.') !== 0) {
+    return FALSE;
+  }
+
+  list(, , $id) = explode('.', $name);
+  $entity = entity_load('menu', $id);
+  $entity->original = clone $entity;
+  foreach ($old_config->get() as $property => $value) {
+    $entity->original->$property = $value;
+  }
+
+  // Iterate through each property of the new config, copying it to the menu
+  // object.
+  foreach ($new_config->get() as $property => $value) {
+    $entity->$property = $value;
+  }
+
+  $entity->save();
+  return TRUE;
+}
+
+/**
+ * Implements hook_config_import_delete().
+ */
+function menu_config_import_delete($name, $new_config, $old_config) {
+  if (strpos($name, 'menu.menu.') !== 0) {
+    return FALSE;
+  }
+
+  list(, , $id) = explode('.', $name);
+  entity_delete_multiple('menu', array($id));
+  return TRUE;
+}
+
+/**
  * Implements hook_theme().
  */
 function menu_theme() {
@@ -171,7 +235,7 @@ function menu_theme() {
     ),
     'menu_admin_overview' => array(
       'file' => 'menu.admin.inc',
-      'variables' => array('title' => NULL, 'name' => NULL, 'description' => NULL),
+      'variables' => array('label' => NULL, 'id' => NULL, 'description' => NULL),
     ),
   );
 }
@@ -186,13 +250,13 @@ function menu_enable() {
   $base_link = db_query("SELECT mlid AS plid, menu_name FROM {menu_links} WHERE link_path = 'admin/structure/menu' AND module = 'system'")->fetchAssoc();
   $base_link['router_path'] = 'admin/structure/menu/manage/%';
   $base_link['module'] = 'menu';
-  $result = db_query("SELECT * FROM {menu_custom}", array(), array('fetch' => PDO::FETCH_ASSOC));
-  foreach ($result as $menu) {
+  $menus = entity_load_multiple('menu');
+  foreach ($menus as $menu) {
     // $link is passed by reference to menu_link_save(), so we make a copy of $base_link.
     $link = $base_link;
     $link['mlid'] = 0;
-    $link['link_title'] = $menu['title'];
-    $link['link_path'] = 'admin/structure/menu/manage/' . $menu['menu_name'];
+    $link['link_title'] = $menu->label();
+    $link['link_path'] = 'admin/structure/menu/manage/' . $menu->id();
     $menu_link = db_query("SELECT mlid FROM {menu_links} WHERE link_path = :path AND plid = :plid", array(
       ':path' => $link['link_path'],
       ':plid' => $link['plid']
@@ -206,13 +270,6 @@ function menu_enable() {
 }
 
 /**
- * Title callback for the menu overview page and links.
- */
-function menu_overview_title($menu) {
-  return $menu['title'];
-}
-
-/**
  * Load the data for a single custom menu.
  *
  * @param $menu_name
@@ -221,34 +278,13 @@ function menu_overview_title($menu) {
  *   Array defining the custom menu, or FALSE if the menu doesn't exist.
  */
 function menu_load($menu_name) {
-  $all_menus = menu_load_all();
-  return isset($all_menus[$menu_name]) ? $all_menus[$menu_name] : FALSE;
-}
-
-/**
- * Load all custom menu data.
- *
- * @return
- *   Array of custom menu data.
- */
-function menu_load_all() {
-  $custom_menus = &drupal_static(__FUNCTION__);
-  if (!isset($custom_menus)) {
-    if ($cached = cache('menu')->get('menu_custom')) {
-      $custom_menus = $cached->data;
-    }
-    else {
-      $custom_menus = db_query('SELECT * FROM {menu_custom}')->fetchAllAssoc('menu_name', PDO::FETCH_ASSOC);
-      cache('menu')->set('menu_custom', $custom_menus);
-    }
-  }
-  return $custom_menus;
+  return entity_load('menu', $menu_name);
 }
 
 /**
  * Save a custom menu.
  *
- * @param $menu
+ * @param Drupal\menu\Plugin\Core\Entity\Menu $menu
  *   An array representing a custom menu:
  *   - menu_name: The unique name of the custom menu (composed of lowercase
  *     letters, numbers, and hyphens).
@@ -260,14 +296,9 @@ function menu_load_all() {
  *
  * @see menu_load()
  */
-function menu_save($menu) {
-  $status = db_merge('menu_custom')
-    ->key(array('menu_name' => $menu['menu_name']))
-    ->fields(array(
-      'title' => $menu['title'],
-      'description' => $menu['description'],
-    ))
-    ->execute();
+function menu_save($menu_array) {
+  $menu = entity_create('menu', $menu_array);
+  $status = $menu->save();
   menu_cache_clear_all();
 
   switch ($status) {
@@ -278,8 +309,8 @@ function menu_save($menu) {
       $config = config('system.menu');
 
       $active_menus = $config->get('active_menus_default') ?: array_keys(menu_get_menus());
-      if (!in_array($menu['menu_name'], $active_menus)) {
-        $active_menus[] = $menu['menu_name'];
+      if (!in_array($menu->id(), $active_menus)) {
+        $active_menus[] = $menu->id();
         $config->set('active_menus_default', $active_menus);
       }
 
@@ -301,7 +332,7 @@ function menu_save($menu) {
  * function should usually be called from a user interface (form submit) handler
  * only, which allows the user to confirm the action.
  *
- * @param $menu
+ * @param Drupal\menu\Plugin\Core\Entity\Menu $menu
  *   An array representing a custom menu:
  *   - menu_name: The unique name of the custom menu.
  *   - title: The human readable menu title.
@@ -318,21 +349,19 @@ function menu_save($menu) {
  */
 function menu_delete($menu) {
   // Delete all links from the menu.
-  menu_delete_links($menu['menu_name']);
+  menu_delete_links($menu->id());
 
   // Remove menu from active menus variable.
   $active_menus = variable_get('menu_default_active_menus', array_keys(menu_get_menus()));
   foreach ($active_menus as $i => $menu_name) {
-    if ($menu['menu_name'] == $menu_name) {
+    if ($menu->id() == $menu_name) {
       unset($active_menus[$i]);
       variable_set('menu_default_active_menus', $active_menus);
     }
   }
 
   // Delete the custom menu.
-  db_delete('menu_custom')
-    ->condition('menu_name', $menu['menu_name'])
-    ->execute();
+  $menu->delete();
 
   menu_cache_clear_all();
   module_invoke_all('menu_delete', $menu);
@@ -801,12 +830,12 @@ function menu_form_node_type_form_alter(&$form, $form_state) {
  *   titles as the values.
  */
 function menu_get_menus($all = TRUE) {
-  if ($custom_menus = menu_load_all()) {
+  if ($custom_menus = entity_load_multiple('menu')) {
     if (!$all) {
       $custom_menus = array_diff_key($custom_menus, menu_list_system_menus());
     }
     foreach ($custom_menus as $menu_name => $menu) {
-      $custom_menus[$menu_name] = t($menu['title']);
+      $custom_menus[$menu_name] = $menu->label();
     }
     asort($custom_menus);
   }
