diff --git a/docker/watcher/watcher-api/Dockerfile.j2 b/docker/watcher/watcher-api/Dockerfile.j2
new file mode 100644
index 0000000000..fadf5ea035
--- /dev/null
+++ b/docker/watcher/watcher-api/Dockerfile.j2
@@ -0,0 +1,16 @@
+FROM {{ namespace }}/{{ image_prefix }}watcher-base:{{ tag }}
+MAINTAINER {{ maintainer }}
+
+{% if install_type == 'binary' %}
+
+RUN echo '{{ install_type }} not yet available for {{ base_distro }}' \
+    && /bin/false
+
+{% endif %}
+
+COPY extend_start.sh /usr/local/bin/kolla_watcher_extend_start
+RUN chmod 755 /usr/local/bin/kolla_watcher_extend_start
+
+{{ include_footer }}
+
+USER watcher
diff --git a/docker/watcher/watcher-api/extend_start.sh b/docker/watcher/watcher-api/extend_start.sh
new file mode 100644
index 0000000000..d1ea401bb3
--- /dev/null
+++ b/docker/watcher/watcher-api/extend_start.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+# Bootstrap and exit if KOLLA_BOOTSTRAP variable is set. This catches all cases
+# of the KOLLA_BOOTSTRAP variable being set, including empty.
+if [[ "${!KOLLA_BOOTSTRAP[@]}" ]]; then
+    watcher-db-manage --config-file /etc/watcher/watcher.conf create_schema
+    exit 0
+fi
diff --git a/docker/watcher/watcher-applier/Dockerfile.j2 b/docker/watcher/watcher-applier/Dockerfile.j2
new file mode 100644
index 0000000000..dec6cff26f
--- /dev/null
+++ b/docker/watcher/watcher-applier/Dockerfile.j2
@@ -0,0 +1,13 @@
+FROM {{ namespace }}/{{ image_prefix }}watcher-base:{{ tag }}
+MAINTAINER {{ maintainer }}
+
+{% if install_type == 'binary' %}
+
+RUN echo '{{ install_type }} not yet available for {{ base_distro }}' \
+    && /bin/false
+
+{% endif %}
+
+{{ include_footer }}
+
+USER watcher
diff --git a/docker/watcher/watcher-base/Dockerfile.j2 b/docker/watcher/watcher-base/Dockerfile.j2
new file mode 100644
index 0000000000..f6f86ddf9b
--- /dev/null
+++ b/docker/watcher/watcher-base/Dockerfile.j2
@@ -0,0 +1,27 @@
+FROM {{ namespace }}/{{ image_prefix }}openstack-base:{{ tag }}
+MAINTAINER {{ maintainer }}
+
+{% if install_type == 'binary' %}
+
+RUN echo '{{ install_type }} not yet available for {{ base_distro }}' \
+    && /bin/false
+
+{% elif install_type == 'source' %}
+
+ADD watcher-base-archive /watcher-base-source
+RUN ln -s watcher-base-source/* watcher \
+    && useradd --user-group watcher \
+    && /var/lib/kolla/venv/bin/pip --no-cache-dir install --upgrade -c requirements/upper-constraints.txt /watcher \
+    && mkdir -p /etc/watcher /home/watcher \
+    && cp -r /watcher/etc/watcher/* /etc/watcher/ \
+    && chown -R watcher: /etc/watcher /home/watcher
+
+{% endif %}
+
+RUN usermod -a -G kolla watcher
+
+COPY extend_start.sh /usr/local/bin/kolla_extend_start
+
+RUN usermod -a -G kolla watcher \
+    && touch /usr/local/bin/kolla_watcher_extend_start \
+    && chmod 755 /usr/local/bin/kolla_extend_start /usr/local/bin/kolla_watcher_extend_start
diff --git a/docker/watcher/watcher-base/extend_start.sh b/docker/watcher/watcher-base/extend_start.sh
new file mode 100644
index 0000000000..b1fcd1974f
--- /dev/null
+++ b/docker/watcher/watcher-base/extend_start.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+if [[ ! -d "/var/log/kolla/watcher" ]]; then
+    mkdir -p /var/log/kolla/watcher
+fi
+if [[ $(stat -c %a /var/log/kolla/watcher) != "755" ]]; then
+    chmod 755 /var/log/kolla/watcher
+fi
+
+source /usr/local/bin/kolla_watcher_extend_start
diff --git a/docker/watcher/watcher-engine/Dockerfile.j2 b/docker/watcher/watcher-engine/Dockerfile.j2
new file mode 100644
index 0000000000..dec6cff26f
--- /dev/null
+++ b/docker/watcher/watcher-engine/Dockerfile.j2
@@ -0,0 +1,13 @@
+FROM {{ namespace }}/{{ image_prefix }}watcher-base:{{ tag }}
+MAINTAINER {{ maintainer }}
+
+{% if install_type == 'binary' %}
+
+RUN echo '{{ install_type }} not yet available for {{ base_distro }}' \
+    && /bin/false
+
+{% endif %}
+
+{{ include_footer }}
+
+USER watcher
diff --git a/kolla/common/config.py b/kolla/common/config.py
index 83494c5d38..b243a953b8 100644
--- a/kolla/common/config.py
+++ b/kolla/common/config.py
@@ -270,6 +270,10 @@ SOURCES = {
         'type': 'url',
         'location': ('http://tarballs.openstack.org/trove/'
                      'trove-master.tar.gz')},
+    'watcher-base': {
+        'type': 'url',
+        'location': ('http://tarballs.openstack.org/watcher/'
+                     'watcher-master.tar.gz')},
     'zaqar': {
         'type': 'url',
         'location': ('http://tarballs.openstack.org/zaqar/'
diff --git a/releasenotes/notes/add-watcher-a97995ace827cf71.yaml b/releasenotes/notes/add-watcher-a97995ace827cf71.yaml
new file mode 100644
index 0000000000..4e081ddeb3
--- /dev/null
+++ b/releasenotes/notes/add-watcher-a97995ace827cf71.yaml
@@ -0,0 +1,6 @@
+---
+ features:
+   - Introduce OpenStack Infrastructure Optimization
+     service, also known as Watcher.  This project makes
+     use of Ceilometer data to rebalance the cloud to
+     meet declared goals and strategies.
diff --git a/tests/test_build.py b/tests/test_build.py
index 85f6b5c011..f8f8460341 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -68,7 +68,8 @@ class BuildTest(object):
 
 class BuildTestCentosBinary(BuildTest, base.BaseTestCase):
     excluded_images = ["kuryr",
-                       "senlin-base"]
+                       "senlin-base",
+                       "watcher-base"]
 
     def setUp(self):
         super(BuildTestCentosBinary, self).setUp()
@@ -106,7 +107,8 @@ class BuildTestUbuntuSource(BuildTest, base.BaseTestCase):
 
 class BuildTestOracleLinuxBinary(BuildTest, base.BaseTestCase):
     excluded_images = ["kuryr",
-                       "senlin-base"]
+                       "senlin-base",
+                       "watcher-base"]
 
     def setUp(self):
         super(BuildTestOracleLinuxBinary, self).setUp()