diff --git a/collaborative/.gitlab-ci.yml b/collaborative/.gitlab-ci.yml
index 98d9cfff5a63dd871bb2bd2c13a7a4bc1ac503d4..c0aa2e5a99b1abcb9c047dde433d656b533cb089 100644
--- a/collaborative/.gitlab-ci.yml
+++ b/collaborative/.gitlab-ci.yml
@@ -3,6 +3,8 @@ image: node:14.16.0
 stages:
   - build
   - test
+  - predeploy
+  - deploy
 
 cache: &global_cache # per-branch cache
   key: ${CI_COMMIT_REF_SLUG}
@@ -60,4 +62,86 @@ test:
   coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
   artifacts:
     paths:
-      - coverage/
+      - collaborative/coverage/
+
+# Filter out node_modules from build artifacts to speed up deploy.
+# Gitlab 13.10 will allow to use multiple cache instead.
+pre-deploy:
+  stage: predeploy
+  rules:
+    - if: "$CI_COMMIT_BRANCH"
+  # use only artifacts from build job
+  dependencies:
+    - build
+  # cache is not needed: don't download
+  cache: {}
+  # don't download repository content
+  variables:
+    GIT_STRATEGY: none
+  artifacts:
+    paths:
+      - collaborative/build/
+  # script is required
+  script:
+    - echo Exporting collaborative/build/…
+
+deploy_review:
+  stage: deploy
+  rules:
+    - if: "$CI_COMMIT_BRANCH"
+  tags:
+    - webserver
+  environment:
+    name: review/$CI_COMMIT_REF_NAME
+    url: https://demo.concordant.io/c-sudoku_${CI_ENVIRONMENT_SLUG}/
+    on_stop: stop_review
+  # use only artifacts from pre-deploy job
+  dependencies:
+    - pre-deploy
+  # cache and repository art not needed: don't download
+  cache: {}
+  variables:
+    GIT_STRATEGY: none
+  script:
+    - rm -rf /var/www/demo.concordant.io/html/c-sudoku_${CI_ENVIRONMENT_SLUG}
+    - mv collaborative/build /var/www/demo.concordant.io/html/c-sudoku_${CI_ENVIRONMENT_SLUG}
+
+stop_review:
+  stage: deploy
+  rules:
+    - if: "$CI_COMMIT_BRANCH"
+      when: manual
+      allow_failure: true
+  tags:
+    - webserver
+  environment:
+    name: review/$CI_COMMIT_REF_NAME
+    action: stop
+  # artifacts, cache and repository are not needed: don't download
+  dependencies: []
+  cache: {}
+  variables:
+    GIT_STRATEGY: none
+  script:
+    - rm -rf /var/www/demo.concordant.io/html/c-sudoku_${CI_ENVIRONMENT_SLUG}
+
+deploy_live:
+  stage: deploy
+  rules:
+    - if: '$CI_COMMIT_BRANCH == "master"'
+      when: manual
+  tags:
+    - webserver
+  environment:
+    name: live
+    url: https://demo.concordant.io/c-sudoku/
+  # use only artifacts from pre-deploy job
+  dependencies:
+    - pre-deploy
+  # cache and repository art not needed: don't download
+  cache: {}
+  variables:
+    GIT_STRATEGY: none
+  script:
+    - rm -rf /var/www/demo.concordant.io/html/c-sudoku
+    - mv collaborative/build /var/www/demo.concordant.io/html/c-sudoku