From 0ac793aa9b6f57a7a79770c2ca7399184f0fb72f Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:54:19 -0300 Subject: [PATCH 1/5] perf(build): tune [profile.release] for smaller binary size (#60) Co-authored-by: Claude Sonnet 4.6 --- CHANGELOG.md | 1 + Cargo.toml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce63b4f..9eb6813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Consolidated `commitlint`, `format`, and `lint` CI jobs into a single `lint` job (#42) +- `[profile.release]` tuned in `Cargo.toml` with `lto = true`, `codegen-units = 1`, `strip = true`, `panic = "abort"` for smaller binary size (#60) - `get_all`, `get_by_id`, `get_by_squad_number`, and `delete` in `player_service` now return `Result` instead of `Result`, aligning with the `CreateError`/`UpdateError` pattern (#56) - `codecov.yml` `ignore` list extended with `src/**/mod.rs` to exclude module re-exports from coverage reporting (#78) - `codecov.yml` comment updated to reflect goal of maximum coverage on business logic layers (#78) diff --git a/Cargo.toml b/Cargo.toml index 1aa4773..7ba3fd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,9 @@ diesel_migrations = "2.3" libsqlite3-sys = { version = "0.30", features = ["bundled"] } rocket_okapi = { version = "0.9.0", features = ["swagger"] } schemars = { version = "1.2" } + +[profile.release] +lto = true # link-time optimisation — reduces binary size and improves inlining +codegen-units = 1 # single codegen unit — required for full LTO +strip = true # strip debug symbols (Rust 1.59+) +panic = "abort" # remove unwinding machinery — smaller binary, faster panics From fec4570c218eb5ee2ad8894eb86ff0d09380fcd7 Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:55:01 -0300 Subject: [PATCH 2/5] perf(docker): switch to musl target and Alpine runtime (#57) Co-authored-by: Claude Sonnet 4.6 --- CHANGELOG.md | 1 + Dockerfile | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eb6813..5e22578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Consolidated `commitlint`, `format`, and `lint` CI jobs into a single `lint` job (#42) - `[profile.release]` tuned in `Cargo.toml` with `lto = true`, `codegen-units = 1`, `strip = true`, `panic = "abort"` for smaller binary size (#60) +- `Dockerfile` builder stage updated to compile against `x86_64-unknown-linux-musl` via `musl-tools` for a fully static binary; runtime stage switched from `debian:bookworm-slim` to `alpine` (#57) - `get_all`, `get_by_id`, `get_by_squad_number`, and `delete` in `player_service` now return `Result` instead of `Result`, aligning with the `CreateError`/`UpdateError` pattern (#56) - `codecov.yml` `ignore` list extended with `src/**/mod.rs` to exclude module re-exports from coverage reporting (#78) - `codecov.yml` comment updated to reflect goal of maximum coverage on business logic layers (#78) diff --git a/Dockerfile b/Dockerfile index f34350f..d361550 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,13 +4,18 @@ # ------------------------------------------------------------------------------ FROM rust:1.88-slim-bookworm AS builder -# Install build dependencies required by rusqlite (bundled feature compiles -# SQLite from source via the cc crate and needs a C compiler) +# Install build dependencies: +# - gcc / pkg-config: required by rusqlite (bundled feature compiles SQLite from source) +# - musl-tools: provides musl-gcc linker for the x86_64-unknown-linux-musl target RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ pkg-config \ + musl-tools \ && rm -rf /var/lib/apt/lists/* +# Add the musl target to produce a fully static binary +RUN rustup target add x86_64-unknown-linux-musl + WORKDIR /app # Copy dependency manifests first to leverage layer caching @@ -23,7 +28,8 @@ RUN mkdir src && echo "fn main() {}" > src/main.rs RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/app/target \ - cargo build --release + CC_x86_64_unknown_linux_musl=musl-gcc \ + cargo build --release --target x86_64-unknown-linux-musl # Overlay with the real application sources COPY src/ ./src/ @@ -35,19 +41,18 @@ COPY Rocket.toml ./ RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/app/target \ touch src/main.rs && \ - cargo build --release && \ - cp target/release/rust-samples-rocket-restful /app/rust-samples-rocket-restful + CC_x86_64_unknown_linux_musl=musl-gcc \ + cargo build --release --target x86_64-unknown-linux-musl && \ + cp target/x86_64-unknown-linux-musl/release/rust-samples-rocket-restful /app/rust-samples-rocket-restful # ------------------------------------------------------------------------------ # Stage 2: Runtime # This stage creates the final, minimal image to run the application. # ------------------------------------------------------------------------------ -FROM debian:bookworm-slim AS runtime +FROM alpine AS runtime # Install curl for health check -RUN apt-get update && apt-get install -y --no-install-recommends \ - curl \ - && rm -rf /var/lib/apt/lists/* +RUN apk add --no-cache curl WORKDIR /app @@ -72,8 +77,8 @@ COPY --chmod=555 scripts/entrypoint.sh ./entrypoint.sh COPY --chmod=555 scripts/healthcheck.sh ./healthcheck.sh # Add system user and prepare volume mount point -RUN addgroup --system rocket && \ - adduser --system --ingroup rocket rocket && \ +RUN addgroup -S rocket && \ + adduser -S -G rocket rocket && \ mkdir -p /storage && \ chown -R rocket:rocket /storage From 799dfb29252a167121c37cc854d0a78cb07a64c4 Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:33:06 -0300 Subject: [PATCH 3/5] docs(docker): improve Dockerfile comments and section structure (#57) Co-authored-by: Claude Sonnet 4.6 --- Dockerfile | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/Dockerfile b/Dockerfile index d361550..5044765 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,26 +4,26 @@ # ------------------------------------------------------------------------------ FROM rust:1.88-slim-bookworm AS builder -# Install build dependencies: -# - gcc / pkg-config: required by rusqlite (bundled feature compiles SQLite from source) -# - musl-tools: provides musl-gcc linker for the x86_64-unknown-linux-musl target +# -- Install system packages --------------------------------------------------- +# gcc / pkg-config: required by libsqlite3-sys (bundled feature compiles from +# source via the cc crate) +# musl-tools: provides musl-gcc, the C linker required for the +# x86_64-unknown-linux-musl target RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ pkg-config \ musl-tools \ && rm -rf /var/lib/apt/lists/* -# Add the musl target to produce a fully static binary RUN rustup target add x86_64-unknown-linux-musl WORKDIR /app -# Copy dependency manifests first to leverage layer caching +# -- Pre-build dependencies (cached) ------------------------------------------ +# Copy only the manifests and a stub main.rs so Cargo can compile all +# dependencies in isolation. This layer is cached and only invalidated when +# Cargo.toml or Cargo.lock change — not when application source changes. COPY Cargo.toml Cargo.lock ./ - -# Stub out a minimal src/main.rs so Cargo can resolve and compile dependencies -# without the real application sources — this layer is only invalidated when -# Cargo.toml or Cargo.lock change. RUN mkdir src && echo "fn main() {}" > src/main.rs RUN --mount=type=cache,target=/usr/local/cargo/registry \ @@ -31,13 +31,15 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ CC_x86_64_unknown_linux_musl=musl-gcc \ cargo build --release --target x86_64-unknown-linux-musl -# Overlay with the real application sources +# -- Build application --------------------------------------------------------- +# Overlay with the real sources. The stub main.rs is overwritten by the COPY, +# but Cargo uses mtime to detect changes — touching main.rs after the COPY +# ensures Cargo recompiles the application crate without re-compiling +# dependencies (which remain in the cache-mounted target/). COPY src/ ./src/ COPY migrations/ ./migrations/ COPY Rocket.toml ./ -# Touch main.rs so Cargo detects the change, rebuild only app code, then copy -# the binary out of the cache-mounted target/ into the image layer. RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/app/target \ touch src/main.rs && \ @@ -51,32 +53,27 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ # ------------------------------------------------------------------------------ FROM alpine AS runtime -# Install curl for health check -RUN apk add --no-cache curl - WORKDIR /app -# Metadata labels for the image. These are useful for registries and inspection. +# -- Install system packages --------------------------------------------------- +RUN apk add --no-cache curl + +# -- Metadata ------------------------------------------------------------------ LABEL org.opencontainers.image.title="🧪 RESTful API with Rust and Rocket" LABEL org.opencontainers.image.description="Proof of Concept for a RESTful API made with Rust and Rocket" LABEL org.opencontainers.image.licenses="MIT" LABEL org.opencontainers.image.source="https://github.com/nanotaboada/rust-samples-rocket-restful" LABEL org.sonarsource.docker.dockerfile="/Dockerfile" -# https://rules.sonarsource.com/docker/RSPEC-6504/ - -# Copy application binary and Rocket configuration +# -- Copy artifacts ------------------------------------------------------------ COPY --from=builder /app/rust-samples-rocket-restful . COPY --from=builder /app/Rocket.toml ./Rocket.toml - -# Copy metadata docs for container registries (e.g.: GitHub Container Registry) COPY --chmod=444 README.md ./ - -# Copy entrypoint and healthcheck scripts COPY --chmod=555 scripts/entrypoint.sh ./entrypoint.sh COPY --chmod=555 scripts/healthcheck.sh ./healthcheck.sh -# Add system user and prepare volume mount point +# -- Configure runtime --------------------------------------------------------- +# https://rules.sonarsource.com/docker/RSPEC-6504/ RUN addgroup -S rocket && \ adduser -S -G rocket rocket && \ mkdir -p /storage && \ From e09ec70e8904b3eac293603b9f9b8ed0c62d0bcc Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:06:09 -0300 Subject: [PATCH 4/5] fix(docker): pin both FROM stages to linux/amd64 platform (#57) Co-authored-by: Claude Sonnet 4.6 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5044765..b7e2959 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # Stage 1: Builder # This stage builds the application and its dependencies. # ------------------------------------------------------------------------------ -FROM rust:1.88-slim-bookworm AS builder +FROM --platform=linux/amd64 rust:1.88-slim-bookworm AS builder # -- Install system packages --------------------------------------------------- # gcc / pkg-config: required by libsqlite3-sys (bundled feature compiles from @@ -51,7 +51,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ # Stage 2: Runtime # This stage creates the final, minimal image to run the application. # ------------------------------------------------------------------------------ -FROM alpine AS runtime +FROM --platform=linux/amd64 alpine AS runtime WORKDIR /app From e8cb513fe44f705269b49114b84d7cfc25c89a83 Mon Sep 17 00:00:00 2001 From: Nano Taboada <87288+nanotaboada@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:18:29 -0300 Subject: [PATCH 5/5] fix(docker): pin Alpine runtime to 3.23 for reproducible builds (#57) Co-authored-by: Claude Sonnet 4.6 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b7e2959..3cdc027 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,7 +51,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ # Stage 2: Runtime # This stage creates the final, minimal image to run the application. # ------------------------------------------------------------------------------ -FROM --platform=linux/amd64 alpine AS runtime +FROM --platform=linux/amd64 alpine:3.23 AS runtime WORKDIR /app