Get Started
← All Posts

Zero-CVE: Building a Vulnerability-Free Rust Container

How we engineered a mathematically proven, secure Docker pipeline for MarkReply using Chainguard Wolfi, Trivy, and OWASP ZAP.

Zero-CVE: Building a Vulnerability-Free Rust Container

When building MarkReply, our goal was to completely eliminate the bloat associated with modern commenting engines. But writing memory-safe Rust code is only half the battle. If you wrap a secure binary in a vulnerable Linux container, your infrastructure is still at risk.

If you have ever run a vulnerability scanner against a standard debian-slim or alpine image, you know the frustration. You immediately hit a wall of “deferred” CVEs tied to obscure OS utilities. We refused to ship MarkReply with 60+ known vulnerabilities, so we engineered a genuinely zero-CVE pipeline.

Here is how we did it.

🗑️ The Problem with “Slim” Images

Most Docker tutorials recommend using debian-slim for your runtime environment. While smaller than a full OS, these images still pack a massive amount of unnecessary baggage—package managers, shell utilities, and core libraries that a compiled Rust application will never actually execute.

Every unused utility included in your container is a potential attack vector for supply chain exploits.

🛡️ The Chainguard Wolfi Solution

To eliminate this attack surface, we migrated the runtime stage of our Dockerfile to Chainguard’s Wolfi. Wolfi is an “undistro”—a Linux environment designed specifically for containerized workloads with zero unnecessary OS bloat.

By leveraging a multi-stage Docker build, we use a standard Debian environment to compile the Rust binary, but we export only that final binary into the ultra-secure Wolfi runtime.

# Stage 1: Build the Rust binary
FROM rust:slim-bookworm AS builder
WORKDIR /usr/src/app
# Install build dependencies (including SQLite for compile-time queries)
RUN apt-get update && apt-get install -y pkg-config libssl-dev libsqlite3-dev build-essential sqlite3
COPY Cargo.toml Cargo.lock ./
COPY src ./src
COPY migrations ./migrations
# Prepare the offline database and compile
RUN cat migrations/*.sql | sqlite3 build.db
ENV DATABASE_URL="sqlite://build.db"
RUN cargo build --release
# Stage 2: The Vulnerability-Free Runtime
FROM cgr.dev/chainguard/wolfi-base:latest
WORKDIR /app
# Install ONLY the absolute necessities
RUN apk add --no-cache sqlite ca-certificates
# Pull the compiled binary from Stage 1
COPY --from=builder /usr/src/app/target/release/markreply ./
COPY --from=builder /usr/src/app/migrations ./migrations
EXPOSE 3000
CMD ["./markreply"]

Wolfi executes as a secure nonroot user by default, adding an extra layer of runtime protection to the container.

🤖 Continuous Security Validation

A secure Dockerfile is great, but we needed to mathematically prove it stays secure on every single commit. We built an automated validation sequence into our GitHub Actions pipeline.

1. Static Analysis with Trivy

Before any image is published, it runs through AquaSecurity’s Trivy scanner. By switching to Chainguard, our vulnerability report instantly dropped from 62 to 0 vulnerabilities. If a Critical or High CVE ever slips in, the pipeline automatically fails and blocks the release.

2. Dynamic Analysis with OWASP ZAP

Static analysis only checks the OS. To secure the API itself, our pipeline spins up a live instance of the container and attacks it using the OWASP ZAP Baseline scanner.

To pass ZAP's strict penetration tests, we wrote custom Axum middleware to inject required security headers like X-Content-Type-Options: nosniff and Cache-Control: no-store on all API responses.

✨ The Takeaway

Securing a container doesn’t mean writing thousands of lines of custom security profiles. It means fundamentally rethinking what you allow inside your production environment. By combining Rust’s memory safety with a microscopic, purpose-built runtime like Wolfi, MarkReply delivers an enterprise-grade security posture right out of the box.