diff --git a/deploy.install b/deploy.install
new file mode 100644
index 0000000..631269f
--- /dev/null
+++ b/deploy.install
@@ -0,0 +1,8 @@
+<?php
+
+/**
+ * New permissions.
+ */
+function deploy_update_8100() {
+  return t('New permissions have been added for deployment, please update your roles.');
+}
diff --git a/deploy.module b/deploy.module
index b743e7b..93ddaa2 100644
--- a/deploy.module
+++ b/deploy.module
@@ -11,36 +11,32 @@ use Drupal\Core\Url;
  * Implements hook_toolbar().
  */
 function deploy_toolbar() {
-  $items = [];
-
   $user = \Drupal::currentUser();
-  $deploy_access = $user->hasPermission('administer deployments');
-  if ($deploy_access) {
-    $items['deploy'] = [
-      '#type' => 'toolbar_item',
-      'tab' => [
-        '#type' => 'link',
-        '#title' => t('Deploy'),
-        '#url' => Url::fromRoute('entity.replication.add_form'),
-        '#attributes' => [
-          'title' => t('Deploy'),
-          'class' => ['toolbar-icon', 'toolbar-icon-deploy', 'use-ajax'],
-          'data-dialog-type' => 'modal',
-          'data-dialog-options' => json_encode([
-            'width' => '50%',
-          ]),
-        ],
-      ],
-      '#wrapper_attributes' => [
-        'class' => ['deploy-toolbar-tab'],
+  $items['deploy'] = [
+    '#type' => 'toolbar_item',
+    'tab' => [
+      '#type' => 'link',
+      '#title' => t('Deploy'),
+      '#url' => Url::fromRoute('entity.replication.add_form'),
+      '#access' => \Drupal::entityTypeManager()->getAccessControlHandler('replication')->createAccess('', $user),
+      '#attributes' => [
+        'title' => t('Deploy'),
+        'class' => ['toolbar-icon', 'toolbar-icon-deploy', 'use-ajax'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => json_encode([
+          'width' => '50%',
+        ]),
       ],
-      '#attached' => [
-        'library' => [
-          'deploy/drupal.deploy.toolbar',
-        ],
+    ],
+    '#wrapper_attributes' => [
+      'class' => ['deploy-toolbar-tab'],
+    ],
+    '#attached' => [
+      'library' => [
+        'deploy/drupal.deploy.toolbar',
       ],
-    ];
-  }
+    ],
+  ];
 
   return $items;
 }
diff --git a/deploy.permissions.yml b/deploy.permissions.yml
index 19eac0c..fd4d44b 100644
--- a/deploy.permissions.yml
+++ b/deploy.permissions.yml
@@ -1,2 +1,5 @@
 administer deployments:
   title: 'Administer deployments'
+
+permission_callbacks:
+  - \Drupal\deploy\DeployPermissions::workspacePointerPermissions
diff --git a/deploy.routing.yml b/deploy.routing.yml
index 4f82720..777e974 100644
--- a/deploy.routing.yml
+++ b/deploy.routing.yml
@@ -15,6 +15,6 @@ entity.replication.add_form:
     _entity_form: replication.add
     _title_callback: '\Drupal\deploy\Entity\Form\ReplicationForm::addTitle'
   requirements:
-    _permission: 'administer deployments'
+    _entity_create_access: 'replication'
   options:
     _admin_route: TRUE
diff --git a/src/DeployPermissions.php b/src/DeployPermissions.php
new file mode 100644
index 0000000..f2dce69
--- /dev/null
+++ b/src/DeployPermissions.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Drupal\deploy;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\workspace\Entity\WorkspacePointer;
+
+/**
+ * Defines permissions for Deploy module.
+ */
+class DeployPermissions {
+  use StringTranslationTrait;
+
+  /**
+   * Generates the permissions for workspace pointer entities.
+   *
+   * @return array
+   *   The permissions for workspace pointer entities.
+   */
+  public function workspacePointerPermissions() {
+    $permissions = [];
+    $workspace_pointers = WorkspacePointer::loadMultiple();
+    foreach ($workspace_pointers as $workspace_pointer) {
+      $permissions['Deploy to ' . $workspace_pointer->label()] = [
+        'title' => $this->t('Deploy to @workspace_pointer', ['@workspace_pointer' => $workspace_pointer->label()]),
+      ];
+    }
+    return $permissions;
+  }
+
+}
diff --git a/src/ReplicationAccessControlHandler.php b/src/ReplicationAccessControlHandler.php
index 33209b8..1a00894 100644
--- a/src/ReplicationAccessControlHandler.php
+++ b/src/ReplicationAccessControlHandler.php
@@ -4,20 +4,53 @@ namespace Drupal\deploy;
 
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\multiversion\Workspace\WorkspaceManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * ReplicationAccessControlHandler class.
  */
-class ReplicationAccessControlHandler extends EntityAccessControlHandler {
+class ReplicationAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
+
+  /**
+   * The workspace manager service.
+   *
+   * @var \Drupal\multiversion\Workspace\WorkspaceManagerInterface
+   */
+  protected $workspaceManager;
+
+  /**
+   * Constructs a NodeAccessControlHandler object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type definition.
+   * @param \Drupal\multiversion\Workspace\WorkspaceManagerInterface $workspace_manager
+   *   The workspace manager service.
+   */
+  public function __construct(EntityTypeInterface $entity_type, WorkspaceManagerInterface $workspace_manager) {
+    parent::__construct($entity_type);
+    $this->workspaceManager = $workspace_manager;
+  }
 
   /**
    * {@inheritdoc}
    */
-  protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('workspace.manager')
+    );
+  }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
     $restricted_fields = ['source', 'target'];
     if (in_array($field_definition->getName(), $restricted_fields)) {
       return AccessResult::forbidden();
@@ -26,4 +59,20 @@ class ReplicationAccessControlHandler extends EntityAccessControlHandler {
     return parent::checkFieldAccess($operation, $field_definition, $account, $items);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    $access = parent::checkCreateAccess($account, $context, $entity_bundle);
+    $active_workspace = $this->workspaceManager->getActiveWorkspace();
+    $upstream_workspace_pointer = $active_workspace->upstream->entity;
+
+    // When no upstream workspace pointer is set or when the user doesn't have
+    // the permissions to deploy to the upstream, the access is forbidden.
+    if (!$upstream_workspace_pointer || !$account->hasPermission('Deploy to ' . $upstream_workspace_pointer->label())) {
+      $access = AccessResult::forbidden();
+    }
+    return $access;
+  }
+
 }
diff --git a/src/Tests/ReplicationEntityTest.php b/src/Tests/ReplicationEntityTest.php
index 494b363..2757355 100644
--- a/src/Tests/ReplicationEntityTest.php
+++ b/src/Tests/ReplicationEntityTest.php
@@ -22,6 +22,7 @@ class ReplicationEntityTest extends WebTestBase {
     $this->webUser = $this->drupalCreateUser([
       'administer workspaces',
       'administer deployments',
+      'Deploy to Live',
     ]);
 
     $this->drupalLogin($this->webUser);
diff --git a/tests/src/Functional/DeploymentAccessTest.php b/tests/src/Functional/DeploymentAccessTest.php
new file mode 100644
index 0000000..5ca9193
--- /dev/null
+++ b/tests/src/Functional/DeploymentAccessTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\Tests\deploy\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+use Drupal\user\Entity\Role;
+
+/**
+ * Tests access for creating deployments.
+ *
+ * @group workspace
+ */
+class DeploymentAccessTest extends BrowserTestBase {
+
+  public static $modules = [
+    'user',
+    'deploy',
+    'toolbar',
+  ];
+
+  /**
+   * Test deployment access.
+   */
+  public function testDeploymentAccess() {
+    $web_assert = $this->assertSession();
+
+    // Create and login a user with limited permissions.
+    $permissions = [
+      'access administration pages',
+      'administer workspaces',
+      'access toolbar',
+    ];
+    $test_user = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($test_user);
+
+    // Check the user can't access the deploy link or the deploy page for Live.
+    $this->drupalGet('<front>');
+    $web_assert->linkExists('Live');
+    $web_assert->linkNotExists('Deploy');
+    $this->drupalGet('/admin/structure/deployment/add');
+    $web_assert->statusCodeEquals('403');
+
+    // Switch to the stage workspace.
+    $this->drupalPostForm('/admin/structure/workspace/2/activate', [], 'Activate');
+
+    // Check ther use can't access the deploy link or the deploy page for Stage.
+    $this->drupalGet('<front>');
+    $web_assert->linkExists('Stage');
+    $web_assert->linkNotExists('Deploy');
+    $this->drupalGet('/admin/structure/deployment/add');
+    $web_assert->statusCodeEquals('403');
+
+    // Give the user access to deploy to live.
+    $test_user_roles = $test_user->getRoles();
+    $this->grantPermissions(Role::load(reset($test_user_roles)), ['Deploy to Live']);
+
+    // Check the use can access the deploy link or the deploy page for Stage.
+    $this->drupalGet('<front>');
+    $web_assert->linkExists('Stage');
+    $web_assert->linkExists('Deploy');
+    $this->drupalGet('/admin/structure/deployment/add');
+    $web_assert->statusCodeEquals('200');
+  }
+
+}
diff --git a/tests/src/Functional/WorkspaceConflictReportingTest.php b/tests/src/Functional/WorkspaceConflictReportingTest.php
index 4d06d9b..1c878be 100644
--- a/tests/src/Functional/WorkspaceConflictReportingTest.php
+++ b/tests/src/Functional/WorkspaceConflictReportingTest.php
@@ -61,6 +61,8 @@ class WorkspaceConflictReportingTest extends BrowserTestBase {
       'administer content types',
       'administer workspaces',
       'administer deployments',
+      'Deploy to Live',
+      'Deploy to Stage',
     ];
 
     $node_type = NodeType::create(['type' => 'test', 'label' => 'Test']);
