diff --git a/.zuul.yaml b/.zuul.yaml
index 248b6592..826ed85c 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -7,9 +7,63 @@
         - monasca-tempest-python-cassandra
         - monasca-tempest-java-cassandra
         - openstack-tox-lower-constraints
+        - docker-build-monasca-base
     gate:
       jobs:
         - monasca-tempest-python-influxdb
         - monasca-tempest-python-cassandra
         - monasca-tempest-java-cassandra
         - openstack-tox-lower-constraints
+    post:
+      jobs:
+        - docker-publish-image
+
+- job:
+    name: docker-build-monasca-base
+    timeout: 10200
+    post-timeout: 3600
+    run: playbooks/run.yml
+    required-projects:
+      - openstack/monasca-common
+    attempts: 1
+    irrelevant-files:
+      - ^.*\.rst$
+      - ^.*\.md$
+      - ^doc/.*$
+
+- job:
+    name: docker-publish-image
+    parent: docker-build-monasca-base
+    post-run: playbooks/publish.yml
+    required-projects:
+      - openstack/monasca-common
+    vars:
+        publisher: true
+    secrets:
+        - doker_hub_login
+
+- secret:
+    name: doker_hub_login
+    data:
+      user:  !encrypted/pkcs1-oaep
+        - j009sQOrpIAhTBhYHjmjcZNuZlb+mIAHT4rMzo0qAsruGhTAsL1AgHvkm/FLLtK9mT6w3
+          dAlHGC1Bba9n+H3INpkZbcNm0B2+PwIfjbB77lEcN/1D3XrXKje9S5IH3lWbBU+m4YIz0
+          +EsqkgciFXiFytW9dTT2oT58vu7TlD9N983UqoJ89Z5jxoSdbquR22er2oX0txb1XNIrC
+          M/7heQ/cigQtB9b04yEozhAUnZjFOPviCPv3R4aqbj5nmZ1w2UGc6HhZX0X0G4BRKlC+0
+          T6wYWbPgYMsdNlTnJqcji3IArbQeT89dY5Dcp5XbpYHd9U3h6wdSfcRihmCVF5bSdfzMW
+          E787kg0q81iFWPcCzbeQCsJexZ3osT7uoow/j4ddshbbQxM6aLRmK56prCXNTxFebSQJh
+          oqux2V7G+U9KCDya7dWe7Id3oMV0hxLIZI615evEtrzjjnsCVFRiOEn5AZdyfKe6uFxeD
+          fjTlM3y9gNpYV2wEcxm2+q8H9xCwlwA1IwZ+rTjCN1NJ53ded+Vs7CUpvkOlFEYMAUD4f
+          aHCyT9mPBDzVBw7TSqJTM6wn+LKkiV87b8QDFcYFZZT4aoIWNLU2dyS5Dk/qc0c3WUNxO
+          vQybCmnG2qIVh+Y4k/FNnPi0BvIyf9d78CEg5ZSTlG517zeqmohKybW3ynlYBM=
+      password: !encrypted/pkcs1-oaep
+        - Bvz5Lf62vqtUfneRd9IcZ+HcR3PG5r0xNk1aWRNVlJ+T4NSoLE9wQe3HLLkKIBG9pYIJN
+          60x+2EziPVYFrwMJloqOiuKHZmifjbYeNTK/FOlMHXxcQACYMc67ZDZO5VtIWtiCzM0aR
+          iN8f5n/kfVa0rPeHnKbabUd4XuTZv0PltT+GyYsLAcM+QqQcfWuYhx4ahiFMF+bpLMUEj
+          kJIFSKu7sk+6XHR6P5ppiUNl/yZzDztezkWS6HGGVaP0nV7nNaOd2hFIwl64iS4er1M6I
+          mc3XdOF1xJyU7IFrVsBlP8LYFS69wlSq3EuUoPYkR/7nk9cTKqmvXo/lThR7Yv9ZTbHxq
+          5pMAmURMQCVwZYDTDv6E1uQMhrGZliVjEsDmIpBG0Om61IpvEWIHm0WiS7OMG/e98IUOD
+          fhyjDMo9IzlsuilWH8xSwfh8f+UHaAwOF60bXEs9b2Zowwzvbi++AwtGbhoaidzaA1e6t
+          27YDErM/Ll8uawY+sLxO2JpE0AQM93lXVQmMg5npN0dlgIkCSohxCRlkiRrZ1DWx80ZiC
+          3nzYnRYQav/cRLp8mhVTLoLrBI5edu8uRw8kfMQWAbFpxvaCVwiii78izJsmJPmPelczS
+          YNZV98I1+pV7KW0PUS5EArkBCWDHLSZT4v4E8m4RfcXMlK7nWUQdN49sWDZg8A=
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 91d2987f..fa001452 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,3 +1,8 @@
+# TODO(Dobroslaw): Update to Alpine 3.8.
+# We can't update to Alpine 3.8 until pyca/cryptography is updated in
+# upper constrains file to be bigger than 2.2.2.
+# LibreSSL 2.7.x is not supported by older versions:
+# https://github.com/pyca/cryptography/pull/4270
 FROM python:3.5.5-alpine3.7
 
 COPY wait_for.sh kafka_wait_for_topics.py /
