recipes / container-images / distroless-image
Distroless instead of Alpine or Debian slim
Ship the Node.js runtime and your app, nothing else. No shell, no package manager, non-root.
The end goal for a production image is: the Node.js binary, its shared libraries, CA certificates, your code and its dependencies. Google’s distroless images are exactly that.
FROM gcr.io/distroless/nodejs24-debian12:nonroot
Why not Alpine or slim
- Alpine uses musl. Native modules built for glibc break, DNS behaves differently, and some performance issues only show up under load.
- Debian slim still ships a shell,
apt, and a hundred packages you never call. Every one of them is CVE surface and image size. - Distroless has no shell. An attacker who gets code execution has no
sh,curloraptto work with. Scanners report almost nothing.
What changes for you
ENTRYPOINTis already["node"]. WriteCMD ["src/server.js"].- No
RUN, nonpm. Install and build in a previous stage, thenCOPY --from. - Use the
:nonroottag. It runs as UID 65532 and satisfies restricted Pod Security Standards. - No
sleepbinary forpreStop. Use the Kubernetessleepaction instead ofexec. - Debugging: temporarily switch to the
:debugtag, which adds busybox, or usekubectl debugwith an ephemeral container.
Tags
| Tag | Runs as | Contains |
|---|---|---|
nodejs24-debian12 |
root | node |
nodejs24-debian12:nonroot |
nonroot | node |
nodejs24-debian12:debug |
root | node + busybox |
nodejs24-debian12:debug-nonroot |
nonroot | node + busybox |
Chainguard’s cgr.dev/chainguard/node images are an equivalent alternative
with the same properties.
See it applied
- examples/full/Dockerfile in the repository
- examples/full - every recipe applied to one app
Updated 2026-09-10 · tags: dockerfile, security, distroless · edit on GitHub