diff --git a/.gitlab/ci_scripts/create_vps.sh b/.gitlab/ci_scripts/create_vps.sh
index 0e8972148008b1bca0ccb1660189323714320935..5de2a709bf66ca688183c6d0b9dc4260311cd26c 100644
--- a/.gitlab/ci_scripts/create_vps.sh
+++ b/.gitlab/ci_scripts/create_vps.sh
@@ -11,11 +11,14 @@ echo "Creating new machine"
 python3 -m openappstack $HOSTNAME create \
   --acme-staging \
   --local-flux \
-  --prometheus-enable-ingress \
   --create-droplet $DOMAIN \
   --create-hostname $HOSTNAME \
   --ssh-key-id $SSH_KEY_ID \
   --create-domain-records \
   --subdomain $SUBDOMAIN \
   --disk-image-id '-7473' \
-  --truncate-subdomain
+  --truncate-subdomain \
+  --docker-mirror-server="${CI_DEPENDENCY_PROXY_SERVER}" \
+  --docker-mirror-endpoint="${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}" \
+  --docker-mirror-username="${CI_RUNNER_PERSONAL_ACCESS_USERNAME}" \
+  --docker-mirror-password="${CI_RUNNER_PERSONAL_ACCESS_TOKEN}"
diff --git a/ansible/group_vars/all/oas.yml b/ansible/group_vars/all/oas.yml
index 758f08f77772a275f9839d86f9c959135e8fad3c..cfd1e39d129df6367aefb68e1156e3dde4be0133 100644
--- a/ansible/group_vars/all/oas.yml
+++ b/ansible/group_vars/all/oas.yml
@@ -53,7 +53,7 @@ flux:
   version: 0.14.2
 
 k3s:
-  version: 'v1.20.4+k3s1'
+  version: 'v1.21.2+k3s1'
   # args to start the k3s server with
   # https://rancher.com/docs/k3s/latest/en/installation/install-options/server-config/
   # kubelet arguments can be passed with `--kubelet-arg`
diff --git a/ansible/group_vars/all/settings.yml.example b/ansible/group_vars/all/settings.yml.example
index c99ace1707a940e87fe8526f38502413ef4122b1..c3995919cbd0a80a0ff524209747212472cbf00d 100644
--- a/ansible/group_vars/all/settings.yml.example
+++ b/ansible/group_vars/all/settings.yml.example
@@ -11,6 +11,13 @@ admin_email: "admin@example.com"
 # Important: Don't quote this variable !
 acme_staging: false
 
+# It is possible to add a docker mirror that serves images from docker.io.
+docker_mirror:
+  enabled: false
+  # endpoint: URL of docker mirror
+  # username: username for auth
+  # password: password for auth
+
 outgoing_mail:
   enabled: false
   fromAddress: ""
diff --git a/ansible/roles/pre-configure/tasks/main.yml b/ansible/roles/pre-configure/tasks/main.yml
index cb7b37cd41321f587f74cc2bcdb3824379296599..53a953ae8a4ba497c1d856d2c8174fc5efec12a6 100644
--- a/ansible/roles/pre-configure/tasks/main.yml
+++ b/ansible/roles/pre-configure/tasks/main.yml
@@ -103,3 +103,17 @@
 
 - name: Configure firewall
   import_tasks: firewall.yml
