hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Deploying Node.js Apps with Docker

Why Docker

  • The "Works on My Machine" Problem
  • Docker Concepts

Your First Dockerfile

  • Writing a Dockerfile
  • Multi-Stage Builds
  • The .dockerignore File

Running Containers

  • Running and Managing Containers
  • Environment Variables and Secrets
  • Health Checks

Multi-Container Apps

  • Docker Compose
  • Adding a Reverse Proxy
  • Persistent Data

Deploying to Production

  • Deploying to a VPS
  • HTTPS with Let's Encrypt
  • Zero-Downtime Deploys

CI/CD

  • Building Images in CI
  • Automated Deployment

Production Hardening

  • Container Security
  • Logging and Monitoring
  • Deployment Checklist and Capstone

Docker Concepts

The four concepts

Docker has four core concepts. Understanding them makes everything else click.

Images

An image is a blueprint. It contains the file system (your code, Node.js, OS libraries) and instructions for how to run the app. Images are read-only. You build an image once, and it never changes.

Think of an image like a class in programming. It defines what will exist, but it does not run anything.

# List images on your machine
docker images

Containers

A container is a running instance of an image. When you start a container from an image, Docker creates a writable layer on top of the read-only image, starts the process (your Node.js app), and isolates it from the host.

Think of a container like an object — an instance of the class. You can have multiple containers from the same image.

# Run a container from an image
docker run node:22-alpine node -e "console.log('Hello')"

# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

Layers

Images are built in layers. Each instruction in a Dockerfile creates a layer. Layers are cached — if nothing changed, Docker reuses the cached layer instead of rebuilding it.

Layer 1: FROM node:22-alpine        (the base OS + Node.js)
Layer 2: COPY package.json .        (your dependency list)
Layer 3: RUN npm ci                 (installed dependencies)
Layer 4: COPY . .                   (your application code)
Layer 5: CMD ["node", "dist/server.js"]  (how to start)

If you change your application code (layer 4) but not your dependencies (layers 2-3), Docker reuses the cached dependency layer. npm ci does not run again. This makes rebuilds fast.

The order of layers matters. Put things that change rarely (base image, dependencies) at the top and things that change often (your code) at the bottom.

Registries

A registry is where images are stored and shared. Docker Hub is the default public registry. GitHub Container Registry (ghcr.io), AWS ECR, and Google Artifact Registry are common alternatives.

# Pull an image from Docker Hub
docker pull node:22-alpine

# Push your image to a registry
docker push yourusername/myapp:latest

When you deploy, the server pulls the image from the registry. You do not copy files with scp — you push to a registry and pull from the server.

The workflow

1. Write a Dockerfile         (define the image)
2. docker build                (create the image)
3. docker push                 (upload to a registry)
4. docker pull (on the server) (download the image)
5. docker run (on the server)  (start the container)

This is the deployment pipeline. The next lessons implement each step.

The base image

Every Dockerfile starts with FROM, which specifies the base image. For Node.js apps:

node:22 — Full Debian-based image. ~350 MB. Has everything (build tools, python, git). Good for building.

node:22-slim — Debian without extras. ~80 MB. Good for production when you need some system libraries.

node:22-alpine — Alpine Linux-based. ~50 MB. Minimal. Some native modules (like sharp) need extra steps on Alpine.

For this course, we use node:22-alpine for production (small) and node:22 for the build stage (has build tools).

Exercises

Exercise 1: Run docker images and docker ps. What images and containers do you have?

Exercise 2: Run docker run -it node:22-alpine sh. You are now inside a Node.js container. Run node --version and ls /. Type exit to leave.

Exercise 3: Pull node:22 and node:22-alpine. Compare their sizes with docker images. (node:22 is ~350 MB, node:22-alpine is ~50 MB.)

What is the difference between a Docker image and a container?

← The "Works on My Machine" Problem Writing a Dockerfile →

© 2026 hectoday. All rights reserved.