Running as non-root

Containers run as root unless told otherwise. With USER in the Dockerfile, a compromised app only has the rights of an ordinary user.

How it works

Many images ship a user: node in the node images, nobody almost everywhere, nonroot in distroless; otherwise create one with adduser. Put USER after the steps that need root, like installing packages, and give the user the folders it writes to with COPY --chown or chown.

What breaks

Switching to USER without fixing ownership gives "EACCES: permission denied" on the folder the app writes to. A named volume takes the owner of the image folder the first time it is mounted, so create that folder with the right owner in the image.

In a Dockerfile

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN mkdir -p /app/uploads && chown node:node /app/uploads
USER node
CMD ["node", "src/server.js"]

See it break, then fix it

Write the Dockerfile or the compose file, type the docker commands, and watch a simulated server run them through reboots, upgrades and traffic. Free to start.