feat: minimize gateway Docker image to reduce attack surface and pull size
@drew is already working on this.
Since Apr 29, 2026.
- Dominant language
- Rust
- Stars
- 8.7k
- Forks
- 1.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 253
Description
Problem Statement
The gateway runtime image is based on nvcr.io/nvidia/base/ubuntu:noble-20251013, a full Ubuntu userspace. While the multi-stage build already avoids shipping build tools, the final image still carries unnecessary OS packages, shells, and utilities that increase:
- Attack surface — a compromised container has access to package managers, shells, and system utilities that aid lateral movement.
- Image size — larger images slow cold-start pulls, especially in air-gapped or bandwidth-constrained environments.
- CVE noise — distro packages accumulate vulnerabilities unrelated to the gateway binary, increasing scanner findings and maintenance burden.
Proposed Design
1. Switch base image to distroless
Replace nvcr.io/nvidia/base/ubuntu:noble with gcr.io/distroless/cc-debian13:nonroot.
This image includes glibc and libgcc — sufficient for the dynamically-linked Rust binary (built with x86_64-unknown-linux-gnu / aarch64-unknown-linux-gnu targets) — but ships no shell, no package manager, and no unnecessary utilities. It is ~20 MB compressed vs ~80 MB for the Ubuntu base.
A follow-up issue can evaluate switching to static-debian13 with a musl-linked binary for an even smaller footprint (~2 MB base), but that requires target/toolchain changes and broader testing.
2. Remove unnecessary migrations COPY
The current Dockerfile copies crates/openshell-server/migrations/ into the final image. This is unnecessary — migrations are embedded into the binary at compile time via sqlx::migrate!("./migrations/sqlite") and sqlx::migrate!("./migrations/postgres") proc macros. Removing this COPY eliminates dead weight from the image.
3. Minimal final stage
FROM gcr.io/distroless/cc-debian13:nonroot AS gateway
COPY --from=gateway-binary /build/out/openshell-gateway /usr/local/bin/
ENTRYPOINT ["openshell-gateway"]
CMD ["--port", "8080"]
Optionally the openshell user (UID 1000) can be preserved.
CA certs are included in the distroless base. No other runtime files are needed.
Alternatives Considered
distroless/static-debian13— smallest possible (~2 MB) but requires switching tox86_64-unknown-linux-musland verifying all crate dependencies work under musl. Better as a follow-up.- Alpine-based image — smaller than Ubuntu but still ships a shell, package manager, and introduces musl quirks (DNS resolver, stack size defaults).
- Keep Ubuntu, remove packages — fragile (apt remove can break dep chains), doesn't eliminate shell or package manager, still large.
- Chainguard
cgr.dev/chainguard/cc-dynamic— signed and SBOM-attached but adds an external registry dependency.
Agent Investigation
Current state of deploy/docker/Dockerfile.images gateway stage:
- Multi-stage build already separates builder from runtime.
- Final stage:
FROM nvcr.io/nvidia/base/ubuntu:noble-20251013 AS gateway - Installs
ca-certificatesand upgradesgpgvat runtime. - Copies binary + migrations (migrations COPY is unnecessary — embedded via
sqlx::migrate!()). - Runs as
openshelluser (UID 1000). - Debug symbols already stripped (
strip = truein[profile.release]). - Binary is dynamically linked against glibc (
*-unknown-linux-gnutargets incross-build.sh).
Estimated compressed image size: current ~100+ MB → ~30-40 MB with cc-debian13 + removing dead COPY.
Checklist
- I've reviewed existing issues and the architecture docs
- This is a design proposal, not a "please build this" request
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.