diff --git a/Dockerfile b/Dockerfile
index d5870bff..69e12ecb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM python:3.6
+ARG FROM=python:3.6
+FROM ${FROM}
 
 VOLUME /etc/promenade
 VOLUME /target
diff --git a/Makefile b/Makefile
index 8c721e11..d4b62f6e 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,22 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-BUILD_DIR := $(shell mktemp -d)
+BUILD_DIR         := $(shell mktemp -d)
+DOCKER_REGISTRY   ?= quay.io
+IMAGE_PREFIX      ?= airshipit
+IMAGE_NAME        ?= promenade
+IMAGE_TAG         ?= latest
+HELM              ?= helm
+PROXY             ?= http://proxy.foo.com:8000
+NO_PROXY          ?= localhost,127.0.0.1,.svc.cluster.local
+USE_PROXY         ?= false
+PUSH_IMAGE        ?= false
+LABEL             ?= commit-id
+PYTHON            = python3
+CHARTS            := $(patsubst charts/%/.,%,$(wildcard charts/*/.))
+IMAGE             := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${IMAGE_NAME}:${IMAGE_TAG}
+PYTHON_BASE_IMAGE ?= python:3.6
+
 HELM := $(BUILD_DIR)/helm
 HELM ?= helm
 HELM_PIDFILE ?= $(abspath ./.helm-pid)
@@ -76,6 +91,16 @@ helm-lint-%: helm-install helm-init-%
 	@echo Linting chart $*
 	cd charts;$(HELM) lint $*
 
+.PHONY: images
+images: check-docker build_promenade
+
+.PHONY: check-docker
+check-docker:
+	@if [ -z $$(which docker) ]; then \
+		echo "Missing \`docker\` client which is required for development"; \
+		exit 2; \
+	fi
+
 .PHONY: dry-run
 dry-run: $(addprefix dry-run-,$(CHARTS))
 
@@ -88,6 +113,26 @@ dry-run-%: helm-lint-%
 $(CHARTS): $(addprefix dry-run-,$(CHARTS)) chartbanner
 	$(HELM) package -d charts charts/$@
 
+.PHONY: build_promenade
+build_promenade:
+ifeq ($(USE_PROXY), true)
+	docker build --network host -t $(IMAGE) --label $(LABEL) -f ./Dockerfile \
+		--build-arg FROM=$(PYTHON_BASE_IMAGE) \
+		--build-arg http_proxy=$(PROXY) \
+		--build-arg https_proxy=$(PROXY) \
+		--build-arg HTTP_PROXY=$(PROXY) \
+		--build-arg HTTPS_PROXY=$(PROXY) \
+		--build-arg no_proxy=$(NO_PROXY) \
+		--build-arg NO_PROXY=$(NO_PROXY) .
+else
+	docker build --network host -t $(IMAGE) --label $(LABEL) -f ./Dockerfile \
+		--build-arg FROM=$(PYTHON_BASE_IMAGE) .
+endif
+ifeq ($(PUSH_IMAGE), true)
+	docker push $(IMAGE)
+endif
+
+
 .PHONY: helm-serve
 helm-serve: helm-install
 	./tools/helm_tk.sh $(HELM) $(HELM_PIDFILE)
diff --git a/tools/zuul/playbooks/docker-image-build.yaml b/tools/zuul/playbooks/docker-image-build.yaml
index fba69d52..86592394 100644
--- a/tools/zuul/playbooks/docker-image-build.yaml
+++ b/tools/zuul/playbooks/docker-image-build.yaml
@@ -1,16 +1,49 @@
 - hosts: all
   tasks:
+    - include_vars: vars.yaml
+
     - name: Install Docker (Debian)
+      when: ansible_os_family == 'Debian'
       block:
+        - file:
+            path: "{{ item }}"
+            state: directory
+          with_items:
+            - /etc/docker/
+            - /etc/systemd/system/docker.service.d/
+            - /var/lib/docker/
+        - mount:
+            path: /var/lib/docker/
+            src: tmpfs
+            fstype: tmpfs
+            opts: size=25g
+            state: mounted
+        - copy: "{{ item }}"
+          with_items:
+            - content: "{{ docker_daemon | to_json }}"
+              dest: /etc/docker/daemon.json
+            - src: files/docker-systemd.conf
+              dest: /etc/systemd/system/docker.service.d/
+        - apt_key:
+            url: https://download.docker.com/linux/ubuntu/gpg
+        - apt_repository:
+            repo: deb http://{{ zuul_site_mirror_fqdn }}/deb-docker xenial stable
         - apt:
             name: "{{ item }}"
+            allow_unauthenticated: True
           with_items:
-            - docker.io
+            - docker-ce
             - python-pip
-          when: ansible_os_family == 'Debian'
         - pip:
             name: docker
             version: 2.7.0
+        # NOTE(SamYaple): Allow all connections from containers to host so the
+        # containers can access the http server for git and wheels
+        - iptables:
+            action: insert
+            chain: INPUT
+            in_interface: docker0
+            jump: ACCEPT
       become: True
 
     - name: Debug tag generation inputs
diff --git a/tools/zuul/playbooks/files/docker-systemd.conf b/tools/zuul/playbooks/files/docker-systemd.conf
new file mode 100644
index 00000000..6b01af0f
--- /dev/null
+++ b/tools/zuul/playbooks/files/docker-systemd.conf
@@ -0,0 +1,8 @@
+# NOTE(SamYaple): CentOS cannot be build with userns-remap enabled. httpd uses
+# cap_set_file capability and there is no way to pass that in at build as of
+# docker 17.06.
+# TODO(SamYaple): Periodically check to see if this is possible in newer
+# versions of Docker
+[Service]
+ExecStart=
+ExecStart=/usr/bin/dockerd
diff --git a/tools/zuul/playbooks/vars.yaml b/tools/zuul/playbooks/vars.yaml
index eb6ffae1..c89b7980 100644
--- a/tools/zuul/playbooks/vars.yaml
+++ b/tools/zuul/playbooks/vars.yaml
@@ -12,4 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-null: null
+docker_daemon:
+  group: zuul
+  registry-mirrors:
+    - "http://{{ zuul_site_mirror_fqdn }}:8082/"
+  storage-driver: overlay2