diff --git a/docker/ci-runner/Dockerfile b/docker/ci-runner/Dockerfile
index d016805c4a8c2392b3a373d05a672225b59b2f15..aa795837c670f9e26b3c9dea4e7010007b91a1c8 100644
--- a/docker/ci-runner/Dockerfile
+++ b/docker/ci-runner/Dockerfile
@@ -1,6 +1,8 @@
+# This dockerfile is used throughout steps of the CI/CD pipeline
+# It contains dependencies to execute the template and run tests
 FROM python:3.13
 
 RUN python -m pip install --upgrade pip
-RUN python -m pip install --upgrade cibuildwheel==2.23.0 cookiecutter tox twine
+RUN python -m pip install --upgrade cibuildwheel>=2.23.0 cookiecutter tox twine
 RUN curl -sSL https://get.docker.com/ | sh
 
diff --git a/project.gitlab-ci.yml b/project.gitlab-ci.yml
index 663b7b530b0b476902c3ffb0e18674d7bc16da2c..740497a5bdb6a0b706f980cba8867aa4f45d7ee9 100644
--- a/project.gitlab-ci.yml
+++ b/project.gitlab-ci.yml
@@ -78,13 +78,13 @@ package_docs:
     paths:
       - my_awesome_app/docs/build/*
 
-#docker_build:
-#  needs:
-#    - pipeline: $PARENT_PIPELINE_ID
-#      job: build-template
-#    - package_files
-#  before_script:
-#    - cd my_awesome_app
+docker_build:
+  needs:
+    - pipeline: $PARENT_PIPELINE_ID
+      job: build-template
+    - package_files
+  before_script:
+    - cd my_awesome_app
 
 run_integration_tests:
   needs:
diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml
index a08ac42cee44750882e09355b11c49321f1f5673..d08a6214f35dec222333e4062815c637b6ff8b71 100644
--- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml
+++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml
@@ -2,8 +2,6 @@ default:
   image:
     name: $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG
     pull_policy: always
-  before_script:
-    - python --version # For debugging
   cache:
     paths:
       - .cache/pip
@@ -22,14 +20,13 @@ stages:
 variables:
   PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
 
-
+# Job templates from Gitlab that scan for security issues or leaked secrets
 include:
   - template: Security/SAST.gitlab-ci.yml
   - template: Security/Dependency-Scanning.gitlab-ci.yml
   - template: Security/Secret-Detection.gitlab-ci.yml
 
-
-# Prepare image to run ci on
+# Prepare image to run ci on, image will be used in most jobs
 trigger_prepare:
   stage: prepare
   trigger:
@@ -48,16 +45,6 @@ sast:
       pmd-apex, sobelow, spotbugs
   stage: test
 
-dependency_scanning:
-  # override default before_script, job won't have Python available
-  before_script:
-    - uname
-
-secret_detection:
-  # override default before_script, job won't have Python available
-  before_script:
-    - uname
-
 # Basic setup for all Python versions for which we don't have a base image
 .run_unit_test_version_base:
   before_script:
@@ -66,6 +53,7 @@ secret_detection:
     - python -m pip install --upgrade tox twine
 
 # Run all unit tests for Python versions except the base image
+# The base image is used for the highest Python version
 run_unit_tests:
   extends: .run_unit_test_version_base
   stage: test
@@ -90,6 +78,8 @@ run_unit_tests_coverage:
     paths:
       - htmlcov/*
 
+# Binary packages will be build for many architectures
+# using scikitbuild-core and cibuildwheel
 package_files:
   stage: package
   tags:
@@ -102,6 +92,7 @@ package_files:
     - source scripts/setup-docker-host.sh
     - tox -e build-ci-linux
 
+# Build the sphinx documentation
 package_docs:
   stage: package
   artifacts:
@@ -111,6 +102,22 @@ package_docs:
   script:
     - tox -e docs
 
+# Build a tiny docker image that contains the minimal dependencies to run this project
+docker_build:
+  stage: images
+  image: docker:latest
+  needs:
+    - package_files
+  tags:
+    - dind
+  before_script: []
+  script:
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+    - docker build -f docker/{{cookiecutter.project_slug}}/Dockerfile . --build-arg BUILD_ENV=copy --tag $CI_REGISTRY_IMAGE/{{cookiecutter.project_slug}}:$CI_COMMIT_REF_SLUG
+    # enable this push line once you have configured docker registry cleanup policy
+    # - docker push $CI_REGISTRY_IMAGE/{{cookiecutter.project_slug}}:$CI_COMMIT_REF_SLUG
+
+
 run_integration_tests:
   stage: integration
   allow_failure: true
@@ -187,6 +194,7 @@ publish_to_readthedocs:
     - echo "scp docs/* ???"
     - exit 1
 
+# automatically create a release on Gitlab for every tagged commit
 release_job:
   stage: publish
   image: registry.gitlab.com/gitlab-org/release-cli:latest
diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml
index c6570e2e9f17962260b75e7e3b80f5ffde48c422..c7c1b720cfc8d8d631b4c7c2640aebd0499b3a08 100644
--- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml
+++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml
@@ -1,4 +1,7 @@
-# https://pre-commit.com/
+# run several checks prior to creating a commit or pushing to a remote
+# These can be skipped (but you shouldn't generally as the pipeline will fail)
+# using `git push / commit --no-check`
+# for more information see: https://pre-commit.com/
 default_stages: [ pre-commit, pre-push ]
 default_language_version:
   python: python3
diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md
index 4e6e8c75c5bbc2a58e17e98bb27d8b7bf49143b0..f74c6e09970b07a4cbd88e8b588933e75269d617 100644
--- a/{{cookiecutter.project_slug}}/README.md
+++ b/{{cookiecutter.project_slug}}/README.md
@@ -38,5 +38,7 @@ To automatically apply most suggested linting changes execute:
 
 ```tox -e format```
 
+The configuration for linting and tox can be found in `pyproject.toml`
+
 ## License
 This project is licensed under the Apache License Version 2.0
diff --git a/{{cookiecutter.project_slug}}/bin/install-hooks/pre-commit.sh b/{{cookiecutter.project_slug}}/bin/install-hooks/pre-commit.sh
deleted file mode 100755
index 792a3aabef83dc4ebf0c94635ae1de0e7412c479..0000000000000000000000000000000000000000
--- a/{{cookiecutter.project_slug}}/bin/install-hooks/pre-commit.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-if [ ! -f "setup.sh" ]; then
-  echo "pre-commit.sh must be executed with repository root as working directory!"
-  exit 1
-fi
-
-pre-commit install --hook-type pre-push
diff --git a/{{cookiecutter.project_slug}}/build-requirements.txt b/{{cookiecutter.project_slug}}/build-requirements.txt
index d2d6bde8d4c933d8debe5d02272df5fe446e288e..502b5f30232469f7a5614b019b541d998546db1e 100644
--- a/{{cookiecutter.project_slug}}/build-requirements.txt
+++ b/{{cookiecutter.project_slug}}/build-requirements.txt
@@ -1,3 +1,3 @@
-cibuildwheel==2.23.0 # BSD
+cibuildwheel>=2.23.0 # BSD
 tox >= 4.0 # ?
 build >= 0.8.0 # MIT
diff --git a/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile
index d61c39998b79b4975715276f7a179ca1eb68b549..4494c2bc3b3819ffa2b4b4b0faabe62d7f2eaa11 100644
--- a/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile
+++ b/{{cookiecutter.project_slug}}/docker/ci-runner/Dockerfile
@@ -1,5 +1,7 @@
+# This dockerfile is used throughout steps of the CI/CD pipeline
+# It contains dependencies to run tests
 FROM python:3.13
 
 RUN python -m pip install --upgrade pip
-RUN python -m pip install --upgrade cibuildwheel==2.23.0 tox twine
+RUN python -m pip install --upgrade cibuildwheel>=2.23.0 tox twine
 RUN curl -sSL https://get.docker.com/ | sh
diff --git a/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile b/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile
index d64db4971e9fa1cbf067936c7152fc02d4c4fbff..9e9cb238f3302d1d16207d9300a32575d9e96bc8 100644
--- a/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile
+++ b/{{cookiecutter.project_slug}}/docker/{{cookiecutter.project_slug}}/Dockerfile
@@ -1,13 +1,18 @@
+# This dockerfile creates a minimal docker image that can run the project.
+# It can copy the result of `tox -e build` from the host to the container
+# or build from source inside the container.
+# Set `docker build --build-arg BUILD_ENV=copy` to copy from the host, rebuild otherwise
+# contrarily, to the ci image it does not contain any dependencies used for testing.
 ARG BUILD_ENV=no_copy
 
 # Build without copying from host inside docker
 FROM python:3.13 AS build_no_copy
 ADD ../../requirements.txt .
 COPY ../.. /work
-RUN rm -r /work/dist | true
+RUN rm -rf /work/dist
 RUN python -m pip install --user tox
 WORKDIR /work
-RUN python -m tox -e build
+RUN python -m tox -e build-local
 
 # Copy build package from host `dist` directory
 FROM python:3.13 AS build_copy
diff --git a/{{cookiecutter.project_slug}}/docs/cleanup.py b/{{cookiecutter.project_slug}}/docs/cleanup.py
index b2e219cbfe66e894109aefb3b8cf1cb34d6d40ea..c88889067f1dc736a881d565ee02460f8cd05148 100644
--- a/{{cookiecutter.project_slug}}/docs/cleanup.py
+++ b/{{cookiecutter.project_slug}}/docs/cleanup.py
@@ -5,6 +5,12 @@
 
 """
 Remove generated source documentation files except for index.rst
+
+If a source file is created, documentation is generated and the source file is later
+removed. The documentation for this source file will persists.
+
+This file ensures generated source documentation files, which are automatically
+generated from source, are removed between every build.
 """
 
 import os
diff --git a/{{cookiecutter.project_slug}}/setup.sh b/{{cookiecutter.project_slug}}/setup.sh
index 3eb742940523b3955505298d84cb499eb4ae602f..b1a3a35ac02753f663a029cd320f7936aca93b9b 100755
--- a/{{cookiecutter.project_slug}}/setup.sh
+++ b/{{cookiecutter.project_slug}}/setup.sh
@@ -4,14 +4,16 @@
 # SPDX-License-Identifier: Apache-2.0
 #
 
-# This file's directory is used to determine the station control directory
-# location. We substitute BASH_SOURCE if it doesn't get set, this particularly
-# happens when sourcing using the `docker` docker image due to its primitive
-# shell.
+# Substitute BASH_SOURCE if unset this is required for simple shells
+# such as the one found in docker or alpine docker images.
+# `#! /usr/bin/env bash` does not actually ensure the sourcing is executed
+# using BASH
 if [ -z ${BASH_SOURCE} ]; then
   BASH_SOURCE=${(%):-%x}
 fi
 
+# From BASH_SOURCE determine the absolute path, this is needed because $0 is
+# unset when sourcing in some shells.
 ABSOLUTE_PATH=$(realpath $(dirname ${BASH_SOURCE}))
 
 # Create a virtual environment directory if it doesn't exist
@@ -26,7 +28,7 @@ source "$VENV_DIR/bin/activate"
 python -m pip install pre-commit
 python -m pip install "tox>=4.21.0"
 
-# Install git pre-commit pre-push hook
-if [ ! -f "${ABSOLUTE_PATH}/.git/hooks/pre-push.legacy" ]; then
-  source "${ABSOLUTE_PATH}/bin/install-hooks/pre-commit.sh"
+# Install git pre-commit pre-push hook if not already installed
+if [ ! -f "${ABSOLUTE_PATH}/.git/hooks/pre-push" ]; then
+  pre-commit install --hook-type pre-push
 fi