# Distroless instead of Alpine or Debian slim

> Ship the Node.js runtime and your app, nothing else. No shell, no package manager, non-root.

- Canonical: https://js-on-k8s.dev/recipes/container-images/distroless-image
- Site: JavaScript on Kubernetes (https://js-on-k8s.dev)
- Updated: 2026-09-10
- Part of: https://js-on-k8s.dev/recipes/container-images
- Tags: dockerfile, security, distroless
- Example: https://github.com/vojtechmares/js-on-k8s/tree/main/examples/full/Dockerfile
- Full example: https://github.com/vojtechmares/js-on-k8s/tree/main/examples/full

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.

```dockerfile
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`,
  `curl` or `apt` to work with. Scanners report almost nothing.

## What changes for you

- `ENTRYPOINT` is already `["node"]`. Write `CMD ["src/server.js"]`.
- No `RUN`, no `npm`. Install and build in a previous stage, then `COPY --from`.
- Use the `:nonroot` tag. It runs as UID 65532 and satisfies restricted Pod Security Standards.
- No `sleep` binary for `preStop`. Use the Kubernetes `sleep` action instead of `exec`.
- Debugging: temporarily switch to the `:debug` tag, which adds busybox, or use
  `kubectl debug` with 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.
