Zum Inhalt springen

building-docker-images

Type Skill
Plugin awl-cicd · v0.0.22
Invoke /awl-cicd:building-docker-images
Tools Read, Write, Edit, Bash, Glob, Grep
Source plugins/awl-cicd/skills/building-docker-images/SKILL.md

This skill should be used when the user asks to “add a docker build”, “write a Dockerfile”, “build the image in CI”, “multi-arch image”, “buildkit”, “docker build fails in CI”, “private npm packages in docker”, or hits registry 401/404 errors. Builds Docker images in GitLab CI/CD with the AWL components (build-buildkit, build-docker-simple, build-docker-multiarch), passes build arguments and private registry tokens, and diagnoses builds that pass tests but fail in the image.

Trigger phrases: add a docker build · write a Dockerfile · build the image in CI · multi-arch image · buildkit · docker build fails in CI · private npm packages in docker

Situation Template
App deploys with deploy-default-branches none, it includes build-docker-simple as build-docker
Standalone build, new project build-buildkit (in-cluster BuildKit, no dind, registry cache)
Standalone build on the docker runner build-docker-simple
linux/amd64 + linux/arm64 build-docker-multiarch
Legacy develop/release flow with version build-docker
include:
- component: $CI_SERVER_FQDN/devops/ci-cd-templates/build-buildkit@main
inputs:
build_dir: .
dockerfile: Dockerfile
tag: $CI_COMMIT_REF_SLUG
cache_tag: cache
build-image:
extends: .build-buildkit
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
build-image-mr:
extends: .build-buildkit
variables:
PUSH: "false"
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"

Build args go through extra_args: "--opt build-arg:API_URL=https://..." (BuildKit syntax), not --build-arg.

For private @awl/* packages pass CI_JOB_TOKEN into the build.

.gitlab-ci.yml (build-docker-simple / deploy-default-branches):

build-docker:
variables:
BUILD_ARG: "--build-arg CI_JOB_TOKEN=${CI_JOB_TOKEN}"

Dockerfile:

FROM registry.appswithlove.net/devops/docker-images/node:22-alpine AS deps
WORKDIR /app
ARG CI_JOB_TOKEN
COPY package.json pnpm-lock.yaml .npmrc* ./
RUN CI_JOB_TOKEN=${CI_JOB_TOKEN} pnpm i --frozen-lockfile

.npmrc:

@awl:registry=https://gitlab.appswithlove.net/api/v4/packages/npm/
//gitlab.appswithlove.net/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}

The AWL node images already contain pnpm and the registry config; plain node:*-alpine needs corepack enable pnpm.

Build-time secrets (Sentry sourcemaps and similar)

Abschnitt betitelt „Build-time secrets (Sentry sourcemaps and similar)“

A build arg that comes from an environment-scoped CI variable is empty unless the build job has an environment: block:

build-docker:
environment:
name: staging
action: verify
variables:
BUILD_ARG: >-
--build-arg SENTRY_AUTH_TOKEN=${SENTRY_AUTH_TOKEN}
--build-arg SENTRY_RELEASE=${CI_COMMIT_SHORT_SHA}

Without it the upload step in the image runs with an empty token and usually fails silently.

Hidden job .build-buildkit, image moby/buildkit:*-rootless, talks to the cluster BuildKit daemon.

Input / variable Default Description
build_dir . Build context
dockerfile Dockerfile Path relative to the workspace
image $CI_REGISTRY_IMAGE
tag $CI_COMMIT_REF_SLUG
extra_args "" Extra buildctl build args, e.g. --opt build-arg:K=V
push true PUSH: "false" on a job builds without pushing
platform "" e.g. linux/amd64,linux/arm64; empty = native
cache_tag "" Registry cache image tag; empty disables caching
buildkit_host tcp://buildkit:1234
stage build

Inputs resolve at include time. Per-job overrides use the upper-case variables (PUSH, IMAGE_TAG, DOCKERFILE, BUILD_DIR, EXTRA_ARGS, PLATFORM, CACHE_TAG).

Hidden job .docker-build-simple, runs docker build on the docker runner.

Variable Default Description
BUILD_DIR . Dockerfile directory
BUILD_ARG "" --build-arg K=V ...
IMAGE_NAME $CI_REGISTRY_IMAGE
IMAGE_TAG $CI_COMMIT_REF_NAME

Hidden job .docker-build-multiarch, Docker Buildx with registry build cache and reproducible-build flags.

Input Default Description
platforms linux/amd64,linux/arm64
build_dir .
dockerfile Dockerfile
build_arg "" --build-arg K=V
image_tag $CI_COMMIT_REF_SLUG
cache_tag cache
image docker:cli Job image

Matrix builds override the upper-case variables per cell (IMAGE_TAG, DOCKERFILE, CACHE_TAG), not the inputs. DOCKER_PUSH: "false" for MR validation.

Tags IMAGE:develop on develop and IMAGE:release-<VERSION> on main/release. Needs the version template. Inputs build_dir, build_arg.

Non-Docker artifact build: NODE_IMAGE, NPM_INSTALL, NPM_BUILD, ARTIFACTS_FOLDER, WORKING_DIR.

Error Cause Fix
ERR_PNPM_FETCH_404 / “No authorization header” Missing CI_JOB_TOKEN Add BUILD_ARG: "--build-arg CI_JOB_TOKEN=${CI_JOB_TOKEN}"
Permission denied on registry Missing .npmrc COPY .npmrc* ./ before install
Build arg not used Wrong variable name BUILD_ARG (upper case), not build_arg
Docker login fails Missing registry vars Check CI_REGISTRY_* are available
Tests green, pnpm build fails only in Docker .dockerignore excludes a file the build imports (e.g. test/ used by vitest.config.ts) Exclude the importing config too, or stop importing from ignored paths. Reproduce with the exact Docker step locally
Build arg from a scoped variable is empty Job has no environment: Add environment: { name: <env>, action: verify }
Image builds but the container has no icon/asset/config File added after the image was built, or PoC never rebuilt Rebuild; PoC deployments need Renovate or a kill date
Sourcemap upload silently skipped Empty SENTRY_AUTH_TOKEN in the build See build-time secrets
  1. Copy .npmrc* before npm install / pnpm install.
  2. Use multi-stage builds; the runtime stage ships only build output.
  3. Pin base images (node:22-alpine, not node:latest); prefer the AWL images for @awl/* packages.
  4. Use --frozen-lockfile.
  5. Keep .dockerignore and the build’s imports consistent. CI tests run on a full checkout and don’t catch what the image can’t see.
  6. Never bake secrets into layers. Build-time tokens go through build args and stay in the build stage.
  7. Rebuild deployed images regularly. An image from months ago carries the vulnerabilities of that month; a showcase on natron-staging was exploited through an unpatched Next.js in 2026.
FROM registry.appswithlove.net/devops/docker-images/node:22-alpine AS deps
WORKDIR /app
ARG CI_JOB_TOKEN
COPY package.json pnpm-lock.yaml .npmrc* ./
RUN CI_JOB_TOKEN=${CI_JOB_TOKEN} pnpm i --frozen-lockfile
FROM registry.appswithlove.net/devops/docker-images/node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build
FROM registry.appswithlove.net/devops/docker-images/node:22-alpine
WORKDIR /app
COPY --from=builder /app/build ./build
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["node", "build"]

The steps/build-sveltekit-docker CI step ships this Dockerfile as a shared, versioned file (see gitlab-cicd/references/templates/utilities.md).