I’m using dockerized node, and in order to minimize the image as much as possible, I’m mixing between bookworm (Debian 12) and Alpine, in the following way:
FROM node:18.17.1-bookworm-slim AS build
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18.17.1-alpine AS production
COPY --chown=node:node --from=build /app/node_modules ./node_modules
COPY --chown=node:node --from=build /app/dist ./
COPY --chown=node:node --from=build /app/package*.json ./
USER node
EXPOSE 3000
CMD ["node", "src/app.js"]
What is the risk I’m taking here by building my node app in Debian and deploying it in Alpine?
2
Answers
None of the previous stages are included in the final image when you build a multi-stage image. However, it is strongly advised to use the same image for building the application as the production one, to keep things consistent. Since the operating systems are different, chances are the container will fail to start due to different OS being used for building and running the app.
For a multi-stage Node.js apps where there are build stages, I would recommend using the following config:
There are a couple of notable differences between Alpine- and Debian-based images. One of the most notable is that the shared system C library
libc.so
has a completely different implementation. Alpine images use the smaller musl libc, where Debian images use the more full-featured GNU libc. This in turn may have some implications on thenode
binary itself.If the
node_modules
tree contains any libraries that work by compiling C extensions, these may just break trying to copy them from a Debian base to an Alpine base, or vice versa. You’ll probably see some sort of weird load-time dependency, maybe something mentioning aGLIBC_2.30
version constraint or similar.Using a multi-stage build is fine here, but you should probably use the same base Linux distribution in every stage; especially don’t mix Alpine and anything else. If you are in the state where you potentially have compiled native extensions it may be important to use the exact same starting image in both stages (caveat: I know this matters for Python, less practical experience for Node).
You could use a shared base stage to only declare that base image once