Best practices for Docker image node version and .nvmrc

dockernode.js

If I'm building microservices using Node on Docker images – it's necessary to keep an idea of what version of Node I'm using.

The idea is – I'm going to be running Node locally in development – and then it still needs to work when running on a container.

If my Dockerfile looks like this:

FROM node:10.10-alpine

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install
# If you are building your code for production
# RUN npm install --only=production

# Bundle app source
COPY . .

EXPOSE 3001
CMD [ "npm", "start" ]

This is fine – except my local environment doesn't know which version of node I'm using.

Now I could just put 10.10 in my .nvmrc file.

But then I have to remember to keep these in sync. Is there a better way to do it? Is there a node:alpine that respects .nvmrc?

Best Answer

The primary point of Docker is isolating what happens inside a container from what happens outside (be that other running programs, other containers, or in your .nvmrc). Docker images tend to be extremely spare, containing just the components and data they need, explicitly specified in the Dockerfile. Any configurations required need to be explicitly set, say by environment variables or copied-over config files. An isolated environment with all requirements bundled / encapsulated—that's how Docker gets workloads with different (even highly incompatible) requirements running side-by-side.

Node Version Manager comes from a very different worldview. One that believes you might have multiple versions of node installed, and might freely switch back and forth between them. There's nothing wrong with that per se, especially for a developer workstation, but it's almost inimical to the Docker philosophy. The number of Docker configs that have >1 node version installed is going to be vanishingly small, and they'd be extremely unusual, especially those built on the minimalistic Alpine Linux or for the minimalist strategy of microservices.

If you need to switch between node versions on your development station, so be it. But recommend you do not attempt to map that workflow / multiple installed versions strategy into your Docker images. It's going to be a force-fit at best, and will rob you of the very precision and minimalism that makes Docker work so well. Instead, consider your Docker images the targeted, specific outcome of your build process. The specific versions of node or other resources can be "burned into" the image as environment variables or config files if you need to query the current version at runtime. If you need to build several variants, each targeting a separate node version, that's quite doable. Docker has a rich tagging ability; it's commonly used for just that purpose.