Start learning free

LESSON 50 OF 58 · FULL-STACK: SHIPPING IT

One Image

Practice

A Docker image packs everything a program needs to run into one file: Linux, Node.js, your code and its packages. The Dockerfile is the recipe. Docker runs it one line at a time, from top to bottom, and the image then runs the same way on any machine with Docker.

Vite has to build the storefront before the server can serve it. The server itself doesn't need Vite or the client's source code. A multi-stage Dockerfile builds in a first stage and copies only the result into the last stage. Only the last stage becomes the image, and the earlier stages are left behind. Here is the same idea for a docs site served by nginx:

FROM node:22-slim AS docs
WORKDIR /docs
COPY docs/ ./
RUN npm ci
RUN npm run build

FROM nginx:alpine
COPY --from=docs /docs/out /usr/share/nginx/html

Two more rules. First, copy the package files and install the packages before you copy the rest of the code. Docker reuses a line's result as long as its input hasn't changed, so editing a component doesn't install everything again. Second, don't run the server as root. The node image comes with a user called node, and USER node switches to it. Put a chown before it so node owns the folder the server writes to.

The check builds the image line by line, lists what the image holds, then runs it. When your Dockerfile is done, the check ends with:

> what the image holds (only the last stage: the stages above it are left behind)
  /app: package-lock.json, package.json
  /app/client/dist: assets/app-4f2a.js, index.html
  /app/node_modules: express, express-session
  /app/server: app.js, app.test.js, db.js, index.js, passwords.js
  left behind in the stage build: the client's code and its packages (react, …, vite)
> docker run -p 3000:3000 -e SESSION_SECRET=x7Kq9vR2mW4pL8sN shop
  node server/index.js starts as the user node, with NODE_ENV=production
  GET / -> 200, the file client/dist/index.html
  GET /assets/app-4f2a.js -> 200, the file client/dist/assets/app-4f2a.js
  GET /api/products -> 200, a JSON list of 8 products

Your task: the Dockerfile already has the start of the last stage (FROM, ENV NODE_ENV=production, WORKDIR /app and the copy of the package files) and its end (EXPOSE 3000 and CMD). At the top, add a stage named build that installs the client's packages and builds the storefront. Then fill in the middle of the last stage: the production packages, server/, client/dist from the stage build, and the user node.


Assignment
Follow the Coding Tutorial to write a two-stage Dockerfile. The build stage makes the storefront. The last stage holds only the server, its production packages and client/dist, and it runs as the user node.


Hint
A RUN only sees files that a COPY above it brought in. COPY --from=build takes files from that stage instead of from your project folder. Write the chown before USER node, because the commands after USER node run without root's rights.


STEP 1 OF 11
    Ctrl + Enter to check