From 611ae0624efb4182ecb0a28bc58d6248b2b483f8 Mon Sep 17 00:00:00 2001 From: Stefan Saenger Date: Thu, 6 Aug 2026 09:50:13 +0200 Subject: [PATCH 1/8] fix(deploy): switch dev to letsencrypt-prod and add HTTP->HTTPS redirect --- deploy/k8s/base/ingress.yaml | 1 + deploy/k8s/overlays/dev/patch-ingress.yaml | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/deploy/k8s/base/ingress.yaml b/deploy/k8s/base/ingress.yaml index 24dbb55e..c3c00482 100644 --- a/deploy/k8s/base/ingress.yaml +++ b/deploy/k8s/base/ingress.yaml @@ -15,6 +15,7 @@ metadata: name: vncmail-plus annotations: cert-manager.io/cluster-issuer: CHANGEME + traefik.ingress.kubernetes.io/router.middlewares: traefik-redirect-to-https@kubernetescrd spec: ingressClassName: traefik tls: diff --git a/deploy/k8s/overlays/dev/patch-ingress.yaml b/deploy/k8s/overlays/dev/patch-ingress.yaml index f9efd41a..ff268a5f 100644 --- a/deploy/k8s/overlays/dev/patch-ingress.yaml +++ b/deploy/k8s/overlays/dev/patch-ingress.yaml @@ -1,14 +1,12 @@ -# dev-k8s cluster confirmed to have a `letsencrypt-staging` ClusterIssuer -# already (no `letsencrypt-prod` exists there) - staging avoids burning -# Let's Encrypt's real rate limits while this is still being stood up. -# vncmail.sandbox.vnc.de DNS does not point here yet either - this is the -# intended host, not a live one (see VNCMAIL-SETUP.md for what's still open). +# Dev now uses letsencrypt-prod so the sandbox certificate is trusted by +# browsers. Both letsencrypt-staging and letsencrypt-prod ClusterIssuers +# exist on the dev-k8s cluster (see VNCiAC-dev-cluster-runbook.md §7). apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: vncmail-plus annotations: - cert-manager.io/cluster-issuer: letsencrypt-staging + cert-manager.io/cluster-issuer: letsencrypt-prod spec: tls: - hosts: From 26e1f31945a830fe449acf4ca3885848e63b1b82 Mon Sep 17 00:00:00 2001 From: Stefan Saenger Date: Thu, 6 Aug 2026 10:18:00 +0200 Subject: [PATCH 2/8] ci(deploy): migrate build to docker:28.4.0-dind + GitLab container registry --- .gitlab-ci.yml | 133 ++++-------------- deploy/k8s/README.md | 29 ++-- deploy/k8s/base/deployment.yaml | 22 ++- .../overlays/dev/image-tag/kustomization.yaml | 4 +- .../overlays/dev/patch-image-pull-policy.yaml | 13 -- .../prod/image-tag/kustomization.yaml | 2 +- 6 files changed, 56 insertions(+), 147 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05a84df6..64a97771 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,80 +1,30 @@ # GitLab-CI dev→prod pipeline for VNCmail+ — GitOps via ArgoCD. # -# Revised after direct inspection of the real infrastructure found ArgoCD -# already installed (idle, zero Applications) on the dev-k8s-1/2/3 cluster. -# That's more idiomatic than a runner-executes-kubectl design, and it means -# this pipeline needs ZERO cluster credentials — CI only ever talks to the -# container registry and to this git repo. ArgoCD (which already has -# whatever cluster access it needs, set up once when its Applications were -# registered — see deploy/argocd/) is what actually applies anything. -# # Design: -# - One image name, environment lives only in the tag. No more -dev/-beta -# name confusion from the old GitHub Actions workflow. # - MR into `dev`: verify only (typecheck/lint/unit test/build check). No # push, no deploy — this is the multi-developer merge gate. -# - Push to `dev`: build+push an immutable `sha-` tag, then commit a -# one-line tag-bump into overlays/dev/image-tag/kustomization.yaml -# (`[skip ci]`, so this doesn't retrigger itself). ArgoCD's `vncmail-dev` -# Application has automated sync — it notices the git change and applies -# it. No approval needed, dev always deploys, and this job never touches -# the cluster directly. -# - Push to `main`: NEVER rebuilds. `main` only ever advances via +# - Push to `dev`: build+push an immutable `sha-` tag with Docker + +# docker-in-docker, then commit a one-line tag-bump into +# overlays/dev/image-tag/kustomization.yaml (`[skip ci]`). ArgoCD's +# `vncmail-dev` Application syncs it automatically. +# - Push to `main`: NEVER rebuilds. `main` only advances via # `git merge --ff-only dev`, so main's HEAD commit already has a built -# image (the same sha- tag dev already deployed). This job just bumps -# overlays/prod/image-tag/kustomization.yaml to point at that same tag. -# The actual promotion gate is a HUMAN clicking Sync on the `vncmail-prod` ArgoCD -# Application (deliberately NOT automated sync) — not a GitLab manual -# job, since ArgoCD already provides that exact gate more directly. -# Until prod Stalwart/hostname/secrets are real (see VNCMAIL-SETUP.md), -# nobody should click that Sync button — but nothing here does it for -# you either way. +# image. This job just bumps overlays/prod/image-tag/kustomization.yaml +# to point at that same tag. The actual promotion gate is a HUMAN +# clicking Sync on the `vncmail-prod` ArgoCD Application. # # Deliberately single-platform (linux/amd64) — this pipeline serves two # known amd64 microk8s clusters, not public multi-arch distribution (that's # what the GHCR release workflows are for, untouched by this file). # -# Registry history (so nobody re-litigates this from scratch). GitLab's own -# Container Registry was tried twice and does not work on this server: -# -# Round 1: registry_external_url was unset, so $CI_REGISTRY was empty and -# docker login silently fell through to Docker Hub. -# Round 2: after the server-side config, the project's sidebar DID show -# "Container Registry" and docker login DID succeed - but the push -# failed 403. Diagnosed 2026-08-05 by curling the vhost directly: -# -# $ curl -i https://registry.gitlab.vnc.biz/v2/ -# www-authenticate: Bearer realm="http://gitlab.vnc.biz/jwt/auth", -# service="dependency_proxy" -# x-runtime: 0.020470 -# x-gitlab-meta: {"correlation_id":...} -# -# x-runtime/x-gitlab-meta are RAILS headers, and the service is -# "dependency_proxy" - so nginx routes that hostname to the GitLab -# Rails app, which reads /v2/ as the dependency proxy (a Docker Hub -# pull-through cache), NOT to the registry container. The registry -# service was never actually wired behind that vhost. That is why -# login worked (Rails issues an unscoped dependency-proxy token) -# while a scoped :push request 403'd - the dependency proxy has no -# push concept at all. -# -# Fixing that is an nginx/omnibus change on the GitLab server (registry -# service must actually listen behind registry.gitlab.vnc.biz), not something -# any .gitlab-ci.yml can reach. Until someone does that, GHCR it is - the -# same image the sandbox already pulls, and confirmed public so the cluster -# needs no imagePullSecrets (see deploy/k8s/base/deployment.yaml). -# -# Prerequisite this file assumes (documented in VNCMAIL-SETUP.md, not -# something this file can set up itself): -# - A GitHub PAT with `write:packages` for ghcr.io/brvncde-dotcom as -# $GITLAB_CI_GHCR_TOKEN, plus the matching GitHub username as -# $GITLAB_CI_GHCR_USER — both masked+protected CI/CD variables. A GitHub -# credential can only come from GitHub; nothing GitLab-native substitutes. -# - A GitLab Runner (any kind — no cluster access needed at all now). +# Prerequisite this file assumes: +# - A GitLab Runner with Docker-in-Docker service support (Kubernetes or +# Docker executor). The `docker:28.4.0-dind` service requires privileged +# mode on most Kubernetes executors. # - Either "allow this job token to push to this project" enabled # (Settings → CI/CD → Job token permissions), OR a project access token -# with `write_repository` scope in $GITLAB_PUSH_TOKEN. The job below -# tries CI_JOB_TOKEN first (see the script). +# with `write_repository` scope in $GITLAB_PUSH_TOKEN. The bump jobs +# try CI_JOB_TOKEN first (see the script). # # deploy/k8s/ca/ (the EJBCA internal CA) is never referenced anywhere below, # and neither ArgoCD Application in deploy/argocd/ points at it — that stays @@ -87,11 +37,10 @@ stages: - bump-prod variables: - # GHCR, not GitLab's own registry - see the "Registry history" note in the - # header for the curl output proving why. Same image the sandbox already - # pulls today. - IMAGE: ghcr.io/brvncde-dotcom/vncmail-plus-dev + IMAGE: $CI_REGISTRY_IMAGE GIT_STRATEGY: clone + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: "/certs" # --------------------------------------------------------------------------- # verify — required check on every MR into dev. No registry, no cluster. @@ -118,45 +67,21 @@ verify: # --------------------------------------------------------------------------- build: stage: build - # Kaniko builds OCI images without a Docker daemon, so it needs neither a - # dind service nor a privileged pod — GitLab's own recommended approach - # for the Kubernetes executor specifically. The docker:27-cli + dind - # combination that was here before this got as far as a successful - # $CI_REGISTRY login, then failed every way it was pointed - # (unix:///var/run/docker.sock, tcp://docker:2375, tcp://localhost:2375): - # the dind container itself was never actually listening, which on this - # executor means it needs `privileged: true` in the runner's own - # config.toml — a cluster/GitLab-admin setting outside this file's - # control. Kaniko sidesteps that requirement entirely rather than chasing - # runner permissions further, and is also the safer default on a shared - # cluster (no privileged containers at all). - image: - name: gcr.io/kaniko-project/executor:v1.23.2-debug - entrypoint: [""] + image: docker:28.4.0 + services: + - docker:28.4.0-dind rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "dev"' script: - # Fail loudly and immediately if the GHCR credentials aren't configured, - # rather than letting kaniko get all the way through a full Next.js build - # and only then 403 on push (which is exactly how the GitLab-registry - # attempt burned several pipeline runs). - - | - if [ -z "$GITLAB_CI_GHCR_TOKEN" ] || [ -z "$GITLAB_CI_GHCR_USER" ]; then - echo "ERROR: \$GITLAB_CI_GHCR_TOKEN and/or \$GITLAB_CI_GHCR_USER are not set." - echo "Add both under Settings -> CI/CD -> Variables (masked + protected)." - echo "The token is a GitHub PAT with the write:packages scope." - exit 1 - fi - - mkdir -p /kaniko/.docker - - | - echo "{\"auths\":{\"ghcr.io\":{\"auth\":\"$(printf '%s:%s' "$GITLAB_CI_GHCR_USER" "$GITLAB_CI_GHCR_TOKEN" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" - > - /kaniko/executor - --context "$CI_PROJECT_DIR" - --dockerfile "$CI_PROJECT_DIR/Dockerfile" + docker build --build-arg GIT_COMMIT=$CI_COMMIT_SHA - --destination "$IMAGE:sha-$CI_COMMIT_SHORT_SHA" - --destination "$IMAGE:dev-latest" + -t "$IMAGE:sha-$CI_COMMIT_SHORT_SHA" + -t "$IMAGE:dev-latest" + . + - docker push "$IMAGE:sha-$CI_COMMIT_SHORT_SHA" + - docker push "$IMAGE:dev-latest" # --------------------------------------------------------------------------- # bump-dev — no cluster access. Commits the just-built tag into the overlay @@ -176,7 +101,7 @@ bump-dev: apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component images: - - name: ghcr.io/brvncde-dotcom/vncmail-plus-dev + - name: vncmail-plus newName: $IMAGE newTag: $TAG EOF @@ -215,7 +140,7 @@ bump-prod: apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component images: - - name: ghcr.io/brvncde-dotcom/vncmail-plus-dev + - name: vncmail-plus newName: $IMAGE newTag: $TAG EOF diff --git a/deploy/k8s/README.md b/deploy/k8s/README.md index b4325fa1..a11f4b7e 100644 --- a/deploy/k8s/README.md +++ b/deploy/k8s/README.md @@ -59,10 +59,9 @@ is CI's job to decide — it's an explicit, human-triggered event. | 7 | Ingress `vncmail-plus` | `base/ingress.yaml` (+ overlay patches for prod) | TLS host | **Image:** CI builds and pushes to `registry.gitlab.vnc.biz/gitlab-instance-b9b5cf2f/vncmail-plus` -(tag `sha-` per deploy, moving pointers `dev-latest`/`prod-latest`). The -`ghcr.io/brvncde-dotcom/vncmail-plus-dev` image referenced in `base/deployment.yaml` -is a legacy default only — CI overrides it per-deploy via `kubectl set image`, -so what's committed there never needs to track what's actually running. +(tag `sha-` per deploy, moving pointer `dev-latest`). The generic `vncmail-plus` +image name in `base/deployment.yaml` is a placeholder — kustomize's image-tag +Component replaces it with the real registry path on every deploy. --- @@ -98,15 +97,15 @@ pick up the fix): ```bash cd deploy/k8s/overlays/dev # or overlays/prod, once real -# a) Image-pull secret — the registry package is private. -kubectl create secret docker-registry ghcr-pull \ +# a) Image-pull secret — the GitLab registry requires authentication. +# Use a project deploy token with `read_registry` scope, or the CI job +# token (short-lived — better for CI, not for long-running clusters). +kubectl create secret docker-registry gitlab-registry \ --namespace vncmail \ - --docker-server=ghcr.io \ - --docker-username=brvncde-dotcom \ - --docker-password='' \ - --docker-email=br@vnc.biz -# Once CI has cut over to registry.gitlab.vnc.biz, this becomes a -# docker-registry secret for that registry instead — see VNCMAIL-SETUP.md. + --docker-server=registry.gitlab.vnc.biz \ + --docker-username= \ + --docker-password='' \ + --docker-email=ci@vnc.biz # b) App config secret — copy the template, set a real SESSION_SECRET, apply. cp secret.example.yaml secret.yaml @@ -117,8 +116,8 @@ kubectl apply -f secret.yaml kubectl apply -k . ``` -> Alternative to (a): make the registry package public, then delete the -> `imagePullSecrets:` block from `base/deployment.yaml`. +> Alternative to (a): make the GitLab container registry public for this +> project, then delete the `imagePullSecrets:` block from `base/deployment.yaml`. After this one-time setup, routine deploys to `dev` happen automatically via CI on every push — see "Routine deploys go through CI now" above. This @@ -170,7 +169,7 @@ ArgoCD re-sync. | Symptom | Cause / fix | |---------|-------------| -| Pod `ImagePullBackOff` | `ghcr-pull` secret missing/expired, or package still private. Recreate the secret (§3a) or make the package public. | +| Pod `ImagePullBackOff` | `gitlab-registry` secret missing/expired, or token lacks `read_registry`. Recreate the secret (§3a) or make the registry public. | | Pod `CrashLoopBackOff`, logs show `EACCES`/permission on `/app/data` | Volume not writable by uid 1001. `securityContext.fsGroup: 1001` is set in `base/deployment.yaml` — keep it; some storage drivers also need it on the PVC. | | PVC stuck `Pending` | Wrong `storageClassName` in `base/pvc.yaml`. Set it to one from `kubectl get sc`. | | Ingress has no address / no cert | Wrong `ingressClassName` or cert issuer. Match bulwark's (§2). Check `kubectl -n vncmail describe ingress vncmail-plus`. | diff --git a/deploy/k8s/base/deployment.yaml b/deploy/k8s/base/deployment.yaml index b0ad23c0..cc41423c 100644 --- a/deploy/k8s/base/deployment.yaml +++ b/deploy/k8s/base/deployment.yaml @@ -23,20 +23,18 @@ spec: fsGroup: 1001 runAsUser: 1001 runAsGroup: 1001 - # Confirmed 2026-08-05: ghcr.io/brvncde-dotcom/vncmail-plus-dev IS public - # (anonymous token pull succeeded) — no imagePullSecrets needed. This is - # deploy/k8s/README.md's own documented alternative to creating a - # ghcr-pull secret. Removed rather than left referencing a - # not-yet-created secret, which would otherwise block every pod from - # starting regardless of the image being public (kubelet fails to - # resolve a missing imagePullSecrets entry before it ever gets to - # deciding whether auth was actually required). + # The GitLab container registry is private by default. Nodes need a + # docker-registry secret named `gitlab-registry` in the target namespace. + # Create it once per environment during first-time setup + # (see deploy/k8s/README.md §3a). + imagePullSecrets: + - name: gitlab-registry containers: - name: vncmail-plus - # Default/legacy value — CI overrides the image per-deploy via - # `kustomize edit set image`, so what's committed here never goes - # stale. For a one-off manual apply, pin a digest instead of :latest. - image: ghcr.io/brvncde-dotcom/vncmail-plus-dev:latest + # Generic placeholder — the real image name + tag are injected by the + # image-tag kustomize Component on every deploy (see + # overlays/*/image-tag/kustomization.yaml, rewritten by CI). + image: vncmail-plus:latest imagePullPolicy: Always ports: - containerPort: 3000 diff --git a/deploy/k8s/overlays/dev/image-tag/kustomization.yaml b/deploy/k8s/overlays/dev/image-tag/kustomization.yaml index 0f6cd8a1..1fdb4a71 100644 --- a/deploy/k8s/overlays/dev/image-tag/kustomization.yaml +++ b/deploy/k8s/overlays/dev/image-tag/kustomization.yaml @@ -6,6 +6,6 @@ apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component images: - - name: ghcr.io/brvncde-dotcom/vncmail-plus-dev - newName: ghcr.io/brvncde-dotcom/vncmail-plus-dev + - name: vncmail-plus + newName: registry.gitlab.vnc.biz/gitlab-instance-b9b5cf2f/vncmail-plus newTag: sha-147660a diff --git a/deploy/k8s/overlays/dev/patch-image-pull-policy.yaml b/deploy/k8s/overlays/dev/patch-image-pull-policy.yaml index 05011284..2203d87c 100644 --- a/deploy/k8s/overlays/dev/patch-image-pull-policy.yaml +++ b/deploy/k8s/overlays/dev/patch-image-pull-policy.yaml @@ -4,19 +4,6 @@ # is pure waste - the content behind that tag can never change, so re-pulling # it on every pod start only adds a registry round-trip and a hard dependency # on the registry being reachable at scheduling time. -# -# It is also load-bearing right now: until CI can actually push (GitLab's -# registry vhost serves Rails, not the registry - see .gitlab-ci.yml's -# "Registry history" note), sha- tagged images are side-loaded straight into -# each node's containerd: -# -# docker save --platform linux/amd64 -o vncmail.tar : -# scp vncmail.tar dev-k8s-N:/tmp/ && ssh dev-k8s-N \ -# 'microk8s ctr images import /tmp/vncmail.tar' -# -# imported to ALL of dev-k8s-1/2/3 so the pod can schedule anywhere. With -# Always, kubelet would ignore that local image and fail on a registry pull -# for a tag the registry has never seen. apiVersion: apps/v1 kind: Deployment metadata: diff --git a/deploy/k8s/overlays/prod/image-tag/kustomization.yaml b/deploy/k8s/overlays/prod/image-tag/kustomization.yaml index 7619b621..d8191fe5 100644 --- a/deploy/k8s/overlays/prod/image-tag/kustomization.yaml +++ b/deploy/k8s/overlays/prod/image-tag/kustomization.yaml @@ -7,6 +7,6 @@ apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component images: - - name: ghcr.io/brvncde-dotcom/vncmail-plus-dev + - name: vncmail-plus newName: registry.gitlab.vnc.biz/gitlab-instance-b9b5cf2f/vncmail-plus newTag: not-yet-promoted From 9377684fb1022b031d47e6734ad4830b0406ef9a Mon Sep 17 00:00:00 2001 From: Stefan Saenger Date: Thu, 6 Aug 2026 10:43:13 +0200 Subject: [PATCH 3/8] ci(build): add before_script for dind readiness and registry login --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 64a97771..a3b41726 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -72,8 +72,10 @@ build: - docker:28.4.0-dind rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "dev"' - script: + before_script: + - until docker info; do sleep 1; done - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" + script: - > docker build --build-arg GIT_COMMIT=$CI_COMMIT_SHA From cd02a9f806b600f755fcb15aeb594f79358fa328 Mon Sep 17 00:00:00 2001 From: Stefan Saenger Date: Thu, 6 Aug 2026 10:59:41 +0200 Subject: [PATCH 4/8] fix: dind URL --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a3b41726..59889d38 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,6 +41,7 @@ variables: GIT_STRATEGY: clone DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "/certs" + DOCKER_HOST: tcp://localhost:2375 # --------------------------------------------------------------------------- # verify — required check on every MR into dev. No registry, no cluster. From 77a9060c00aa560bbea001a63a0abc61ae4e1bba Mon Sep 17 00:00:00 2001 From: Stefan Saenger Date: Thu, 6 Aug 2026 11:12:25 +0200 Subject: [PATCH 5/8] fix(ci): remove DOCKER_TLS_CERTDIR to fix DinD connection failure DOCKER_TLS_CERTDIR forced the docker:28.4.0-dind service to listen on port 2376 with TLS, but DOCKER_HOST pointed to the non-TLS port 2375. This caused the docker client to loop forever with: Cannot connect to the Docker daemon at tcp://localhost:2375. Removing DOCKER_TLS_CERTDIR lets the daemon listen on 2375 again, matching DOCKER_HOST, restoring docker-in-docker connectivity. --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 59889d38..e2a71612 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -40,7 +40,6 @@ variables: IMAGE: $CI_REGISTRY_IMAGE GIT_STRATEGY: clone DOCKER_DRIVER: overlay2 - DOCKER_TLS_CERTDIR: "/certs" DOCKER_HOST: tcp://localhost:2375 # --------------------------------------------------------------------------- From dfa5667548d2f41cc88759d0257ab288911d59a7 Mon Sep 17 00:00:00 2001 From: Stefan Saenger Date: Thu, 6 Aug 2026 11:25:33 +0200 Subject: [PATCH 6/8] fix(ci): point DinD at the docker service alias, not localhost The build job failed with: Cannot connect to the Docker daemon at tcp://localhost:2375. Is the docker daemon running? Three things were wrong: 1. DOCKER_HOST was set to tcp://localhost:2375. The DinD daemon runs in the service sidecar container, not in the build container, so localhost was always going to refuse the connection. The correct host is the service alias 'docker'. 2. The docker:28.4.0-dind service was declared without an explicit alias. Without alias: docker, GitLab derives the hostname from the image string 'docker:28.4.0-dind', and since ':' is invalid in DNS, the 'docker' hostname never resolves. The explicit alias is required for tcp://docker:2375 to work at all. 3. docker:28.4.0-dind enables TLS by default and listens on 2376, but DOCKER_HOST points at 2375. Setting DOCKER_TLS_CERTDIR="" disables TLS so the daemon listens on plaintext 2375, matching DOCKER_HOST. This mirrors the known-working pattern in the vnc-localidp pipeline (docker:20.10.17-dind + alias: docker + DOCKER_HOST=tcp://docker:2375 + DOCKER_TLS_CERTDIR=""). The TLS-defaults behavior has been unchanged since docker 19.03, so the same pattern applies on 28.4.0. --- .gitlab-ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e2a71612..1818d462 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -40,7 +40,11 @@ variables: IMAGE: $CI_REGISTRY_IMAGE GIT_STRATEGY: clone DOCKER_DRIVER: overlay2 - DOCKER_HOST: tcp://localhost:2375 + # DinD service is reached at the `docker` alias (set explicitly on the + # service below), not localhost. TLS disabled so the daemon listens on + # plaintext 2375 — same pattern as the working vnc-localidp pipeline. + DOCKER_HOST: tcp://docker:2375 + DOCKER_TLS_CERTDIR: "" # --------------------------------------------------------------------------- # verify — required check on every MR into dev. No registry, no cluster. @@ -69,7 +73,8 @@ build: stage: build image: docker:28.4.0 services: - - docker:28.4.0-dind + - name: docker:28.4.0-dind + alias: docker rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "dev"' before_script: From 30d6c239087b76c6764846c3b80e3a82f1ae2dc2 Mon Sep 17 00:00:00 2001 From: Stefan Saenger Date: Thu, 6 Aug 2026 11:44:05 +0200 Subject: [PATCH 7/8] =?UTF-8?q?fix(ci):=20use=20alpine/git:2.47.2=20?= =?UTF-8?q?=E2=80=94=202.47.0=20was=20never=20published?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bump-dev (and identically-configured bump-prod) job failed during prepare_script with: ERROR: Job failed: prepare environment: waiting for pod running: pulling image "alpine/git:2.47.0": image pull failed: ... not found Root cause: alpine/git:2.47.0 does not exist on Docker Hub. The alpine/git 2.47.x line starts at 2.47.1 — there is no 2.47.0 build. The runner's image pull correctly fails with 'not found', and GitLab's Kubernetes executor treats an image-pull failure during prepare_script as fatal, so the job never reaches its script block. Fix: pin both bump-dev and bump-prod to alpine/git:2.47.2 (latest 2.47.x). Pinned rather than 'latest' so the job stays reproducible. bump-prod had the same nonexistent tag and would have hit the identical failure on its next run (whenever main advances), so both are fixed together. --- .gitlab-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1818d462..1cde7cea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,7 +96,9 @@ build: # --------------------------------------------------------------------------- bump-dev: stage: bump-dev - image: alpine/git:2.47.0 + # alpine/git:2.47.0 was never published on Docker Hub — the 2.47.x line + # starts at 2.47.1. Using 2.47.2 (latest 2.47.x). + image: alpine/git:2.47.2 rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "dev"' script: @@ -131,7 +133,7 @@ bump-dev: # --------------------------------------------------------------------------- bump-prod: stage: bump-prod - image: alpine/git:2.47.0 + image: alpine/git:2.47.2 rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "main"' script: From 3d5c05ffb933e5414730303d3462f9397e1a2bbe Mon Sep 17 00:00:00 2001 From: vncmail-ci Date: Thu, 6 Aug 2026 10:32:27 +0000 Subject: [PATCH 8/8] chore(deploy): pin dev to sha-f46614a4 [skip ci] --- deploy/k8s/overlays/dev/image-tag/kustomization.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/deploy/k8s/overlays/dev/image-tag/kustomization.yaml b/deploy/k8s/overlays/dev/image-tag/kustomization.yaml index 1fdb4a71..4d0c44eb 100644 --- a/deploy/k8s/overlays/dev/image-tag/kustomization.yaml +++ b/deploy/k8s/overlays/dev/image-tag/kustomization.yaml @@ -1,11 +1,8 @@ -# Owned by CI (the bump-dev job in .gitlab-ci.yml), not by hand. Kept as its -# own Component so CI only ever rewrites this 6-line file, never the parent -# overlays/dev/kustomization.yaml (structure/patches there stay under normal -# code review — CI regenerating a whole hand-maintained file on every push -# would silently revert any change made there between deploys). +# Owned by CI (bump-dev job in .gitlab-ci.yml) - regenerated every +# push to dev. Do not hand-edit; edits here get overwritten. apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component images: - name: vncmail-plus newName: registry.gitlab.vnc.biz/gitlab-instance-b9b5cf2f/vncmail-plus - newTag: sha-147660a + newTag: sha-f46614a4