diff --git a/domain.admin.inc b/domain.admin.inc
index 8aa394f..2a9c914 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.install b/domain.install
index 1f79b66..caa9852 100644
--- a/domain.install
+++ b/domain.install
@@ -19,7 +19,7 @@ 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' => 'Primary key'),
       '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'),
@@ -32,6 +32,22 @@ function domain_schema() {
       'weight' => array('weight'),
       'is_default' => array('is_default'),
     ),
+    'foreign_keys' => array(
+      'domain_id' => array('domain_export' => 'domain_id'),
+    ),
+    'export' => array(
+      'identifier' => 'domain',
+      'key' => 'subdomain',
+      'primary key' => 'domain_id',
+      'default hook' => 'domain_default_domains',
+      'api' => array(
+        'owner' => 'domain',
+        'api' => 'domain',
+        'minimum_version' => 3,
+        'current_version' => 3,
+      ),
+      'can disable' => FALSE,
+    ),
   );
   $schema['domain_access'] = array(
     'description' => 'Stores domain information for each node',
@@ -58,6 +74,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' => '255', '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 +132,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 +147,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 +162,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 +175,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 +191,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 +200,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 +241,53 @@ 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' => '255', '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.');
+}
+
+/**
+ * Insert existing domain records into {domain_export}.
+ */
+function domain_update_7305(&$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->subdomain,
+        'status' => 0,
+      ));
+    $query->execute();
+  }
+  return t('{domain_export} table populated.');
+}
+
+/**
+ * Rebuild the {domain} table.
+ */
+function domain_update_7306(&$sandbox) {
+  db_drop_index('domain', 'domain_id');
+  $spec = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Primary key');
+  db_change_field('domain', 'domain_id', 'domain_id', $spec);
+  db_add_index('domain', 'domain_id', array('domain_id'));
+  return t('{domain} table updated.');
+}
diff --git a/domain.module b/domain.module
index a9f6444..abf8394 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.
@@ -900,6 +906,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();
@@ -911,9 +921,29 @@ 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' => $values['subdomain'],
+      'status' => DOMAIN_EXPORT_STATUS_DATABASE,
+    );
+    drupal_write_record('domain_export', $new_record);
+    $values['domain_id'] = $new_record['domain_id'];
   }
 
   // If this is the default domain, reset other domains.
@@ -942,12 +972,19 @@ function domain_save($values, $form_values = array()) {
  *   An array of values passed by a form submit, if any.
  */
 function domain_delete($domain, $values = array()) {
+  // Exportables might pass an object.
+  if (is_object($domain)) {
+    $domain = (array) $domain;
+  }
+  // Let other modules act.
+  module_invoke_all('domain_delete', $domain, $values);
   // Delete the record.
   db_delete('domain')
     ->condition('domain_id', $domain['domain_id'])
     ->execute();
-  // Let other modules act.
-  module_invoke_all('domain_delete', $domain, $values);
+  db_delete('domain_export')
+    ->condition('domain_id', $domain['domain_id'])
+    ->execute();
   // Notify that node access needs to be rebuilt.
   node_access_needs_rebuild(TRUE);
 }
@@ -1090,8 +1127,16 @@ 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' => $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,
@@ -2991,3 +3036,12 @@ function domain_check_response($domain, $drush = FALSE) {
   }
   return FALSE;
 }
+
+/**
+ * Implements hook_ctools_plugin_directory.
+ */
+function domain_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'ctools' && $plugin == 'export_ui') {
+    return 'plugins/' . $plugin;
+  }
+}
diff --git a/plugins/export_ui.inc b/plugins/export_ui.inc
index e69de29..f89838c 100644
--- a/plugins/export_ui.inc
+++ b/plugins/export_ui.inc
@@ -0,0 +1,18 @@
+<?php
+$plugin = array(
+  'schema' => 'domain',
+  'access' => 'administer domains',
+  'menu' => array(
+    'menu prefix' => 'admin/structure',
+    'menu item' => 'domain-export',
+    'menu title' => 'Export', 
+    'menu description' => 'Export domains',
+  ),
+  'title singular' => t('domain'),
+  'title singular proper' => t('Domain'),
+  'title plural' => t('domains'),
+  'title plural proper' => t('Domains'),
+
+
+);
+
diff --git a/tests/domain.test b/tests/domain.test
index 21e8794..78a8344 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");
 
@@ -131,6 +133,14 @@ class DomainInstallTest extends DomainTestCase {
   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'));
+    $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('Datacase records match.'));
   }
 
   // Check the existence of our default grant.
@@ -395,6 +405,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)) {
