Node.js Docker
Docker packages your app and everything it needs into one container. The container runs the same way on your laptop, a teammate's computer, and a cloud server. The phrase "it works on my machine" disappears.
The Shipping Container Idea
Cargo ships carry standard steel containers. The ship does not care whether a container holds toys or fruit. Docker applies this idea to software. Every app travels in a standard box that runs anywhere Docker runs.
+-----------------------------+
| Container |
| +-----------------------+ |
| | Your Node.js code | |
| | node_modules | |
| | Node.js runtime | |
| | System libraries | |
| +-----------------------+ |
+-----------------------------+
runs on
+-----------------------------+
| Docker on any computer |
+-----------------------------+
Key Terms
- Image is a read-only recipe that holds your app and its tools.
- Container is a running copy of an image.
- Dockerfile is a text file that lists the steps to build an image.
- Registry is an online store for images, such as Docker Hub.
Dockerfile --build--> Image --run--> Container (recipe) (packaged) (running app)
Prepare a Sample App
// index.js
const express = require("express");
const app = express();
app.get("/", (req, res) => res.send("Hello from Docker!"));
const port = process.env.PORT || 3000;
app.listen(port, () => console.log("Listening on " + port));Write the Dockerfile
Create a file named Dockerfile with no extension.
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]Read Each Line
- FROM picks the base image. Alpine versions stay small.
- WORKDIR sets the folder inside the container.
- COPY package*.json copies the dependency lists.
- RUN npm ci installs the libraries.
- COPY . . copies the rest of your code.
- EXPOSE documents the port.
- CMD sets the start command.
Why the Order Matters
Docker saves each step as a layer and reuses unchanged layers. Copying package files before the code lets Docker skip the install step when only your code changes. Builds finish faster.
Layer 1: node base image (cached) Layer 2: package files (cached) Layer 3: npm install (cached) Layer 4: your code (rebuilt after each code change)
Ignore Unneeded Files
Create a .dockerignore file.
node_modules
npm-debug.log
.env
.gitThis file keeps secrets and heavy folders out of the image.
Build and Run
docker build -t my-node-app .
docker run -d -p 8080:3000 --name web my-node-appThe flag -p 8080:3000 maps port 8080 on your computer to port 3000 in the container. Open http://localhost:8080 to see the message.
Browser --> localhost:8080 --> [ Docker ] --> container port 3000 --> app
Useful Commands
docker ps // list running containers
docker logs web // read app logs
docker stop web // stop the container
docker rm web // remove the container
docker images // list imagesPass Settings
docker run -d -p 8080:3000 -e PORT=3000 -e NODE_ENV=production my-node-appPass secrets at run time. Never write them into the Dockerfile.
Run Several Services with Compose
Docker Compose starts an app and its database together.
services:
web:
build: .
ports:
- "8080:3000"
environment:
- MONGO_URL=mongodb://db:27017/shop
depends_on:
- db
db:
image: mongo:7
volumes:
- dbdata:/data/db
volumes:
dbdata:
docker compose up -d
+------------------+ +------------------+ | web (Node.js) | ----> | db (MongoDB) | | port 8080 | | volume: dbdata | +------------------+ +------------------+
A volume keeps database files after the container stops.
Good Habits
- Choose small base images.
- Pin a version tag instead of using latest.
- Run the app as a non-root user.
- Keep one main process in each container.
Practice Tasks
- Containerize a small Express app and open it in your browser.
- Add a .dockerignore file and compare the image size.
- Write a Compose file that starts your app and a database.
