diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 70cfcadb05eed8c528f9e0984bc1eb2f572eb1f7..9c4fa9ac89f2dcc7d44ee2e4113a9dc842449b5c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,6 +4,8 @@ include:
     file: templates/pre-commit.yml
   - project: shore/ci-templates
     file: templates/docker.yml
+  - project: shore/ci-templates
+    file: templates/notify.yml
 
 stages:
   - test
@@ -13,11 +15,41 @@ stages:
 build:
   extends: .compose-build
   tags: &tags [ns4.shore.co.il]
+  rules:
+    - if: $CI_PIPELINE_SOURCE != "schedule"
 
 pull:
   extends: .compose-pull
   tags: *tags
+  rules:
+    - if: $CI_PIPELINE_SOURCE != "schedule"
 
 run:
+  rules:
+    - if: $CI_PIPELINE_SOURCE != "schedule"
+      when: manual
   extends: .compose-run
   tags: *tags
+
+
+backup:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+  stage: deploy
+  tags: [host01.shore.co.il]
+  image: docker.io/library/docker:20.10
+  before_script:
+    - >-
+      docker build
+      --tag registry.shore.co.il/registry-backup
+      --pull
+      backup
+  script:
+    - >-
+      docker run
+      --volume /var/backups/registry:/var/backups/registry
+      --volume /run/docker.sock:/run/docker.sock
+      registry.shore.co.il/registry-backup
+      backup registry.shore.co.il /var/backups/registry
+  after_script:
+    - docker image prune -f
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1b8468929bf63482ead46185326d9a0a8ca927c4..5ae94b53dadf1ffd48f779f4e46da01d98c3ed56 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -54,3 +54,53 @@ repos:
     rev: v2.7.0
     hooks:
       - id: hadolint
+
+  - repo: https://github.com/ambv/black
+    rev: 21.9b0
+    hooks:
+      - id: black
+        args:
+          - |
+              --line-length=79
+
+  - repo: https://github.com/PyCQA/prospector
+    rev: 1.5.1
+    hooks:
+      - id: prospector
+        args:
+          - |-
+            --max-line-length=79
+          - |-
+            --with-tool=pyroma
+          - |-
+            --with-tool=bandit
+          - |-
+            --without-tool=pep257
+          - |-
+            --doc-warnings
+          - |-
+            --test-warnings
+          - |-
+            --full-pep8
+          - |-
+            --strictness=high
+          - |-
+            --no-autodetect
+        additional_dependencies:
+          - bandit
+          - pyroma
+
+  - repo: https://gitlab.com/pycqa/flake8.git
+    rev: 3.9.2
+    hooks:
+      - id: flake8
+        args:
+          - |-
+            --doctests
+        additional_dependencies:
+          - flake8-bugbear
+
+  - repo: https://github.com/codespell-project/codespell.git
+    rev: v2.1.0
+    hooks:
+      - id: codespell
diff --git a/backup/.dockerignore b/backup/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..ff99b532cd160e0d083867a08d8842b64bb138a8
--- /dev/null
+++ b/backup/.dockerignore
@@ -0,0 +1,2 @@
+*
+!backup
diff --git a/backup/Dockerfile b/backup/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..7be4b236985ce4948597f6650460153e8255b32e
--- /dev/null
+++ b/backup/Dockerfile
@@ -0,0 +1,4 @@
+FROM docker.io/library/alpine:3.14
+# hadolint ignore=DL3018
+RUN apk add --update --no-cache docker-py
+COPY --chown=root:root backup /usr/local/bin/backup
diff --git a/backup/backup b/backup/backup
new file mode 100755
index 0000000000000000000000000000000000000000..82505f41b527a231d1a51d90e58ad4dad5270351
--- /dev/null
+++ b/backup/backup
@@ -0,0 +1,2 @@
+#!/usr/bin/env python3
+"""Backup a container image registry."""