diff --git a/.eslintignore b/.eslintignore
index 8005e839c55c53e1279faefe23189146388416f6..cf8c8d1e9384b56f3f20cc5e5b3899cfc2b406cf 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,4 @@
 **/_build
 **/node_modules
 **/coverage
+packages/ui/styleguide
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 799c8019a2829e7d55f3e1bf1aed0574fa37366c..1b84ca66864246e56e6d1f1399a9cf893427069b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,25 +1,128 @@
-image: pubsweet/pubsweet-test-base
+variables:
+  IMAGE_ORG: pubsweet
+  IMAGE_NAME: pubsweet
+  BASE_DOMAIN: gateway.ps.semioticsquares.com
+  CONFIGURATION_REPOSITORY: https://gitlab.coko.foundation/pubsweet/deployment-config.git
 
-install:
+stages:
+  - build
+  - test
+  - review
+  - staging
+  - production
+  - demo
+
+build:
+  image: docker:latest
   stage: build
-  script: yarn
-  artifacts:
-    expire_in: 1h
-    paths:
-      - node_modules
-      - packages/*/node_modules
-      - packages/components/packages/*/node_modules
+  script:
+    - docker version
+    - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
+    - echo "Ignore warning! Cannot perform an interactive login from a non TTY device"
+    - docker build -t $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA .
+    - docker push $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
 
 lint:style:
+  image: $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
+  stage: test
+  variables:
+    GIT_STRATEGY: none
   script:
+    - cd ${HOME}
     - npm run lint:style
 
 lint:js:
+  image: $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
+  stage: test
+  variables:
+    GIT_STRATEGY: none
   script:
+    - cd ${HOME}
     - npm run lint:js
 
 test:
+  image: $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
+  stage: test
+  variables:
+    GIT_STRATEGY: none
+  script:
+    - cd ${HOME}
+    - NODE_ENV=test npm run test
+
+# -----------------------------------------------
+# ui --------------------------------------------
+# -----------------------------------------------
+
+review:ui:
+  image: xpub/deployer:latest
+  stage: review
+  variables:
+    PACKAGE_NAME: ui
+  environment:
+    name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME
+    # !! kube-lego will fail if domain > 63 chars
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+    on_stop: stop_review:ui
+  except:
+  - master
   script:
-    - npm run test
+    - source deploy.sh
+    - create_deployment
 
+stop_review:ui:
+  image: xpub/deployer:latest
+  stage: review
+  variables:
+    PACKAGE_NAME: ui 
+    GIT_STRATEGY: none
+  environment:
+    name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME
+    action: stop
+  when: manual
+  except:
+  - master
+  script:
+    - source deploy.sh
+    - delete_deployment_no_db
 
+staging:ui:
+  image: xpub/deployer:latest
+  stage: staging
+  variables:
+    PACKAGE_NAME: ui
+  environment:
+    name: $PACKAGE_NAME/staging
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+  only:
+  - master
+  script:
+    - source deploy.sh
+    - create_deployment
+
+production:ui:
+  image: xpub/deployer:latest
+  stage: production
+  variables:
+    PACKAGE_NAME: ui
+  environment:
+    name: $PACKAGE_NAME/production
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+  when: manual
+  only:
+  - master
+  script:
+    - source deploy.sh
+    - create_deployment
+
+demo:ui:
+  image: xpub/deployer:latest
+  stage: demo
+  variables:
+    PACKAGE_NAME: ui
+  environment:
+    name: $PACKAGE_NAME/demo
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+  when: manual
+  script:
+    - source deploy.sh
+    - create_deployment
diff --git a/.stylelintignore b/.stylelintignore
index 8005e839c55c53e1279faefe23189146388416f6..cf8c8d1e9384b56f3f20cc5e5b3899cfc2b406cf 100644
--- a/.stylelintignore
+++ b/.stylelintignore
@@ -1,3 +1,4 @@
 **/_build
 **/node_modules
 **/coverage
+packages/ui/styleguide
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..5c15f57cb67585f81fe67eacb4c78936b7f2f5c0
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,26 @@
+FROM pubsweet/pubsweet:base
+
+COPY package.json yarn.lock ./
+COPY lerna.json .eslintignore .eslintrc .prettierrc .stylelintignore .stylelintrc ./
+COPY packages packages
+
+RUN [ "yarn", "config", "set", "workspaces-experimental", "true" ]
+
+# We do a development install because react-styleguidist is a dev dependency
+RUN [ "yarn", "install", "--frozen-lockfile" ]
+
+# Remove cache and offline mirror
+RUN [ "yarn", "cache", "clean"]
+RUN [ "rm", "-rf", "/npm-packages-offline-cache"]
+
+ENV NODE_ENV "production"
+
+WORKDIR ${HOME}/packages/ui
+RUN [ "npm", "run", "styleguide:build" ]
+# Create file for kubernetes health checks
+RUN touch ./styleguide/health
+
+EXPOSE 3000
+
+WORKDIR ${HOME}
+CMD []