diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f521b03cdc0b23b96084bafa5146350108a9a0a1..7e7dd356c18548bde1ef011060aea8f87765c708 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,13 +1,8 @@
-image: "debian"
+# maintained by q3k, built using docker/build-env
+image: "registry.k0.hswaw.net/q3k/card10-build-env:20190725-110707Z-4f3bf2a"
 
 build:
     stage: build
-    before_script:
-        - echo "deb http://deb.debian.org/debian stretch-backports main" >> /etc/apt/sources.list
-        - apt update -qq
-        - apt install -y -qq gcc-arm-none-eabi python3-pip git
-        - apt install -y -qq -t stretch-backports meson
-        - pip3 install crc16
     script:
         - ./bootstrap.sh
         - ninja -C build/
@@ -15,11 +10,8 @@ build:
 
 pages:
     stage: deploy
-    image: ubuntu:bionic
-    before_script:
-        - apt update -qq
-        - apt install -y -qq python3-pip git clang libclang-dev llvm
-        - pip3 install sphinx sphinx_rtd_theme clang
+    # maintaned by q3k, build using docker/deploy-env
+    image: "registry.k0.hswaw.net/q3k/card10-deploy-env:20190725-112709Z-4c5d8e8"
     script:
         - export LD_LIBRARY_PATH=$(llvm-config --libdir)
         - echo $LD_LIBRARY_PATH
diff --git a/docker/build-env/Dockerfile b/docker/build-env/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..4cab5b4ff4fd70ada034821f7cf6e21c878a3bd6
--- /dev/null
+++ b/docker/build-env/Dockerfile
@@ -0,0 +1,14 @@
+FROM debian:stretch-backports
+
+RUN set -e -x ;\
+    export DEBIAN_FRONTEND=noninteractive ;\
+    apt-get update -y ;\
+    apt-get install -y \
+        git \
+        gcc-arm-none-eabi \
+        python3-pip ;\
+    apt-get install -y -t stretch-backports \
+        meson ;\
+    pip3 install crc16 ;\
+    rm -rf /var/lib/apt/lists
+
diff --git a/docker/build-env/push.sh b/docker/build-env/push.sh
new file mode 100755
index 0000000000000000000000000000000000000000..0458b84db6f54d051cbf85e3cdeb16f7011b413e
--- /dev/null
+++ b/docker/build-env/push.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+# Build and push the build env Docker container to the Gitlab container registry.
+
+set -e
+
+function rev() {
+    ( cd $1; git describe --always --match "v[0-9].*" --dirty )
+}
+
+IMAGE=registry.k0.hswaw.net/q3k/card10-build-env
+TAG=$(TZ=UTC date +%Y%m%d-%H%M%SZ)-$(rev .)
+
+docker build -t $IMAGE:$TAG .
+docker push $IMAGE:$TAG
+
+echo "Pushed $IMAGE:$TAG"
diff --git a/docker/deploy-env/Dockerfile b/docker/deploy-env/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..28434fb52bd20f10ac7e1771d9b8de515a8036b1
--- /dev/null
+++ b/docker/deploy-env/Dockerfile
@@ -0,0 +1,17 @@
+FROM ubuntu:bionic
+
+RUN set -e -x ;\
+    export DEBIAN_FRONTEND=noninteractive ;\
+    apt-get update -y ;\
+    apt-get install -y \
+        clang \
+        git \
+        libclang-dev \
+        llvm \
+        python3-pip ;\
+    pip3 install \
+        clang \
+        sphinx \
+        sphinx_rtd_theme ;\
+    rm -rf /var/lib/apt/lists
+
diff --git a/docker/deploy-env/push.sh b/docker/deploy-env/push.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6efb60278ae312e6c12817bb40ba22f4d16f7272
--- /dev/null
+++ b/docker/deploy-env/push.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+# Build and push the deploy env Docker container to the Gitlab container registry.
+
+set -e
+
+function rev() {
+    ( cd $1; git describe --always --match "v[0-9].*" --dirty )
+}
+
+IMAGE=registry.k0.hswaw.net/q3k/card10-deploy-env
+TAG=$(TZ=UTC date +%Y%m%d-%H%M%SZ)-$(rev .)
+
+docker build -t $IMAGE:$TAG .
+docker push $IMAGE:$TAG
+
+echo "Pushed $IMAGE:$TAG"