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
npx claudepluginhub pigfoot/claude-code-hubs --plugin secure-container-buildThis skill uses the workspace's default tool permissions.
Build secure, minimal container images using security-hardened runtime images and multi-stage builds.
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.mdBuilds minimal container images using Google distroless bases to reduce attack surface by removing shells, package managers, and OS utilities for secure Docker/Kubernetes deployments.
Builds minimal container images on Google distroless bases to reduce attack surface. Includes multi-stage Dockerfiles for Go, Java, Python, Node.js apps.
Creates multi-stage Dockerfiles separating build and runtime for minimal production images using scratch, distroless, alpine. Use for oversized images, build tools in runtime, or edge/serverless deployments.
Share bugs, ideas, or general feedback.
Build secure, minimal container images using security-hardened runtime images and multi-stage builds.
Use Wolfi glibc-dynamic as the runtime base image:
# 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 debuggingpodman 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
Copy Containerfile template:
cp assets/Containerfile.golang ./Containerfile
Update binary name:
CMD ["/app/server"] # Change to your binary name
Copy CGO template:
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.
If you need the smallest possible image and have no C dependencies:
Copy musl template:
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-devFor 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"