diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 659edee4889ef30d9d97c654e3557405e5aafe2b..c5be52e76b3d489662634b1c8877dc5e518dac39 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,11 @@
 default:
   image: python:3.12-slim
 
+.install_dependencies:
+  before_script:
+    - pip install --upgrade pip
+    - pip install setuptools wheel build twine
+
 stages:
   - build
   - test
@@ -8,16 +13,11 @@ stages:
 
 variables:
   PACKAGE_NAME: "pak"
-  DOCKER_IMAGE: "$CI_REGISTRY_IMAGE/$PACKAGE_NAME"
-  DOCKER_TLS_CERTDIR: "/certs"
-  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
-  PIP_INDEX_URL: "https://__token__:${CI_JOB_TOKEN}@gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/pypi/simple"
-  PIP_EXTRA_INDEX_URL: "https://pypi.org/simple"
 
 build_package:
   stage: build
+  extends: .install_dependencies
   script:
-    - pip install setuptools wheel build twine
     - python -m build
   artifacts:
     paths:
@@ -32,15 +32,32 @@ test_package:
 
 publish_pip:
   stage: publish
+  extends: .install_dependencies
+  variables:
+    PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+    PIP_INDEX_URL: "https://__token__:${CI_JOB_TOKEN}@gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/pypi/simple"
+    PIP_EXTRA_INDEX_URL: "https://pypi.org/simple"
   script:
     - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python3 -m twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
 
 build_and_push_docker_image:
   stage: publish
-  image: docker:26.0.1-dind
-  script:
+  image: docker:26.0.1
+  services:
+    - docker:26.0.1-dind
+  variables:
+    DOCKER_IMAGE_NAME: $CI_REGISTRY_IMAGE/$PACKAGE_NAME:$CI_COMMIT_REF_SLUG
+    DOCKER_TLS_CERTDIR: "/certs"
+  before_script:
     - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
-    - docker build -t $DOCKER_IMAGE:$CI_COMMIT_SHORT_SHA .
-    - docker push $DOCKER_IMAGE:$CI_COMMIT_SHORT_SHA
+  script:
+    - docker build --pull -t "$DOCKER_IMAGE_NAME" .
+    - docker push "$DOCKER_IMAGE_NAME"
+    - |
+      if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
+        docker tag "$DOCKER_IMAGE_NAME" "$CI_REGISTRY_IMAGE:latest"
+        docker push "$CI_REGISTRY_IMAGE:latest"
+      fi
+
   only:
     - main  # Only run on the main branch