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?