diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..97d77e55652dbf33cbbf09cb7c586478dcf5a24f
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,2 @@
+packages/*/node_modules/**/*
+packages/*/node_modules
diff --git a/.gitignore b/.gitignore
index 04256dbf47f0e00792afb6b6b678e348ed9d6a19..3817278106349f7af0064d689534d7a52d9704df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@ node_modules
 coverage/
 api/db/*
 logs/*
+log.txt
+config.json
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 558b20d51de0181e5f47d4a5a8b0d7e3129221d4..b3ed6720cfa77ac4329cd28099b51c1fe5bd13f6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,20 +1,199 @@
-image: node:8
+variables:
+  DOCKER_DRIVER: overlay
+  IMAGE_NAME: xpub
+  BASE_DOMAIN: gateway.xpub.semioticsquares.com
+  CONFIGURATION_REPOSITORY: https://gitlab.coko.foundation/pubsweet/infra.git
 
-install:
+stages:
+  - build
+  - test
+  - review
+  - staging
+  - production
+  - demo
+
+build:
+  image: docker:latest
+  services:
+    - docker:dind
   stage: build
-  script: yarn
-  artifacts:
-    expire_in: 1h
-    paths:
-      - node_modules
-      - 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 --pull -t $DOCKERHUB_USERNAME/$IMAGE_NAME .
+    - docker tag $DOCKERHUB_USERNAME/$IMAGE_NAME $DOCKERHUB_USERNAME/$IMAGE_NAME:$CI_COMMIT_SHA
+    - docker push $DOCKERHUB_USERNAME/$IMAGE_NAME:$CI_COMMIT_SHA
 
 lint:
- script: npm run lint
+  image: $DOCKERHUB_USERNAME/$IMAGE_NAME:$CI_COMMIT_SHA
+  stage: test
+  variables:
+    GIT_STRATEGY: none
+  script:
+    - cd ${HOME}
+    - npm run lint
 
 test:
-  script: npm run test
+  image: $DOCKERHUB_USERNAME/$IMAGE_NAME:$CI_COMMIT_SHA
+  stage: test
+  variables:
+    GIT_STRATEGY: none
+  script:
+    - cd ${HOME}
+    - npm run test
+
+# -----------------------------------------------
+# xpub-collabra ---------------------------------
+# -----------------------------------------------
+
+review:xpub-collabra:
+  image: xpub/deployer:latest
+  stage: review
+  variables:
+    PACKAGE_NAME: xpub-collabra
+    FORCE_FRESH_DB: "yes"
+  environment:
+    name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME
+    # !! kube-lego will fail if domain > 64 chars
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+    on_stop: stop_review:xpub-collabra
+  except:
+  - master
+  script:
+    - source deploy.sh
+    - create_deployment
+
+stop_review:xpub-collabra:
+  image: xpub/deployer:latest
+  stage: review
+  variables:
+    PACKAGE_NAME: xpub-collabra
+    GIT_STRATEGY: none
+  environment:
+    name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME
+    action: stop
+  when: manual
+  script:
+    - source deploy.sh
+    - delete_deployment
+
+
+staging:xpub-collabra:
+  image: xpub/deployer:latest
+  stage: staging
+  variables:
+    PACKAGE_NAME: xpub-collabra
+  environment:
+    name: $PACKAGE_NAME/staging
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+  only:
+  - master
+  script:
+    - source deploy.sh
+    - create_deployment
+
+production:xpub-collabra:
+  image: xpub/deployer:latest
+  stage: production
+  variables:
+    PACKAGE_NAME: xpub-collabra
+  environment:
+    name: $PACKAGE_NAME/production
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+  when: manual
+  only:
+  - master
+  script:
+    - source deploy.sh
+    - create_deployment
+
+demo:xpub-collabra:
+  image: xpub/deployer:latest
+  stage: demo
+  variables:
+    PACKAGE_NAME: xpub-collabra
+  environment:
+    name: $PACKAGE_NAME/demo
+    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+  when: manual
+  script:
+    - source deploy.sh
+    - create_deployment
+
+# -----------------------------------------------
+# xpub-ui ---------------------------------------
+# -----------------------------------------------
 
-# TODO: start?
-# TODO: build
-# TODO: deploy
+#review:xpub-ui:
+#  image: xpub/deployer:latest
+#  stage: review
+#  variables:
+#    PACKAGE_NAME: xpub-ui
+#  environment:
+#    name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME
+#    # !! kube-lego will fail if domain > 64 chars
+#    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+#    on_stop: stop_review:xpub-ui
+#  except:
+#  - master
+#  script:
+#    - source deploy.sh
+#    - create_deployment
+#
+#stop_review:xpub-ui:
+#  image: xpub/deployer:latest
+#  stage: review
+#  variables:
+#    PACKAGE_NAME: xpub-ui
+#    GIT_STRATEGY: none
+#  when: manual
+#  environment:
+#    name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME
+#    action: stop
+#  script:
+#    - source deploy.sh
+#    - delete_deployment
+#
+#staging:xpub-ui:
+#  image: xpub/deployer:latest
+#  stage: staging
+#  variables:
+#    PACKAGE_NAME: xpub-ui
+#  environment:
+#    name: $PACKAGE_NAME/staging
+#    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+#  only:
+#  - master
+#  script:
+#    - source deploy.sh
+#    - create_deployment
+#
+#production:xpub-ui:
+#  image: xpub/deployer:latest
+#  stage: production
+#  variables:
+#    PACKAGE_NAME: xpub-ui
+#  environment:
+#    name: $PACKAGE_NAME/production
+#    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+#  when: manual
+#  only:
+#  - master
+#  script:
+#    - source deploy.sh
+#    - create_deployment
+#
+#demo:xpub-ui:
+#  image: xpub/deployer:latest
+#  stage: demo
+#  variables:
+#    PACKAGE_NAME: xpub-ui
+#  environment:
+#    name: $PACKAGE_NAME/demo
+#    url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}"
+#  when: manual
+#  script:
+#    - source deploy.sh
+#    - create_deployment
+#
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..e883718f29978d4c8aa044524af78658017fc6b9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,48 @@
+FROM node:8.9-alpine
+MAINTAINER PubSweet Team <richard@coko.foundation>
+
+ENV HOME "/home/xpub"
+
+RUN mkdir -p ${HOME}
+
+# git required for yarn. Why isn't it included!?
+# the rest is for compiling leveldown
+RUN apk add --no-cache --virtual .gyp \
+        python \
+        curl \
+        make \
+        g++ \
+        git
+
+RUN git config --global user.email "test@testing.com" && \
+    git config --global user.name "Fakey McFakerson"
+
+WORKDIR ${HOME}
+
+COPY package.json yarn.lock 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" ]
+
+RUN [ "npm", "rebuild", "bcrypt", "--build-from-source=bcrypt"]
+
+ENV NODE_ENV "production"
+
+# We are temporarily going to use the same image with different commands to deploy different apps in the monorepo. This is bad :(.
+
+WORKDIR ${HOME}/packages/xpub-collabra
+# TODO pass in username and password as build arguments
+RUN [ "npx", "pubsweet", "build"]
+
+## No xpub-ui to deploy yet
+# WORKDIR ${HOME}/packages/xpub-ui
+# RUN [ "npm", "run", "styleguide:build" ]
+## Create file for kubernetes health checks
+# RUN touch ./styleguide/health
+
+EXPOSE 3000
+
+WORKDIR ${HOME}
+CMD []
diff --git a/README.md b/README.md
index cff5558e5e1e85d386ec6c9c7d1f06eab968ac28..089192dd0cb70dd2b4020f795c64a98d429d5363 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
 Note: xpub is still _very_ new. This repository contains an initial set of components but is not yet ready for use.
 
+
 ## Contents
 
 ### PubSweet components
@@ -11,11 +12,11 @@ Note: xpub is still _very_ new. This repository contains an initial set of compo
 * `component-review`: a PubSweet component that provides a Review page.
 * `component-submit`: a PubSweet component that provides a Submit page.
 
-## PubSweet applications
+### PubSweet applications
 
 * `xpub-collabra`: a PubSweet application that provides configuration and routing for a journal.
 
-## xpub packages
+### xpub packages
 
 * `xpub-connect`: a helper component for connecting pages to data
 * `xpub-edit`: WYSIWYG editors for use in xpub forms
@@ -50,8 +51,12 @@ To enable manuscript conversion via INK, add the following values to `packages/x
 ## Running the app
 
 1. `cd packages/xpub-collabra`
-1. The first time you run the app, initialise the database with `yarn run setupdb` (press Enter when asked for a collection title, to skip that step).
-1. `yarn start`
+2. The first time you run the app, initialise the database with `yarn run setupdb` (press Enter when asked for a collection title, to skip that step).
+3. `yarn start`
+
+## CI
+
+CI requires a Kubernetes cluster, resources for which can be found in `pubsweet/infra`. In order to set up a Kubernetes cluster (using AWS) you need to follow the instructions there.
 
 ## Community
 
diff --git a/packages/xpub-collabra/app.js b/packages/xpub-collabra/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..77977bfcb23e6ebab7285332d295852d2a9ec6d5
--- /dev/null
+++ b/packages/xpub-collabra/app.js
@@ -0,0 +1,7 @@
+const logger = require('@pubsweet/logger')
+const startServer = require('pubsweet-server')
+
+startServer().catch(err => {
+  logger.error('FATAL ERROR, SHUTTING DOWN:', err)
+  process.exit(1)
+})
diff --git a/packages/xpub-collabra/config/default.js b/packages/xpub-collabra/config/default.js
index 095442582cfe20cdf45e5ffe9c2c7f794317127c..2328924e95b182a0956ae08a30af7919e44b75f8 100644
--- a/packages/xpub-collabra/config/default.js
+++ b/packages/xpub-collabra/config/default.js
@@ -20,7 +20,7 @@ module.exports = {
       path.join(__dirname, '..', 'api', 'db', environment),
   },
   'pubsweet-client': {
-    API_ENDPOINT: 'http://localhost:3000/api',
+    API_ENDPOINT: '/api',
     'login-redirect': '/',
     'redux-log': false,
     theme: process.env.PUBSWEET_THEME,
@@ -40,6 +40,7 @@ module.exports = {
     email: process.env.INK_USERNAME,
     password: process.env.INK_PASSWORD,
     maxRetries: 500,
+    recipes: {},
   },
   publicKeys: ['pubsweet-client', 'authsome', 'validations'],
 }