diff --git a/domain.admin.inc b/domain.admin.inc
index f3a4aa7..a5840ff 100644
--- a/domain.admin.inc
+++ b/domain.admin.inc
@@ -526,7 +526,7 @@ function domain_form_validate($form, &$form_state) {
 function domain_form_submit($form, &$form_state) {
   $values = domain_values_from_form_state($form_state);
   // Set the proper message.
-  if (!empty($values['domain_id']) || $values['domain_id'] == 0) {
+  if (!empty($values['domain_id'])) {
     $message = t('Domain record updated.');
   }
   else {
diff --git a/domain.bootstrap.inc b/domain.bootstrap.inc
index b2b63e2..30dc4df 100644
--- a/domain.bootstrap.inc
+++ b/domain.bootstrap.inc
@@ -106,7 +106,8 @@ function _domain_bootstrap($phase) {
       // If the Domain Access module has been disabled, stop loading.
       $table = domain_get_primary_table('system');
       $check = (bool) db_query("SELECT status FROM $table WHERE name = 'domain' AND type = 'module'")->fetchField();
-      if (empty($check)) {
+      // Also ensure that the machin_name field update has run.
+      if (empty($check) || !db_field_exists('domain', 'machine_name')) {
         return NULL;
       }
 
diff --git a/domain.install b/domain.install
index 1f79b66..34f1a03 100644
--- a/domain.install
+++ b/domain.install
@@ -19,19 +19,43 @@ function domain_schema() {
   $schema['domain'] = array(
     'description' => 'The base table for domain records',
     'fields' => array(
-      'domain_id' => array('type' => 'serial', 'not null' => TRUE, 'description' => 'Primary key'),
+      'domain_id' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Domain numeric id.'),
       'subdomain' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE, 'default' => '', 'description' => 'Registered DNS entry, will match HTTP_HOST requests'),
       'sitename' => array('type' => 'varchar', 'length' => '255', 'not null' => TRUE, 'default' => '', 'description' => 'Site display name'),
       'scheme' => array('type' => 'varchar', 'length' => '8', 'not null' => TRUE, 'default' => 'http', 'description' => 'Protocol'),
       'valid' => array('type' => 'varchar', 'length' => '1', 'not null' => TRUE, 'default' => '1', 'description' => 'Active status'),
       'weight' => array('type' => 'int', 'unsigned' => FALSE, 'not null' => TRUE, 'default' => 0, 'description' => 'Sort order'),
-      'is_default' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Indicates primary domain')),
+      'is_default' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Indicates primary domain'),
+      'machine_name' => array('type' => 'varchar', 'length' => '40', 'not null' => TRUE, 'default' => '', 'description' => 'The machine name for this domain.')),
     'primary key' => array('domain_id'),
     'indexes' => array(
       'subdomain' => array('subdomain'),
       'weight' => array('weight'),
       'is_default' => array('is_default'),
     ),
+    'foreign_keys' => array(
+      'domain_id' => array('domain_export' => 'domain_id'),
+      'machine_name' => array('domain_export' => 'machine_name'),
+    ),
+    'export' => array(
+      'identifier' => 'domain',
+      'key' => 'machine_name',
+      'primary key' => 'machine_name',
+      'admin_title' => 'subdomain',
+      'default hook' => 'domain_default_domains',
+      'api' => array(
+        'owner' => 'domain',
+        'api' => 'domain',
+        'minimum_version' => 3,
+        'current_version' => 3,
+      ),
+      'can disable' => FALSE,
+      'load callback' => 'domain_export_load',
+      'load all callback' => 'domain_export_load_all',
+      'create callback' => 'domain_export_create',
+      'save callback' => 'domain_export_save',
+      'delete callback' => 'domain_delete',
+    ),
   );
   $schema['domain_access'] = array(
     'description' => 'Stores domain information for each node',
@@ -58,6 +82,17 @@ function domain_schema() {
       'domain_id' => array('domain' => 'domain_id'),
     ),
   );
+  $schema['domain_export'] = array(
+    'description' => 'Stores canonical machine names for domains.',
+    'fields' => array(
+      'domain_id' => array('type' => 'serial', 'description' => 'Domain id. Automatic master key.'),
+      'machine_name' => array('type' => 'varchar', 'length' => '40', 'not null' => TRUE, 'default' => '', 'description' => 'The machine name for this domain.'),
+      'status' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Stores whether domain is loaded from code.')),
+    'primary key' => array('machine_name'),
+    'indexes' => array(
+      'domain_id' => array('domain_id')
+    ),
+  );
   return $schema;
 }
 
@@ -105,7 +140,7 @@ function domain_uninstall() {
 /**
  * Update block deltas to Drupal 7.
  */
-function domain_update_7000() {
+function domain_update_7000(&$sandbox) {
   // Get an array of the renamed block deltas, organized by module.
   $renamed_deltas = array(
     'domain' => array(
@@ -120,7 +155,7 @@ function domain_update_7000() {
 /**
  * Change the edit and delete permissions.
  */
-function domain_update_7001() {
+function domain_update_7001(&$sandbox) {
   db_update('role_permission')
     ->condition('permission', 'edit domain nodes')
     ->fields(array('permission' => 'edit domain content'))
@@ -135,7 +170,7 @@ function domain_update_7001() {
 /**
  * Add sorting to domains.
  */
-function domain_update_7300() {
+function domain_update_7300(&$sandbox) {
   if (db_field_exists('domain', 'weight')) {
     return('No update required');
   }
@@ -148,7 +183,7 @@ function domain_update_7300() {
 /**
  * Add default domain flag and weight the default higher.
  */
-function domain_update_7301() {
+function domain_update_7301(&$sandbox) {
   if (db_field_exists('domain', 'is_default')) {
     return('No update required');
   }
@@ -164,7 +199,7 @@ function domain_update_7301() {
 /**
  * Add an index on {domain}.is_default.
  */
-function domain_update_7302() {
+function domain_update_7302(&$sandbox) {
   if (!db_index_exists('domain', 'is_default')) {
     db_add_index('domain', 'is_default', array('is_default'));
   }
@@ -173,7 +208,7 @@ function domain_update_7302() {
 /**
  * Remove the zero record from the database.
  */
-function domain_update_7303() {
+function domain_update_7303(&$sandbox) {
   // We grab the default domain, remove it from the database, and
   // then re-save it into the table, using the new value as the default domain.
   $default = db_query("SELECT * FROM {domain} WHERE domain_id = 0")->fetchAssoc();
@@ -214,3 +249,70 @@ function domain_update_7303() {
   // Update message.
   return t('Domain Access updated successfully');
 }
+
+/**
+ * Allow domains to be made exportable.
+ */
+function domain_update_7304(&$sandbox) {
+  if (db_table_exists('domain_export')) {
+    return t('{domain_export} table exists.');
+  }
+  $schema = array(
+    'description' => 'Stores canonical machine names for domains.',
+    'fields' => array(
+      'domain_id' => array('type' => 'serial', 'description' => 'Domain id. Automatic master key.'),
+      'machine_name' => array('type' => 'varchar', 'length' => '40', 'not null' => TRUE, 'default' => '', 'description' => 'The machine name for this domain.'),
+      'status' => array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Stores whether domain is loaded from code.')),
+    'primary key' => array('machine_name'),
+    'indexes' => array(
+      'domain_id' => array('domain_id')
+    ),
+  );
+  db_create_table('domain_export', $schema);
+  return t('{domain_export} table created.');
+}
+
+/**
+ * Rebuild the {domain} table.
+ */
+function domain_update_7305(&$sandbox) {
+  // Reset the domain_id field as a plain integer.
+  db_drop_primary_key('domain');
+  $spec = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Domain numeric id.');
+  db_change_field('domain', 'domain_id', 'domain_id', $spec);
+
+  // Add the machine name column.
+  if (!db_field_exists('domain', 'machine_name')) {
+    $spec = array('type' => 'varchar', 'length' => '40', 'not null' => TRUE, 'default' => '', 'description' => 'The machine name for this domain.');
+    db_add_field('domain', 'machine_name', $spec);
+  }
+
+  // Now we can safely add the new primary key.
+  db_add_primary_key('domain', array('domain_id'));
+
+  return t('{domain} table updated.');
+}
+
+/**
+ * Insert existing domain records into {domain_export} and {domain}.
+ */
+function domain_update_7306(&$sandbox) {
+  $domains = db_query("SELECT domain_id, subdomain FROM {domain} ORDER BY domain_id")->fetchAll();
+  foreach ($domains as $domain) {
+    $query = db_insert('domain_export')
+      ->fields(array(
+        'domain_id' => $domain->domain_id,
+        'machine_name' => domain_machine_name($domain->subdomain),
+        'status' => 0,
+      ));
+    $query->execute();
+    $query = db_update('domain')
+      ->condition('domain_id', $domain->domain_id)
+      ->fields(array(
+        'machine_name' => domain_machine_name($domain->subdomain),
+      ));
+    $query->execute();
+  }
+
+  return t('{domain_export} table populated.');
+}
diff --git a/domain.module b/domain.module
index b0b0613..ebe18cb 100644
--- a/domain.module
+++ b/domain.module
@@ -36,6 +36,12 @@ define('DOMAIN_ASSIGN_USERS', TRUE);
 define('DOMAIN_LIST_SIZE', 25);
 
 /**
+ * Defines the export statuses for a domain.
+ */
+define('DOMAIN_EXPORT_STATUS_DATABASE', 0);
+define('DOMAIN_EXPORT_STATUS_CODE', 1);
+
+/**
  * Module setup tasks.
  *
  * 1. Adds the domain user data to the $user object.
@@ -276,6 +282,19 @@ function domain_menu() {
     'file' => 'domain.admin.inc',
     'weight' => 50,
   );
+  if (module_exists('ctools')) {
+    $items['admin/structure/domain/export'] = array(
+      'title' => 'Export',
+      'access arguments' => array('administer domains'),
+      'page callback' => 'ctools_export_ui_switcher_page',
+      'page arguments' => array('domain', 'list'),
+      'description' => 'Edit domain record.',
+      'file' => 'export-ui.inc',
+      'file path' => drupal_get_path('module', 'ctools') . '/includes/',
+      'type' => MENU_LOCAL_TASK,
+      'weight' => 20,
+    );
+  }
   return $items;
 }
 
@@ -903,6 +922,10 @@ function domain_load($domain_id = NULL) {
  *   $domain array on success or -1 on failure.
  */
 function domain_save($values, $form_values = array()) {
+  // Exportables might pass an object.
+  if (is_object($values)) {
+    $values = (array) $values;
+  }
   // Must this be the default domain?
   // Used in cases where there are no domains present.
   $count = (bool) db_query("SELECT COUNT(domain_id) FROM {domain} WHERE is_default = 1")->fetchField();
@@ -914,9 +937,30 @@ function domain_save($values, $form_values = array()) {
 
   if (!empty($update)) {
     $action = 'domain_update';
+    $current = db_query("SELECT * FROM {domain} WHERE domain_id = :domain_id", array(':domain_id' => $values['domain_id']))->fetchAssoc();
+    // Cannot compare is_default right now.
+    // TODO: default handling from this function.
+    unset($current['is_default']);
+    $test = array_diff($current, $values);
+    if (!empty($test)) {
+      db_update('domain_export')
+        ->fields(array(
+          'status' => DOMAIN_EXPORT_STATUS_DATABASE,
+        ))
+        ->condition('domain_id', $values['domain_id'])
+        ->execute();
+    }
   }
   else {
     $action = 'domain_insert';
+    // Create a new record for possible exports.
+    $new_record = array(
+      'machine_name' => domain_machine_name($values['subdomain']),
+      'status' => DOMAIN_EXPORT_STATUS_DATABASE,
+    );
+    drupal_write_record('domain_export', $new_record);
+    $values['domain_id'] = $new_record['domain_id'];
+    $values['machine_name'] = $new_record['machine_name'];
   }
 
   // If this is the default domain, reset other domains.
@@ -947,6 +991,11 @@ function domain_save($values, $form_values = array()) {
  *   keys, which are the domains ids for re-assignment.
  */
 function domain_delete($domain, $values = array()) {
+  // Exportables might pass an object.
+  if (is_object($domain)) {
+    $domain = (array) $domain;
+  }
+
   // Let other modules act to reassign data.
   foreach (array('domain_access', 'domain_editor') as $table) {
     if (isset($values[$table]) && $values[$table] != 'none') {
@@ -972,6 +1021,9 @@ function domain_delete($domain, $values = array()) {
   db_delete('domain')
     ->condition('domain_id', $domain['domain_id'])
     ->execute();
+  db_delete('domain_export')
+    ->condition('domain_id', $domain['domain_id'])
+    ->execute();
 
   // In all cases, we need to force a menu rebuild, which also clears the cache.
   menu_rebuild();
@@ -1009,7 +1061,7 @@ function domain_lookup($domain_id = NULL, $subdomain = NULL, $reset = FALSE) {
   }
   // Get the data from the database. Doing this prevents extra queries.
   if (!isset($result) || $reset) {
-    $result = db_query("SELECT domain_id, subdomain, sitename, scheme, valid, weight, is_default FROM {domain}", array(), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('domain_id');
+    $result = db_query("SELECT domain_id, subdomain, sitename, scheme, valid, weight, is_default, machine_name FROM {domain}", array(), array('fetch' => PDO::FETCH_ASSOC))->fetchAllAssoc('domain_id');
   }
   // If both are NULL, no lookup can be run.
   if ((is_null($domain_id) && is_null($subdomain)) || $domain_id < 0) {
@@ -1063,7 +1115,7 @@ function domain_lookup($domain_id = NULL, $subdomain = NULL, $reset = FALSE) {
 function domain_default($reset = FALSE, $alter = TRUE) {
   $default = &drupal_static(__FUNCTION__);
   if (empty($default) || $reset) {
-    $default = db_query("SELECT domain_id, subdomain, sitename, scheme, valid, weight, is_default FROM {domain} WHERE is_default = 1")->fetchAssoc();
+    $default = db_query("SELECT domain_id, subdomain, sitename, scheme, valid, weight, is_default, machine_name FROM {domain} WHERE is_default = 1")->fetchAssoc();
     // Let submodules overwrite the defaults, if they wish.
     if (empty($default)) {
       $default = array(
@@ -1072,7 +1124,8 @@ function domain_default($reset = FALSE, $alter = TRUE) {
         'sitename' => variable_get('site_name', 'Drupal'),
         'scheme' => empty($_SERVER['HTTPS']) ? 'http' : 'https',
         'valid' => 1,
-        'is_default' => 1
+        'is_default' => 1,
+        'machine_name' => domain_machine_name($default['subdomain']),
       );
     }
     if ($alter) {
@@ -1118,14 +1171,23 @@ function domain_set_primary_domain() {
   }
   $check = (bool) db_query("SELECT COUNT(domain_id) FROM {domain} WHERE is_default = 1")->fetchField();
   if (empty($check)) {
+    // Create a new record for possible exports.
+    $new_record = array(
+      'machine_name' => domain_machine_name($root),
+      'status' => DOMAIN_EXPORT_STATUS_DATABASE,
+    );
+    drupal_write_record('domain_export', $new_record);
+    // Write this record to the db.
     db_insert('domain')
       ->fields(array(
+          'domain_id' => $new_record['domain_id'],
           'subdomain' => $root,
           'sitename' => $site,
           'scheme' => $scheme,
           'weight' => -1,
           'valid' => 1,
           'is_default' => 1,
+          'machine_name' => domain_machine_name($root),
       ))
       ->execute();
     // Allow other modules to respond to changes.
@@ -2971,6 +3033,77 @@ function domain_alter_node_query(QueryAlterableInterface $query, $all_affiliates
       $query->distinct();
     }
   }
+  
+  // Let other modules act.
+  module_invoke_all('domain_reassign', $old_domain, $new_domain, $table);
+
+  // In all cases, we need to force a menu rebuild, which also clears the cache.
+  menu_rebuild();
+
+  // Notify that node access needs to be rebuilt.
+  node_access_needs_rebuild(TRUE);
+}
+
+/**
+ * Implements hook_ctools_plugin_directory.
+ */
+function domain_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'ctools' && $plugin == 'export_ui') {
+    return 'plugins/' . $plugin;
+  }
+}
+
+/**
+ * Generate a machine name for a domain.
+ *
+ * @param $string
+ *  The domain string.
+ *
+ * @return
+ *  A sanitized machine name.
+ */
+function domain_machine_name($string) {
+  return str_replace(array('.', ':'), '_', $string);
+}
+
+/**
+ * Form handler for export_ui.
+ */
+function domain_export_form(&$form, &$form_state) {
+  module_load_include('inc', 'domain', 'domain.admin');
+  // CTools is not handing the item as documented.
+  $id = arg(5);
+  if (!empty($id)) {
+    $form_state['item'] = domain_export_load($id);
+    $domain = (array) $form_state['item'];
+  }
+  $form = domain_form($form, $form_state, $domain);
+  $form['#redirect'] = 'admin/structure/domain/export';
+  unset($form['submit']);
+  $form['#submit'] = array('domain_form_submit');
+}
+
+function domain_export_load_all($reset = FALSE, $machine_name = NULL) {
+  $domains = &drupal_static(__FUNCTION__);
+  if (!isset($domains) || $reset) {
+    $data = domain_domains(TRUE, FALSE);
+    foreach ($data as $record) {
+      $record['type'] = 'what to do here';
+      $domains[$record['machine_name']] = (object) $record;
+    }
+  }
+  if (is_null($machine_name)) {
+    return $domains;
+  }
+  if (isset($domains[$machine_name])) {
+    return $domains[$machine_name];
+  }
+  return FALSE;
+}
+
+function domain_export_load($machine_name) {
+  $data = domain_export_load_all(TRUE, $machine_name);
+  return $data;
 }
 
 /**
diff --git a/plugins/export_ui/domain.inc b/plugins/export_ui/domain.inc
new file mode 100644
index 0000000..de88bd0
--- /dev/null
+++ b/plugins/export_ui/domain.inc
@@ -0,0 +1,20 @@
+<?php
+
+$plugin = array(
+  'schema' => 'domain',
+  'access' => 'administer domains',
+  'menu' => array(
+    'menu item' => 'domain/export',
+    'menu title' => 'Export',
+    'menu description' => 'Import and export domain settings.',
+  ),
+  'title singular' => t('domain'),
+  'title singular proper' => t('Domain'),
+  'title plural' => t('domains'),
+  'title plural proper' => t('Domains'),
+  'form' => array(
+    'settings' => 'domain_export_form',
+    'validate' => 'domain_form_validate',
+    'submit' => 'domain_form_submit',
+  ),
+);
diff --git a/tests/domain.test b/tests/domain.test
index 1080dfd..3d57222 100644
--- a/tests/domain.test
+++ b/tests/domain.test
@@ -25,8 +25,10 @@ class DomainTestCase extends DrupalWebTestCase {
     if (empty($_SERVER['HTTP_HOST']) || $_SERVER['HTTP_HOST'] == 'default') {
       $_SERVER['HTTP_HOST'] = 'example.com';
     }
-    // Build teh {domain} table for this test run.
+    // Build the {domain} tables for this test run.
     db_query("TRUNCATE {domain}");
+    // TODO: Tests for domain exports.
+    db_query("TRUNCATE {domain_export}");
     domain_set_primary_domain();
     db_query("UPDATE {domain} SET sitename = 'TestDomainSitename' WHERE is_default = 1");
 
@@ -129,8 +131,18 @@ class DomainInstallTest extends DomainTestCase {
 
   // If the default domain is not in the database, the sitename will be default sitename.
   public function testDomainInstall() {
-    $domain = db_query("SELECT sitename FROM {domain} WHERE is_default = 1")->fetch();
-    $this->assertTrue($domain->sitename == 'TestDomainSitename', t('Default domain created successfully'));
+    $domain = domain_default(TRUE, FALSE);
+    $this->assertTrue($domain['sitename'] == 'TestDomainSitename', t('Default domain created successfully'));
+    $records = db_query("SELECT COUNT(domain_id) FROM {domain_export}")->fetchField();
+    $this->assertTrue($records == 1, t('{domain_export} has one record.'));
+    $domains = db_query("SELECT COUNT(domain_id) FROM {domain}")->fetchField();
+    $this->assertTrue($domains == 1, t('{domain} has one record.'));
+    $join = db_query("SELECT COUNT(d.domain_id) FROM {domain} d INNER JOIN {domain_export} de ON d.domain_id = de.domain_id")->fetchField();
+    $this->assertTrue($join == 1, t('Database counts match.'));
+    $join2 = db_query("SELECT COUNT(d.domain_id) FROM {domain} d INNER JOIN {domain_export} de ON d.domain_id = de.domain_id WHERE d.domain_id = 1")->fetchField();
+    $this->assertTrue($join2 == 1, t('Database records match.'));
+    $join = db_query("SELECT COUNT(d.domain_id) FROM {domain} d INNER JOIN {domain_export} de ON d.machine_name = de.machine_name")->fetchField();
+    $this->assertTrue($join == 1, t('Machine names match.'));
   }
 
   // Check the existence of our default grant.
@@ -411,6 +423,7 @@ class DomainUnitTest extends DomainTestCase {
     $nodes = node_load_multiple($node_ids);
     // Check domains.
     $set = TRUE;
+    $result = db_query("SELECT * FROM {domain_export}")->fetchAll();
     // TODO: This could be more elegant.
     foreach ($nodes as $node) {
       if (!isset($node->domains)) {
