Build secure container images with Wolfi runtime, non-root users, and multi-stage builds. Templates for Python/uv, Bun, Node.js/pnpm, Golang (static/CGO), and Rust (glibc/musl) with allocator optimization
/plugin marketplace add pigfoot/claude-code-hubs/plugin install secure-container-build@pigfoot-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/Containerfile.bunassets/Containerfile.golangassets/Containerfile.golang-cgoassets/Containerfile.nodejsassets/Containerfile.python-uvassets/Containerfile.rustassets/Containerfile.rust-muslreferences/allocator-comparison.mdreferences/debugging-containers.mdreferences/dependency-management.mdreferences/security-best-practices.mdBuild secure, minimal container images using security-hardened runtime images and multi-stage builds.
Use Wolfi glibc-dynamic as the runtime base image:
Production vs Development:
# ARG for choosing runtime image tag
ARG RUNTIME_TAG=latest
FROM cgr.dev/chainguard/glibc-dynamic:${RUNTIME_TAG}
latest (default): Production - no shell, most securelatest-dev: Development - includes shell for debuggingBuild for debugging:
podman build --build-arg RUNTIME_TAG=latest-dev -t myapp:debug .
podman run -it myapp:debug sh # Can exec with shell
Always use multi-stage builds to minimize runtime image size:
Wolfi images use UID 65532 (nonroot user) by default:
# No need to create user, already exists in Wolfi
USER 65532:65532
# When copying files from builder, set ownership
COPY --from=builder --chown=65532:65532 /app /app
Wolfi images don't include tini. Copy it from builder:
# Copy tini from builder stage
COPY --from=builder /usr/bin/tini-static /usr/bin/tini
# Use as entrypoint for proper signal handling
ENTRYPOINT ["tini", "--"]
CMD ["your-app"]
Copy Containerfile template:
cp assets/Containerfile.python-uv ./Containerfile
Update application command:
CMD ["python", "your_app.py"] # Change to your entry point
Copy Containerfile template:
cp assets/Containerfile.bun ./Containerfile
Update runtime command:
CMD ["bun", "run", "start"] # Change to your script
Copy Containerfile template:
cp assets/Containerfile.nodejs ./Containerfile
Update entry point:
CMD ["node", "./dist/index.js"] # Change to your compiled output
First: Do you need CGO? (packages with C bindings)
Most Go projects don't need CGO. Quick test:
CGO_ENABLED=0 go build .
# Success → Use Containerfile.golang (static, recommended)
# Fails → Use Containerfile.golang-cgo (CGO)
Common CGO packages: mattn/go-sqlite3, git2go/git2go, h2non/bimg
Option A: Pure Go (no CGO) - Recommended
Copy Containerfile template:
cp assets/Containerfile.golang ./Containerfile
Update binary name:
CMD ["/app/server"] # Change to your binary name
Option B: Requires CGO (SQLite, C libraries)
cp assets/Containerfile.golang-cgo ./Containerfile
Default: Use glibc template (best compatibility)
Copy template:
cp assets/Containerfile.rust ./Containerfile
Update binary name:
CMD ["/app/server"] # Change to your binary name
Optional: Boost performance with mimalloc
Three allocator options (see comments in Containerfile):
Why mimalloc? Reduces memory usage by ~50% vs glibc malloc under load. See allocator comparison for details vs jemalloc/tcmalloc.
Advanced: Smallest image with musl
If you need the smallest possible image and have no C dependencies:
cp assets/Containerfile.rust-musl ./Containerfile
Warning: musl's allocator is 7-10x slower in multi-threaded workloads. You MUST add mimalloc to Cargo.toml (see Containerfile comments).
FROM docker.io/python:3-slim AS builder
Use official Python slim images for building Python applications with uv.
FROM docker.io/node:lts-slim AS builder
Use official Node.js LTS slim images for building Bun or Node.js applications.
FROM docker.io/golang:1 AS builder
Use official Golang images for building Go applications. Includes Go toolchain and gcc for CGO support.
FROM docker.io/rust:slim AS builder
Use official Rust slim images for building Rust applications. Includes rustc, cargo, and rustup for target management.
Use BuildKit cache mounts to speed up dependency installation:
# Python/uv
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev
# Bun
RUN bun install --frozen-lockfile
# pnpm
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
# Rust/cargo (both registry and target dir)
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release
FROM cgr.dev/chainguard/glibc-dynamic:${RUNTIME_TAG}
FROM cgr.dev/chainguard/static:${RUNTIME_TAG}
All Containerfile templates support both production and debug builds:
podman build -t myapp:latest .
cgr.dev/chainguard/glibc-dynamic:latestpodman build --build-arg RUNTIME_TAG=latest-dev -t myapp:debug .
cgr.dev/chainguard/glibc-dynamic:latest-devNever use debug images in production!
For detailed debugging techniques, see references/debugging-containers.md.
For detailed information, consult these reference files:
references/security-best-practices.mdreferences/dependency-management.mdreferences/debugging-containers.mdreferences/allocator-comparison.mdFor GitHub Actions workflows to build multi-arch images, see the github-actions-container-build plugin which provides:
Build timeout:
Binary not found:
Container starts but exits immediately:
podman run -it myapp:debug shTest image locally:
podman run --rm -it "$IMAGE:$TAG"
Inspect built image:
podman inspect "$IMAGE:$TAG"
Check image size:
podman images "$IMAGE:$TAG"
Master authentication and authorization patterns including JWT, OAuth2, session management, and RBAC to build secure, scalable access control systems. Use when implementing auth systems, securing APIs, or debugging security issues.