+
+- name: debug docker_mirror data
+  debug:
+    msg: Docker mirror is "{{ docker_mirror }}"
+
+- name: Write docker registries.yaml
+  tags:
+    - k3s
+    - docker
+  template:
+    src: registries.yaml
+    dest: /etc/rancher/k3s/registries.yaml
+    mode: '0740'
+  when: docker_mirror.enabled
diff --git a/ansible/roles/pre-configure/templates/registries.yaml b/ansible/roles/pre-configure/templates/registries.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c5bb1520b6a126e31f171832028daca6b9594893
--- /dev/null
+++ b/ansible/roles/pre-configure/templates/registries.yaml
@@ -0,0 +1,14 @@
+---
+mirrors:
+  docker.io:
+    endpoint:
+      - "{{ docker_mirror.server }}"
+    rewrite:
+      # We need to remove the `server` part from the `endpoint` part, so what we
+      # end up with is a path like ""/openappstack/dependency_proxy/containers/$1"
+      "^(.*)$": "{{ docker_mirror.endpoint | regex_replace(docker_mirror.server, '') }}/$1"
+configs:
+  "{{ docker_mirror.server }}":
+    auth:
+      username: "{{ docker_mirror.username }}"
+      password: "{{ docker_mirror.password }}"
diff --git a/openappstack/__main__.py b/openappstack/__main__.py
index e573294675c98108f60d5d9fb9899b9798a2d082..98ecdbd38261dc989c4f82b77301d7e418bbf7c1 100755
--- a/openappstack/__main__.py
+++ b/openappstack/__main__.py
@@ -77,10 +77,20 @@ def main():  # pylint: disable=too-many-statements,too-many-branches,too-many-lo
         type=str,
         help='hostname of the machine. If not provided for a new machine, the '
              'cluster name is used.')
+
     create_parser.add_argument(
-        '--prometheus-enable-ingress',
-        action='store_true',
-        help=("Use this if you want to access OpenAppStack's prometheus api from outside"))
+        '--docker-mirror-server',
+        help=("Server name for a docker mirror"))
+    create_parser.add_argument(
+        '--docker-mirror-endpoint',
+        help=("Full endpoint for a docker mirror"))
+    create_parser.add_argument(
+        '--docker-mirror-username',
+        help=("Username for `docker login` to docker mirror"))
+    create_parser.add_argument(
+        '--docker-mirror-password',
+        help=("Password for `docker login` to docker mirror"))
+
 
     group = create_parser.add_mutually_exclusive_group(required=True)
 
@@ -345,6 +355,15 @@ def create(clus, args):  # pylint: disable=too-many-branches
     elif args.droplet_hostname:
         clus.set_info_by_hostname(args.droplet_hostname)
 
+    if args.docker_mirror_server:
+        clus.docker_mirror_server = args.docker_mirror_server
+    if args.docker_mirror_endpoint:
+        clus.docker_mirror_endpoint = args.docker_mirror_endpoint
+    if args.docker_mirror_username:
+        clus.docker_mirror_username = args.docker_mirror_username
+    if args.docker_mirror_password:
+        clus.docker_mirror_password = args.docker_mirror_password
+
     # Write inventory.yml and settings.yml files
     clus.write_cluster_files()
 
diff --git a/openappstack/cluster.py b/openappstack/cluster.py
index 4106a1e1323062383b74331a75f5880b303a172e..3a15b99debf3abfc422fc65bb69c8163702c3ea1 100644
--- a/openappstack/cluster.py
+++ b/openappstack/cluster.py
@@ -63,6 +63,10 @@ class Cluster:
             self.load_data()
         # Can be used to use a custom disk image.
         self.disk_image_id = DEFAULT_IMAGE
+        self.docker_mirror_server = None
+        self.docker_mirror_endpoint = None
+        self.docker_mirror_username = None
+        self.docker_mirror_password = None
 
     def load_data(self):
         """
@@ -169,6 +173,15 @@ class Cluster:
         settings['domain'] = self.domain
         settings['admin_email'] = 'admin@{0}'.format(self.domain)
         settings['cluster_dir'] = self.cluster_dir
+        if self.docker_mirror_endpoint \
+                and self.docker_mirror_server \
+                and self.docker_mirror_username \
+                and self.docker_mirror_password:
+            settings['docker_mirror']['enabled'] = True
+            settings['docker_mirror']['endpoint'] = self.docker_mirror_endpoint
+            settings['docker_mirror']['username'] = self.docker_mirror_username
+            settings['docker_mirror']['password'] = self.docker_mirror_password
+            settings['docker_mirror']['server'] = self.docker_mirror_server
 
         # Configure apps to handle invalid certs i.e. from
         # Letsencrypt staging API