Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
[![Hadolint](https://img.shields.io/badge/Hadolint-clean-success)](https://github.com/hadolint/hadolint)
[![Prettier](https://img.shields.io/badge/Code_Style-Prettier-F7B93E?logo=prettier&logoColor=black)](https://prettier.io/)

Reference [Dockerfiles](https://docs.docker.com/engine/reference/builder/) and best-practice guides for building **secure container images** that ship to production: small, signed, scanner-friendly, [OCI](https://opencontainers.org/)-compliant. [Python](https://www.python.org/), [Go](https://go.dev/), [JAX](https://jax.readthedocs.io/), [Node.js](https://nodejs.org/), and [TypeScript](https://www.typescriptlang.org/) are covered; [Rust](https://www.rust-lang.org/) is on the [roadmap](https://github.com/kssd/dockerfiles/issues).
Reference [Dockerfiles](https://docs.docker.com/engine/reference/builder/) and best-practice guides for building **secure container images** that ship to production: small, signed, scanner-friendly, [OCI](https://opencontainers.org/)-compliant. [Python](https://www.python.org/), [Go](https://go.dev/), [JAX](https://jax.readthedocs.io/), [Node.js](https://nodejs.org/), [TypeScript](https://www.typescriptlang.org/), and [Rust](https://www.rust-lang.org/) are covered.

## Why these templates

Expand Down Expand Up @@ -125,11 +125,20 @@ Multi-stage [TypeScript](https://www.typescriptlang.org/) images: a dedicated de

Full documentation: [`dockerfiles/typescript/README.md`](dockerfiles/typescript/README.md).

### Rust — cargo-chef dep caching + Distroless cc

Multi-stage [Rust](https://www.rust-lang.org/) images using [cargo-chef](https://github.com/LukeMathWalker/cargo-chef) to cache the dependency-compile layer separately from application source. Ships in [**Google Distroless cc**](https://github.com/GoogleContainerTools/distroless) — glibc and libstdc++ only, no shell, no package manager.

- Distroless production image (GNU) → [`dockerfiles/rust/Dockerfile.rust`](dockerfiles/rust/Dockerfile.rust)
- Chainguard production image → [`dockerfiles/rust/Dockerfile.rust.chainguard`](dockerfiles/rust/Dockerfile.rust.chainguard)
- VS Code devcontainer (Rust + cargo-edit + cargo-watch + cargo-nextest) → [`dockerfiles/rust/Dockerfile.devcontainer`](dockerfiles/rust/Dockerfile.devcontainer)

Full documentation: [`dockerfiles/rust/README.md`](dockerfiles/rust/README.md).

### Coming soon

Tracked as issues — comment or 👍 to bump priority.

- [Rust Dockerfile templates](https://github.com/kssd/dockerfiles/issues/8) _(planned)_
- [Zig Dockerfile templates](https://github.com/kssd/dockerfiles/issues/11) _(planned)_

## Guides
Expand Down
34 changes: 34 additions & 0 deletions dockerfiles/rust/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "Rust",
"build": {
"dockerfile": "../Dockerfile.devcontainer",
"context": "..",
"args": {
"VARIANT": "1-bookworm"
}
},
"remoteUser": "vscode",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
"workspaceFolder": "/workspace",
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer",
"vadimcn.vscode-lldb",
"tamasfe.even-better-toml",
"serayuzgur.crates",
"fill-labs.dependi",
"usernamehw.errorlens"
],
"settings": {
"rust-analyzer.check.command": "clippy",
"rust-analyzer.cargo.buildScripts.enable": true,
"editor.formatOnSave": true,
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
}
}
}
},
"postCreateCommand": "cargo fetch"
}
28 changes: 28 additions & 0 deletions dockerfiles/rust/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Build output — can be multiple GB; always exclude
target/

# Editor and IDE
.vscode/
.idea/
*.swp
*.swo

# Devcontainer
.devcontainer/

# Credentials and secrets
.env
*.pem
*.key
*.cert

# CI
.github/

# Git
.git/
.gitignore

# Documentation
*.md
LICENSE
25 changes: 25 additions & 0 deletions dockerfiles/rust/Dockerfile.devcontainer
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Rust devcontainer image (Microsoft devcontainers/rust base).
#
# Extends the official VS Code Rust devcontainer with additional tooling:
# - cargo-edit — cargo add/rm/upgrade subcommands
# - cargo-watch — auto-recompile on file changes (cargo watch -x run)
# - cargo-nextest — fast parallel test runner (cargo nextest run)
#
# The base image ships: rustup, cargo, clippy, rustfmt, rust-analyzer.
#
# Build context: dockerfiles/rust/
# docker build -f Dockerfile.devcontainer .
#
# Typical usage: open in VS Code with the Dev Containers extension;
# the .devcontainer/devcontainer.json references this file.
ARG VARIANT=1-bookworm

FROM mcr.microsoft.com/devcontainers/rust:${VARIANT}

USER vscode

RUN rustup component add clippy rustfmt rust-analyzer && \
cargo install cargo-edit --version 0.13.2 --locked && \
cargo install cargo-watch --version 8.5.3 --locked && \
cargo install cargo-nextest --locked && \
cargo install cargo-chef --version 0.1.77 --locked
58 changes: 58 additions & 0 deletions dockerfiles/rust/Dockerfile.rust
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Rust application image (cargo-chef + Google Distroless cc, multi-stage).
#
# Uses cargo-chef to cache the dependency-compile layer separately from
# application source. Changing only src/ reuses the pre-built deps layer,
# cutting rebuild time from minutes to seconds.
#
# cargo-chef stages:
# chef — installs cargo-chef; shared base for the next two stages.
# planner — runs `cargo chef prepare` to extract dependency metadata.
# builder — cooks deps first (cacheable), then compiles the binary.
#
# Runtime: gcr.io/distroless/cc-debian12:nonroot includes glibc and
# libstdc++ for dynamically-linked GNU Rust binaries. To produce a
# fully-static binary instead, compile with --target x86_64-unknown-linux-musl
# and switch the runtime to gcr.io/distroless/static-debian12:nonroot.
#
# BIN_NAME:
# Set --build-arg BIN_NAME=<your_binary_name> (default: app).
# Must match the [[bin]] name in Cargo.toml.
#
# Multi-arch builds:
# docker buildx build --platform=linux/amd64,linux/arm64 \
# --build-arg BIN_NAME=mybin -t myapp -f Dockerfile.rust .
#
# Build:
# docker build --build-arg BIN_NAME=mybin -t myapp -f Dockerfile.rust .
#
# Run (hardened):
# docker run --rm \
# --read-only \
# --cap-drop=ALL \
# --security-opt=no-new-privileges \
# myapp
ARG RUST_TAG=1.82-slim
ARG DISTROLESS_TAG=debian12

FROM rust:${RUST_TAG} AS chef
RUN cargo install cargo-chef --version 0.1.77 --locked
WORKDIR /app

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
ARG BIN_NAME=app
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
RUN cargo build --release --bin "${BIN_NAME}" && \
cp "target/release/${BIN_NAME}" /server

FROM gcr.io/distroless/cc-${DISTROLESS_TAG}:nonroot
COPY --from=builder --chown=nonroot:nonroot /server /app/server

USER nonroot

ENTRYPOINT ["/app/server"]
53 changes: 53 additions & 0 deletions dockerfiles/rust/Dockerfile.rust.chainguard
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Rust application image (cargo-chef + Chainguard glibc-dynamic, multi-stage).
#
# Chainguard sibling of Dockerfile.rust. Builds with cgr.dev/chainguard/rust
# and ships in cgr.dev/chainguard/glibc-dynamic — daily-rebuilt, Sigstore-
# signed, with SLSA provenance and SBOMs attached.
#
# Free vs. paid Chainguard tags:
# The free Developer Edition only publishes :latest. Versioned tags
# require a paid Chainguard subscription. For free-tier reproducibility,
# pin by digest instead of relying on :latest:
#
# docker pull cgr.dev/chainguard/rust:latest
# docker inspect --format='{{index .RepoDigests 0}}' cgr.dev/chainguard/rust:latest
# # bake the digest into the FROM line:
# FROM cgr.dev/chainguard/rust@sha256:<digest> AS chef
#
# Refresh the digest deliberately (e.g. weekly) and re-scan.
#
# cargo-chef stages:
# chef — installs cargo-chef; shared base for the next two stages.
# planner — runs `cargo chef prepare` to extract dependency metadata.
# builder — cooks deps first (cacheable), then compiles the binary.
#
# BIN_NAME:
# Set --build-arg BIN_NAME=<your_binary_name> (default: app).
#
# Build (free tier — pin by digest in production):
# docker build --build-arg BIN_NAME=mybin -t myapp -f Dockerfile.rust.chainguard .
ARG BASE_TAG=latest

FROM cgr.dev/chainguard/rust:${BASE_TAG} AS chef
ENV PATH="/home/nonroot/.cargo/bin:${PATH}"
RUN cargo install cargo-chef --version 0.1.77 --locked
WORKDIR /app

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
ARG BIN_NAME=app
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
COPY . .
RUN cargo build --release --bin "${BIN_NAME}" && \
cp "target/release/${BIN_NAME}" /server

FROM cgr.dev/chainguard/glibc-dynamic:${BASE_TAG}
COPY --from=builder /server /app/server

USER nonroot

ENTRYPOINT ["/app/server"]
Loading
Loading