@@ -9,6 +14,14 @@ ENV \
     PIP_NO_COMPILE="no" \
     PYTHONIOENCODING="utf-8"
 
+ARG BASE_CREATION_TIME
+ARG BASE_GIT_COMMIT
+
+LABEL org.opencontainers.image.created="$BASE_CREATION_TIME"
+LABEL org.opencontainers.image.title="monasca-base"
+LABEL org.opencontainers.image.revision="$BASE_GIT_COMMIT"
+LABEL org.opencontainers.image.licenses="Apache-2.0"
+
 RUN \
     chmod +x /wait_for.sh /kafka_wait_for_topics.py && \
     apk add --no-cache \
@@ -16,6 +29,8 @@ RUN \
         tini=0.16.1-r0 \
         # We need this to allow users choose different time zone.
         tzdata=2017c-r0 && \
+    printf "Monasca base build date: %s\\n" $BASE_CREATION_TIME >> /VERSIONS && \
+    printf "Monasca base revision:   %s\\n" $BASE_GIT_COMMIT >> /VERSIONS && \
     # Cleaning.
     rm -rf /var/cache/apk/* && \
     rm -rf /var/log/* && \
@@ -65,14 +80,14 @@ ONBUILD RUN \
             # This many steps are needed to support gerrit patch sets.
             mkdir -p /app && \
             git -C /app init && \
-            git -C /app remote add origin $APP_REPO && \
-            git -C /app fetch origin $REPO_VERSION && \
+            git -C /app remote add origin "$APP_REPO" && \
+            git -C /app fetch origin "$REPO_VERSION" && \
             git -C /app reset --hard FETCH_HEAD && \
             wget --output-document /app/upper-constraints.txt \
-                $CONSTRAINTS_FILE?h=$CONSTRAINTS_BRANCH && \
+                "$CONSTRAINTS_FILE"?h="$CONSTRAINTS_BRANCH" && \
             # When creating image from master, stable branch or commit use
             # monasca-common from git repository.
-            [ ! $(git -C /app tag -l "${REPO_VERSION}") ] && \
+            [ ! "$(git -C /app tag -l ${REPO_VERSION})" ] && \
                 sed -i "s|monasca-common.*|-e git+$COMMON_REPO@$CONSTRAINTS_BRANCH#egg=monasca-common|" \
                     /app/upper-constraints.txt || true && \
             # Install packages needed by wait scripts and used for templating.
@@ -87,15 +102,15 @@ ONBUILD RUN \
                 --requirement /app/requirements.txt \
                 --constraint /app/upper-constraints.txt && \
             # Save info about build to `/VERSIONS` file.
-            printf "App:        %s\n" $DOCKER_IMAGE >> /VERSIONS && \
-            printf "Repository: %s\n" $APP_REPO >> /VERSIONS && \
-            printf "Version:    %s\n" $REPO_VERSION >> /VERSIONS && \
-            printf "Build date: %s\n" $CREATION_TIME >> /VERSIONS && \
-            printf "Revision:   %s\n" \
+            printf "App:        %s\\n" "$DOCKER_IMAGE" >> /VERSIONS && \
+            printf "Repository: %s\\n" "$APP_REPO" >> /VERSIONS && \
+            printf "Version:    %s\\n" "$REPO_VERSION" >> /VERSIONS && \
+            printf "Build date: %s\\n" "$CREATION_TIME" >> /VERSIONS && \
+            printf "Revision:   %s\\n" \
                 "$(git -C /app rev-parse FETCH_HEAD)" >> /VERSIONS && \
-            printf "Monasca-common version: %s\n" \
-                "$(pip3 freeze 2>1 | grep 'monasca-common')" >> /VERSIONS && \
-            printf "Constraints file: %s\n" \
+            printf "Monasca-common version: %s\\n" \
+                "$(pip3 freeze 2>&1 | grep 'monasca-common')" >> /VERSIONS && \
+            printf "Constraints file: %s\\n" \
                 "$CONSTRAINTS_FILE"?h="$CONSTRAINTS_BRANCH" >> /VERSIONS && \
             # Clean after instalation.
             apk del .build-deps && \
diff --git a/docker/build_base_image.sh b/docker/build_base_image.sh
new file mode 100755
index 00000000..4db80606
--- /dev/null
+++ b/docker/build_base_image.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -x  # Print each script step.
+set -eo pipefail  # Exit the script if any statement returns error.
+
+REAL_PATH=$(python -c "import os,sys;print(os.path.realpath('$0'))")
+cd "$(dirname "$REAL_PATH")/../docker/"
+
+BASE_GIT_COMMIT=$(git rev-parse --verify HEAD)
+[ -z "${BASE_GIT_COMMIT}" ] && echo "No git commit hash found" && exit 1
+
+BASE_CREATION_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
+# Docker tags don't like colons so use shorter version of ISO 8601 for them.
+BASE_CREATION_TIME_SHORT=$(date -d "$BASE_CREATION_TIME" -u +"%Y%m%dT%H%M%SZ")
+
+docker build --no-cache \
+    --build-arg BASE_CREATION_TIME="$BASE_CREATION_TIME" \
+    --build-arg BASE_GIT_COMMIT="$BASE_GIT_COMMIT" \
+    --tag monasca/base:master \
+    --tag monasca/base:"$BASE_CREATION_TIME_SHORT" .
diff --git a/docker/example/README.rst b/docker/example/README.rst
index 7c9cdec6..48e05808 100644
--- a/docker/example/README.rst
+++ b/docker/example/README.rst
@@ -4,7 +4,8 @@ Docker example image
 
 Example image to show how to build child containers from `monasca-base` image.
 
-
-| Variable                  | Default          | Description                                        |
-|-------------------------- |------------------|----------------------------------------------------|
-| `STAY_ALIVE_ON_FAILURE`   | `false`          | If true, container runs 2 hours after service fail |
++----------------------------+---------+----------------------------------------------------+
+| Variable                   | Default | Description                                        |
++============================+=========+====================================================+
+| `STAY_ALIVE_ON_FAILURE`    | `false` | If true, container runs 2 hours after service fail |
++----------------------------+---------+----------------------------------------------------+
diff --git a/docker/example/start.sh b/docker/example/start.sh
index 7cb3080b..2deef3e2 100644
--- a/docker/example/start.sh
+++ b/docker/example/start.sh
@@ -13,10 +13,10 @@
 #  under the License.
 
 # Starting script.
-# All checks you need to do before service could be safely started should
-# be added in this file.
+# All checks and configuration templating you need to do before service
+# could be safely started should be added in this file.
 
-set -e  # Exit the script if any statement returns a non-true return value.
+set -eo pipefail  # Exit the script if any statement returns error.
 
 # Test services we need before starting our service.
 echo "Start script: waiting for needed services"
diff --git a/playbooks/publish.yml b/playbooks/publish.yml
new file mode 100644
index 00000000..fc9507a6
--- /dev/null
+++ b/playbooks/publish.yml
@@ -0,0 +1,12 @@
+---
+- hosts: all
+  tasks:
+    - name: Login to Dockerhub
+      command: "docker login -u {{ doker_hub_login.user }} -p {{ doker_hub_login.password }}"
+      no_log: true
+
+    - name: List images
+      shell: "docker images  --format '{% raw %}{{ .Repository }}:{{ .Tag }}{% endraw %}' | grep monasca"
+
+    - name: Push to Docker Hub all tags
+      shell: "docker push monasca/base"
diff --git a/playbooks/run.yml b/playbooks/run.yml
new file mode 100644
index 00000000..6afc4ba5
--- /dev/null
+++ b/playbooks/run.yml
@@ -0,0 +1,19 @@
+---
+- hosts: all
+  tasks:
+    - name: Run node setup script
+      shell: "{{ zuul.project.src_dir }}/playbooks/setup_ci.sh"
+      become: true
+
+    - name: Changing permission of Docker socket to 666
+      file:
+        path: /run/docker.sock
+        mode: 666
+      become: true
+
+    - name: Build monasca-base Docker image
+      shell: "{{ zuul.project.src_dir }}/docker/build_base_image.sh"
+      become: true
+
+    - name: List images
+      shell: "docker images  --format '{% raw %}{{ .Repository }}:{{ .Tag }}{% endraw %}' | grep monasca"
diff --git a/playbooks/setup_ci.sh b/playbooks/setup_ci.sh
new file mode 100755
index 00000000..53754147
--- /dev/null
+++ b/playbooks/setup_ci.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -o xtrace
+set -o errexit
+
+# Make sure no old images exists.
+sudo rm -rf /var/lib/docker
+
+sudo apt-get update
+sudo apt-get -y install apt-transport-https
+curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
+sudo add-apt-repository \
+   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
+   $(lsb_release -cs) \
+   stable"
+sudo apt-get update
+sudo apt-get -y install --no-install-recommends docker-ce
+
+sudo docker info
+
+echo "Completed $0."