diff --git a/src/UpstreamPluginBase.php b/src/UpstreamPluginBase.php
index 3c26109..2182f93 100644
--- a/src/UpstreamPluginBase.php
+++ b/src/UpstreamPluginBase.php
@@ -48,7 +48,6 @@ abstract class UpstreamPluginBase extends PluginBase implements UpstreamPluginIn
   public function isRemote() {
     return $this->getPluginDefinition()['remote'];
   }
-
   /**
    * {@inheritdoc}
    */
diff --git a/tests/modules/workspace_remote_test/config/install/rest.resource.replication.yml b/tests/modules/workspace_remote_test/config/install/rest.resource.replication.yml
new file mode 100644
index 0000000..098f716
--- /dev/null
+++ b/tests/modules/workspace_remote_test/config/install/rest.resource.replication.yml
@@ -0,0 +1,16 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - basic_auth
+    - workspace
+id: replication
+plugin_id: replication
+granularity: resource
+configuration:
+  methods:
+    - POST
+  formats:
+    - json
+  authentication:
+    - basic_auth
diff --git a/tests/modules/workspace_remote_test/src/Plugin/Upstream/RemoteWorkspaceUpstream.php b/tests/modules/workspace_remote_test/src/Plugin/Upstream/RemoteWorkspaceUpstream.php
new file mode 100644
index 0000000..2e1aadb
--- /dev/null
+++ b/tests/modules/workspace_remote_test/src/Plugin/Upstream/RemoteWorkspaceUpstream.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\workspace_remote_test\Plugin\Upstream;
+
+use Drupal\workspace\UpstreamPluginBase;
+use Drupal\workspace\UpstreamPluginInterface;
+
+/**
+ * Defines an upstream plugin that provides remote content replication.
+ *
+ * This plugin provides the ability to replicate content between workspaces that
+ * are defined in remote Drupal installations.
+ *
+ * @Upstream(
+ *   id = "remote_workspace",
+ *   label = @Translation("Remote workspace"),
+ *   description = @Translation("A workspace that is defined in a remote Drupal installation."),
+ *   remote = TRUE,
+ * )
+ */
+class RemoteWorkspaceUpstream extends UpstreamPluginBase implements UpstreamPluginInterface {
+
+}
diff --git a/tests/modules/workspace_remote_test/src/Plugin/rest/resource/ReplicationResource.php b/tests/modules/workspace_remote_test/src/Plugin/rest/resource/ReplicationResource.php
new file mode 100644
index 0000000..2be54b6
--- /dev/null
+++ b/tests/modules/workspace_remote_test/src/Plugin/rest/resource/ReplicationResource.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace Drupal\workspace_remote_test\Plugin\rest\resource;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\rest\Plugin\ResourceBase;
+use Drupal\rest\ResourceResponse;
+use Drupal\workspace\Entity\Workspace;
+use Drupal\workspace\WorkspaceManager;
+use Drupal\workspace\WorkspaceManagerInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Serializer\Serializer;
+
+/**
+ * Saves a collection of entity revisions.
+ *
+ * @RestResource(
+ *   id = "replication",
+ *   label = @Translation("Replication"),
+ *   uri_paths = {
+ *     "create" = "/admin/config/workflow/workspace/{workspace}/replication"
+ *   }
+ * )
+ */
+class ReplicationResource extends ResourceBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The workspace manager.
+   *
+   * @var \Drupal\workspace\WorkspaceManagerInterface
+   */
+  protected $workspaceManager;
+
+  /**
+   * The serializer.
+   *
+   * @var \Symfony\Component\Serializer\Serializer
+   */
+  protected $serializer;
+
+  /**
+   * Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param array $serializer_formats
+   *   The available serialization formats.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   A logger instance.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\workspace\WorkspaceManagerInterface $workspace_manager
+   *   The workspace manager.
+   * @param \Symfony\Component\Serializer\Serializer $serializer
+   *   The serializer.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager, Serializer $serializer) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
+    $this->entityTypeManager = $entity_type_manager;
+    $this->workspaceManager = $workspace_manager;
+    $this->serializer = $serializer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->getParameter('serializer.formats'),
+      $container->get('logger.factory')->get('rest'),
+      $container->get('entity_type.manager'),
+      $container->get('workspace.manager'),
+      $container->get('serializer')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function post($workspace_id, $revisions) {
+    $workspace = Workspace::load($workspace_id);
+    $this->workspaceManager->setActiveWorkspace($workspace);
+    foreach ($revisions as $entity_type_id => $entity_revisions) {
+      $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
+      $class = $entity_type->getClass();
+      foreach ($entity_revisions as $revision_id => $revision) {
+        /** @var \Drupal\Core\Entity\Entity $entity */
+        $entity = $this->serializer->deserialize(json_encode($revision), $class, 'json');
+        if (WorkspaceManager::DEFAULT_WORKSPACE == $workspace_id) {
+          $loaded_revision = $this->entityTypeManager->getStorage($entity_type_id)->loadRevision($entity->getRevisionId());
+          $loaded_revision->_isReplicating = TRUE;
+          $loaded_revision->isDefaultRevision(TRUE);
+          $loaded_revision->save();
+        }
+      }
+    }
+    return new ResourceResponse(TRUE);
+  }
+
+}
diff --git a/tests/modules/workspace_remote_test/src/RemoteReplicator.php b/tests/modules/workspace_remote_test/src/RemoteReplicator.php
new file mode 100644
index 0000000..ef4ea7d
--- /dev/null
+++ b/tests/modules/workspace_remote_test/src/RemoteReplicator.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Drupal\workspace_remote_test;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\workspace\Entity\Workspace;
+use Drupal\workspace\Replication\ReplicationInterface;
+use Drupal\workspace\UpstreamPluginInterface;
+use GuzzleHttp\Client;
+use Symfony\Component\Serializer\Serializer;
+
+/**
+ * Replicate between remote Drupal instances.
+ */
+class RemoteReplicator implements ReplicationInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * The serializer.
+   *
+   * @var \Symfony\Component\Serializer\Serializer
+   */
+  protected $serializer;
+
+  /**
+   * The HTTP client.
+   *
+   * @var \GuzzleHttp\Client
+   */
+  protected $client;
+
+  /**
+   * Constructs the default replicator service.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection.
+   * @param \Symfony\Component\Serializer\Serializer $serializer
+   *   The serializer.
+   * @param \GuzzleHttp\Client $client
+   *   The HTTP client.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $database, Serializer $serializer, Client $client) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->database = $database;
+    $this->serializer = $serializer;
+    $this->client = $client;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies(UpstreamPluginInterface $source, UpstreamPluginInterface $target) {
+    if ($target->getBaseId() === 'remote_workspace') {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function replicate(UpstreamPluginInterface $source, UpstreamPluginInterface $target) {
+    $source_workspace = Workspace::load($source->getDerivativeId());
+    $select = $this->database->select('content_workspace_revision', 'cwr')->fields('cwr', ['content_entity_type_id', 'content_entity_revision_id']);
+    $select->condition('workspace', $source_workspace->id());
+    $revision_ids = $select->execute()->fetchAllKeyed(1, 0);
+    $revisions = [];
+    foreach ($revision_ids as $revision_id => $entity_type_id) {
+      $revisions[$entity_type_id][$revision_id] = $this->entityTypeManager->getStorage($entity_type_id)->loadRevision($revision_id);
+    }
+    $serialized_revisions = $this->serializer->serialize($revisions, 'json');
+    $base_url = \Drupal::request()->getSchemeAndHttpHost();
+    $token = $this->client->get($base_url . '/rest/session/token')->getBody();
+    $this->client->post($base_url . '/admin/config/workflow/workspace/live/replication', [
+      'auth' => [
+        \Drupal::state()->get('remote_replicator_auth_user'),
+        \Drupal::state()->get('remote_replicator_auth_pass'),
+      ],
+      'body' => $serialized_revisions,
+      'headers' => [
+        'Content-Type' => 'application/json',
+        'X-CSRF-Token' => $token,
+      ],
+    ]);
+  }
+
+}
diff --git a/tests/modules/workspace_remote_test/workspace_remote_test.info.yml b/tests/modules/workspace_remote_test/workspace_remote_test.info.yml
new file mode 100644
index 0000000..7d34356
--- /dev/null
+++ b/tests/modules/workspace_remote_test/workspace_remote_test.info.yml
@@ -0,0 +1,10 @@
+name: 'Workspace Remote Test'
+type: module
+description: 'Provides remote replication for testing.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+  - workspace
+  - rest
+  - basic_auth
diff --git a/tests/modules/workspace_remote_test/workspace_remote_test.services.yml b/tests/modules/workspace_remote_test/workspace_remote_test.services.yml
new file mode 100644
index 0000000..d733782
--- /dev/null
+++ b/tests/modules/workspace_remote_test/workspace_remote_test.services.yml
@@ -0,0 +1,6 @@
+services:
+  workspace.remote_replicator:
+    class: Drupal\workspace_remote_test\RemoteReplicator
+    arguments: ['@entity_type.manager', '@database', '@serializer', '@http_client']
+    tags:
+      - { name: workspace_replicator, priority: 0 }
diff --git a/tests/src/Functional/RemoteReplicationTest.php b/tests/src/Functional/RemoteReplicationTest.php
new file mode 100644
index 0000000..c03e19f
--- /dev/null
+++ b/tests/src/Functional/RemoteReplicationTest.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Drupal\Tests\workspace\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\node\Traits\NodeCreationTrait;
+use Drupal\Tests\user\Traits\UserCreationTrait;
+use Drupal\workspace\Entity\Workspace;
+
+/**
+ * Tests remote replication.
+ *
+ * @group workspace
+ */
+class RemoteReplicationTest extends BrowserTestBase {
+
+  use UserCreationTrait;
+  use NodeCreationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'field',
+    'filter',
+    'node',
+    'text',
+    'user',
+    'system',
+    'views',
+    'workspace',
+    'basic_auth',
+    'serialization',
+    'rest',
+    'workspace_remote_test',
+    ];
+
+  /**
+   * The user account.
+   *
+   * @var \Drupal\user\Entity\User
+   */
+  protected $account;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->createContentType(['type' => 'page']);
+
+    $permissions = [
+      'create page content',
+      'create workspace',
+      'edit any workspace',
+      'view any workspace',
+      'restful post replication',
+    ];
+    $this->account = $this->createUser($permissions);
+    $this->setCurrentUser($this->account);
+  }
+
+  /**
+   * Test replication to a remote workspace.
+   */
+  public function testRemoteReplication() {
+    $workspace_manager = \Drupal::service('workspace.manager');
+
+    // Create three nodes on the stage workspace.
+    $workspace_manager->setActiveWorkspace(Workspace::load('stage'));
+    $node1 = $this->createNode();
+    $node2 = $this->createNode();
+    $node3 = $this->createNode();
+
+    // All three nodes should return a 404 when accessed on live.
+    $workspace_manager->setActiveWorkspace(Workspace::load('live'));
+    $this->drupalGet($node1->toUrl()->toString());
+    $assert_session = $this->assertSession();
+    $assert_session->statusCodeEquals(403);
+    $this->drupalGet($node2->toUrl()->toString());
+    $assert_session->statusCodeEquals(403);
+    $this->drupalGet($node3->toUrl()->toString());
+    $assert_session->statusCodeEquals(403);
+
+    // Store username and password to be used for replication.
+    \Drupal::state()->set('remote_replicator_auth_user', $this->account->getUsername());
+    \Drupal::state()->set('remote_replicator_auth_pass', $this->account->pass_raw);
+
+    // Replicate from state to remote (live).
+    \Drupal::service('workspace.replication_manager')->replicate(
+      \Drupal::service('plugin.manager.workspace.upstream')->createInstance('local_workspace:stage'),
+      \Drupal::service('plugin.manager.workspace.upstream')->createInstance('remote_workspace')
+    );
+
+    // Nodes should now be accessible on live.
+    $this->drupalGet($node1->toUrl()->toString());
+    $assert_session = $this->assertSession();
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet($node2->toUrl()->toString());
+    $assert_session->statusCodeEquals(200);
+    $this->drupalGet($node3->toUrl()->toString());
+    $assert_session->statusCodeEquals(200);
+  }
+
+}
