Build container images from an appropriate maintained base, pin the content used for a release, keep secrets outside image layers, and run the application with minimal permissions. Test the resulting artifact as the same user and architecture you intend to deploy.

Build a small, complete example

This lab requires Docker with BuildKit. Use an empty directory. The BusyBox image below is an illustrative shell application, not a Python base image or a recommendation for every workload.

cat > Dockerfile <<'EOF'
FROM docker.io/library/busybox@sha256:a7dbc319b1438115a207e11930dc4bb3e643dcb3ec0bdd694bb36cd8fad289ec
WORKDIR /app
USER 10001:10001
CMD ["sh", "-c", "id -u; echo container-ready"]
EOF
docker build --pull -t cloudhero-image-lab:local .
docker run --rm --read-only --cap-drop=ALL   --security-opt=no-new-privileges cloudhero-image-lab:local

Expect UID 10001 and container-ready. An image digest identifies registry content; an image ID is not interchangeable with a multi-platform manifest digest. Check the platforms published by your base image. Pinning also requires a deliberate rebuild and update process.

Pass build secrets through secret mounts

Do not put credentials in ARG, ENV or a copied file that is later deleted: layers and build metadata can preserve them. BuildKit secret mounts expose a secret only to the instruction that needs it. Never print or copy it into build output.

# Illustrative Dockerfile instruction; adapt to your artifact download.
RUN --mount=type=secret,id=artifact_token \
    test -s /run/secrets/artifact_token
# Supply a local secret file when building:
# docker build --secret id=artifact_token,src=./artifact-token .

Validate more than image size

Scan the final image and its dependencies, inspect the configured user and entrypoint, and test startup and shutdown. A trusted registry and a small image are useful inputs; neither establishes that an image is vulnerability-free. Runtime secrets can be mounted from a secret manager. Anyone permitted to inspect the workload may still obtain its configuration.

Clean up the lab

docker image rm cloudhero-image-lab:local

For Kubernetes runtime secrets, see our tested External Secrets guide.

Sources and further reading