Project Setup Best Practices
This is a working checklist for how a repository should be set up to build, scan, and ship container images.
Search across all documentation pages
This is a working checklist for how a repository should be set up to build, scan, and ship container images.
It assumes one image per deployable, per-service Dockerfiles co-located with code, and BuildKit as the default backend in Docker Engine 29.
Read the groups top to bottom; they roughly follow the order you would harden a repo.
Each bullet leads with the practice in bold, then the reason.
Adopt them incrementally - a .dockerignore and pinned bases deliver most of the value on day one, and signing plus admission control come as the org matures.
Dockerfile per deployable, co-located with its code. The image boundary is visible in the tree and ownership is unambiguous..dockerignore you actually maintain. It shrinks the build context and keeps .git, node_modules, and .env files out of layers.COPY shared packages.packages/ and services in services/. Predictable paths make selective COPY and cache ordering easy to reason about.USER (for example USER 10001) so the container cannot run as root by default.FROM registry.internal/base/node:22@sha256:... makes builds reproducible and defeats tag drift.RUN --mount=type=secret for build credentials. Secrets stay out of layers instead of being baked in by COPY or ARG.Makefile or just so one command is canonical. Developers and CI run the same build, scan, and push, so behavior does not drift.TAG := $(shell git rev-parse --short HEAD) ties every image back to a commit for traceability.IMAGE ?= registry.internal/api
TAG ?= $(shell git rev-parse --short HEAD)
build:
docker build -f services/api/Dockerfile -t $(IMAGE):$(TAG) .
scan:
trivy image --exit-code 1 --severity HIGH,CRITICAL $(IMAGE):$(TAG)
push: scan
docker push $(IMAGE):$(TAG)docker compose wires databases and dependencies locally; it does not define your deployment.target: build plus a volume reflects edits without a rebuild.You should have per-service Dockerfiles, a maintained root .dockerignore, multi-stage non-root builds, and digest-pinned bases.
Your build/scan/push should live in one Makefile or just file that CI and laptops share, with a CVE gate before push.
Images should be signed and, ideally, verified at admission, so the cluster only runs artifacts your pipeline produced.
Makefile or just? Either works; the point is a single canonical entry point for build, scan, and push. just reads more cleanly, make is universal.
Where should the scan run? In CI, before push, with a non-zero exit on fixable HIGH/CRITICAL findings so vulnerable images never reach the registry.
Do I pin by tag or digest? Publish a readable tag but pin the digest in FROM and in manifests, so builds and deployments are reproducible.
Is Compose part of production? No. Compose is a local development tool; production is Kubernetes manifests or Helm running containerd on nodes.
How much of this is day-one work? A .dockerignore, multi-stage non-root builds, pinned bases, and a wrapped build/scan/push are the high-value basics. Signing and admission come later.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026