You may not want to use Alpine. This blog post goes into depth but let me sum it up with two points:

  1. Alpine made some design choices that have some extremely rare edge cases that can cause failures and be very hard to diagnose. This arises from their choice of replacing the typical glibc with musl. Read the blog post if you want to know more. Suffice to say, unless you're running Kubernetes at a large scale this shouldn't concern you; lots of people run Alpine and never see issues.
  2. Now Alpine isn't the only option!

The four projects to look to here, Wolfi (an open source project), Red Hat's Universal Base Image Micro, Debian's slim variant, and Google's Distroless.

You would be set with any of these. We are going to focus on Distroless because it is currently the most popular but feel free to experiment!

"Distroless" is a bit of a lie as it still based on Debian, but to their point, they've stripped away essentially everything except what is 100% necessary to run your containers. This means you need to install everything you need to get running. It means no package manager. It means it is truly as barebones as it can get.

Let's build a Node.js distroless image.

# build stage
FROM node:20 AS node-builder
WORKDIR /build
COPY package-lock.json package.json ./
RUN npm ci
COPY . .

# runtime stage
FROM gcr.io/distroless/nodejs20
COPY --from=node-builder --chown=node:node /build /app
WORKDIR /app
CMD ["index.js"]

⛓️ Link to the Dockerfile

docker build -t my-distroless .
docker run -it -p 8080:8080 --name my-app --rm --init my-distroless

The size (according to my computer) was about 175MB, so not necessarily any smaller than Alpine, but it is indeed using a Debian-derivative Linux instead of Alpine which does exclude a class of rare-but-possible bugs! These days I tend to use Distroless images but honestly I'm fine with anything you choose here. Probably by the time you need something other than an Alpine image you will have 100x surpassed my knowledge and skills with containers or have a whole dev ops org to attend to these nuances.

One note with the Dockerfile: notice we just give it the Node.js file and not the Node.js command. The Distroless container locks it down so it can only run Node.js apps and cannot be given any other command. Just another way they are hyper-focused for security in production.