diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0288a39de68039ca01b3488f1cac97c113c29255..b19b7fc2c5b6224bc0ad04fbaf4449f5545ce793 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -80,3 +80,48 @@ repos:
         args:
           - "--server"
           - https://git.shore.co.il
+
+  - repo: https://github.com/ambv/black.git
+    rev: 21.9b0
+    hooks:
+      - id: black
+        args:
+          - |
+              --line-length=79
+
+  - repo: https://github.com/PyCQA/prospector.git
+    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
diff --git a/images/docker/Dockerfile b/images/docker/Dockerfile
index 4d1e535a648e7dd5f3bdb717ed9c977669be19af..1d0795b3a109d8dc6b4a478082c89ef88db76d9c 100644
--- a/images/docker/Dockerfile
+++ b/images/docker/Dockerfile
@@ -1,3 +1,8 @@
 FROM docker:20.10
 # hadolint ignore=DL3018
-RUN apk add --update-cache --no-cache docker-compose
+RUN apk add --update-cache --no-cache \
+        docker-compose \
+        docker-py \
+        py3-dotenv \
+    && \
+    rm -rf /root/.cache /tmp/* /var/tmp/*
diff --git a/images/docker/compose-health-check b/images/docker/compose-health-check
new file mode 100755
index 0000000000000000000000000000000000000000..f849664791e5c5adc5981834274de8fd2e660ef2
--- /dev/null
+++ b/images/docker/compose-health-check
@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+"""Checks the health of a Docker Compose deployment."""
+
+
+import argparse
+import datetime
+import os
+import sys
+import time
+import docker  # pylint: disable=import-error
+import dotenv  # pylint: disable=import-error
+
+
+def get_project_containers_health(client, project):
+    """Get a Docker connection and the Docker Compose project name and return a
+    dictionary of container objects and their health status (if available, else
+    their status)."""
+    return {
+        i: i.attrs["State"]["Health"]["Status"]
+        if "Health" in i.attrs["State"]
+        else i.attrs["State"]["Status"]
+        for i in client.containers.list(
+            filters={"label": f"com.docker.compose.project={project}"}
+        )
+    }
+
+
+def check_project_health(project, timeout):
+    """Checks if a Docker Compose project is healthy.
+
+    If all of the containers are healthy or running (because there's no
+    healthcheck set), return True. If any of the containers is starting (health
+    check hasn't run yet or in the grace period), sleep 10 and try again
+    (ignoring the timeout). Otherwise, if haven't passed the timeout, sleep 10
+    and try again, if over the timeout, return if all the containers are
+    healthy or running.
+    """
+    starttime = datetime.datetime.now()
+    deadline = starttime + datetime.timedelta(seconds=timeout)
+    docker_client = docker.from_env()
+    docker_client.ping()
+    while True:
+        healths = get_project_containers_health(docker_client, project)
+        # pylint: disable=no-else-return
+        if all(map(lambda x: x in ["healthy", "running"], healths.values())):
+            return True
+        elif any(map(lambda x: x == "starting", healths.values())):
+            time.sleep(10)
+            continue
+        elif datetime.datetime.now() > deadline:
+            break
+        else:
+            time.sleep(10)
+
+    if all(map(lambda x: x in ["healthy", "running"], healths.values())):
+        return True
+    unhealthy = ", ".join(
+        [
+            f"{x.id}({x.name})"
+            for x in healths
+            if healths[x] not in ["healthy", "running"]
+        ]
+    )
+    print(f"""Containers {unhealthy} are not healthy.""")
+    return False
+
+
+def main():
+    """Main entrypoint."""
+
+    epilog = (
+        "The Docker Compose project name is resolved in the following order:"  # noqa: E501
+        "\n1. From the command line parameter."
+        "\n2. From the COMPOSE_PROJECT_NAME variable in the .env file."
+        "\n3. From the COMPOSE_PROJECT_NAME environment variable."
+    )
+    arg_parser = argparse.ArgumentParser(
+        description=__doc__,
+        epilog=epilog,
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+    )
+    arg_parser.add_argument(
+        "project", help="Name of the Compose Project.", nargs="?"
+    )
+    arg_parser.add_argument(
+        "-t",
+        "--timeout",
+        help="Number of seconds to wait for the deployment to be healthy, defaults to 300.",  # noqa: E501
+        type=int,
+        default=300,
+    )
+    args = arg_parser.parse_args()
+    if args.project:
+        project = args.project
+    else:
+        env = dotenv.dotenv_values(dotenv_path=".env")
+        if "COMPOSE_PROJECT_NAME" in env:
+            project = env["COMPOSE_PROJECT_NAME"]
+        elif "COMPOSE_PROJECT_NAME" in os.environ:
+            project = os.environ["COMPOSE_PROJECT_NAME"]
+        else:
+            arg_parser.error(
+                "Compose project wasn't specified, the COMPOSE_PROJECT_NAME variable is missing from the environment and from the .env file."  # noqa: E501
+            )
+
+    if check_project_health(project, args.timeout):
+        return 0
+    return 1
+
+
+if __name__ == "__main__":
+    sys.exit(main())