Table of Contents
- Course Overview
- Why Use Docker as a Developer?
- Setting Up Your Development Environment
- Connecting Source Code to a Container
- Building Custom Images with Dockerfile
- Communication Between Docker Containers
- Managing Containers with Docker Compose
- Reference Tables
- Architecture Diagrams
1. Course Overview
This course covers using Docker in a web development context:
- Why Docker improves developer productivity and guarantees consistency across environments
- Docker Desktop: installation and configuration
- Images and containers: the layered file system
- Volumes: persisting data and connecting source code to a container
- Dockerfile: creating custom images, multi-stage builds
- Docker Hub: publishing and sharing images
- Container networking: making containers communicate
- Docker Compose: orchestrating multiple services with a single YAML file
2. Why Use Docker as a Developer?
What is Docker?
Docker is a lightweight, open, and secure platform that simplifies building, deploying, and running applications.
“Docker is a shipping container system for code.”
| Concept | Description |
|---|---|
| Image | Read-only template composed of layered file systems. Used to share common files and create container instances. |
| Container | Running instance of an image. Adds a thin writable layer on top of the image. |
Benefits for Developers
- Accelerate developer onboarding — share the complete environment via a few commands
- Eliminate app conflicts — different Node.js, Python, .NET versions coexist without conflicts
- Environment consistency — a container that works locally works the same way in staging and production
- Ship software faster — no hours setting up environments to test a feature
Images vs Containers
Image (read-only)
├── Layer 1: OS base (Alpine, Ubuntu, etc.)
├── Layer 2: Runtime (Node.js, .NET, etc.)
├── Layer 3: Application dependencies
└── Layer 4: Source code / binaries
Container (running)
└── Thin writable layer (logs, temp files)
└── [points to image layers]
A container is not a virtual machine — it shares the host kernel via Docker Engine, making it much lighter.
3. Setting Up Your Development Environment
Installing Docker Desktop
Docker Desktop includes:
- Docker Engine — the container runtime
- Docker CLI Client — command-line interface
- Docker Compose — multi-container orchestration
On Windows
- WSL 2 (recommended): Windows Subsystem for Linux
- Hyper-V: alternative if WSL isn’t available
Configure WSL via %USERPROFILE%\.wslconfig:
[wsl2]
memory=8GB
processors=4
Essential Docker Commands
docker pull nginx:alpine # Pull an image from Docker Hub
docker run -p 8080:80 nginx:alpine # Start a container
docker ps # List running containers
docker ps -a # List all containers (including stopped)
docker images # List local images
docker rm <container_id> # Remove a container
docker rmi <image_id> # Remove an image
docker <command> --help # Get help for any command
4. Connecting Source Code to a Container
The Layered File System
Image (read-only layers)
┌─────────────────────────────┐
│ Layer 4: Source code │
├─────────────────────────────┤
│ Layer 3: npm packages │
├─────────────────────────────┤
│ Layer 2: Node.js runtime │
├─────────────────────────────┤
│ Layer 1: Alpine Linux base │
└─────────────────────────────┘
↓ docker run
Container (adds thin R/W layer)
┌─────────────────────────────┐
│ Writable layer (logs, etc.) │ ← deleted when container is removed
└─────────────────────────────┘
Key points:
- Image layers are immutable (read-only)
- The container adds a thin R/W layer on top
- If the container is deleted → the R/W layer disappears with it
- Layers are cached during build → rebuilds are fast if early layers haven’t changed
Volumes and Containers
A volume is a special directory on the Docker host that containers can use to persist data outside the container.
Option 1: Docker-managed volume
docker run -p 8080:3000 -v /var/www node
docker inspect <container_name> # Find host path
Option 2: Custom volume (bind mount)
# Linux / Mac / WSL:
docker run -p 8080:3000 -v $(pwd):/var/www node
# Windows PowerShell:
docker run -p 8080:3000 -v ${PWD}:/var/www node
Remove container and its volume
docker rm -v <container_name_or_id>
Development Flow with Volumes
# Node.js Express in a container
docker run -p 8080:3000 \
-v $(pwd):/var/www \
-w /var/www \
node \
npm start
# ASP.NET Core in a container
docker run -p 8080:5000 \
-v $(pwd):/app \
-w /app \
-e ASPNETCORE_URLS="http://*:5000" \
-e DOTNET_USE_POLLING_FILE_WATCHER=1 \
mcr.microsoft.com/dotnet/sdk \
dotnet watch run
Note:
ASPNETCORE_URLS="http://*:5000"uses*instead oflocalhostso port forwarding works correctly from the container.
5. Building Custom Images with Dockerfile
Dockerfile Anatomy
# FROM: base image (foundation)
FROM node:alpine
# LABEL: metadata
LABEL author="Web Developer"
# ENV: environment variables
ENV NODE_ENV=production
ENV PORT=3000
# WORKDIR: working directory in container
WORKDIR /var/www
# COPY: copy files from host to image
COPY package*.json ./
COPY . .
# RUN: execute commands during build
RUN npm install --only=production
# EXPOSE: document the port the app listens on
EXPOSE 3000
# ENTRYPOINT: main command
ENTRYPOINT ["node", "server.js"]
Build an image from a Dockerfile
# Syntax: docker build -t <tag> <build_context>
docker build -t my-node-app:1.0 .
# With a Dockerfile at a custom path
docker build -t my-node-app:1.0 -f ./docker/Dockerfile .
The
.at the end is crucial — it defines the build context.
Custom Node.js Image
FROM node:alpine
LABEL author="Web Developer"
ENV NODE_ENV=production
ENV PORT=3000
WORKDIR /var/www
COPY package*.json ./
RUN npm install --only=production
COPY . .
EXPOSE 3000
ENTRYPOINT ["node", "server.js"]
docker build -t myusername/nodeapp:1.0 .
docker run -d -p 8080:3000 --name my-node-app myusername/nodeapp:1.0
docker logs my-node-app
Multi-stage Dockerfile
┌──────────────────────────────────────┐
│ Stage 1: BUILD │
│ Image: mcr.microsoft.com/dotnet/sdk │
│ • Restore packages │
│ • Compile code │
│ • Publish binaries │
└──────────────────────┬───────────────┘
│ COPY --from=build
▼
┌──────────────────────────────────────┐
│ Stage 2: FINAL (production) │
│ Image: mcr.microsoft.com/dotnet/aspnet│
│ • Only binaries │
│ • Lightweight, no SDK │
└──────────────────────────────────────┘
ASP.NET Core multi-stage:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /build
COPY *.csproj ./
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
ENV ASPNETCORE_URLS="http://*:5000"
COPY --from=build /app/publish .
EXPOSE 5000
ENTRYPOINT ["dotnet", "MyApp.dll"]
Node.js multi-stage:
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS production
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --only=production
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
| Advantage | Description |
|---|---|
| Lightweight image | Final image contains no SDK or build tools |
| Security | Smaller attack surface (no debug tools in prod) |
| Simplified CI/CD | Build server only needs Docker — no SDK installed |
Publishing an Image to Docker Hub
docker login
docker tag myapp:1.0 myusername/myapp:1.0
docker push myusername/myapp:1.0
docker pull myusername/myapp:1.0
Other Registries
| Registry | Tag Format |
|---|---|
| Docker Hub | username/image:tag |
| Azure Container Registry | myacr.azurecr.io/image:tag |
| AWS ECR | 123456789.dkr.ecr.us-east-1.amazonaws.com/image:tag |
| GCP Artifact Registry | us-central1-docker.pkg.dev/project/repo/image:tag |
6. Communication Between Docker Containers
Container Networks
By default, containers are isolated and cannot communicate. Place them in the same Docker network to enable communication.
Bridge Network in Action
# 1. Create a custom bridge network
docker network create --driver bridge my-network
# 2. Start MongoDB container on this network
docker run -d \
--net=my-network \
--name=mongodb \
mongo
# 3. Start Node.js container on the same network
docker run -d \
-p 8080:3000 \
--net=my-network \
--name=nodeapp \
my-node-image
# Node.js container can now reach MongoDB via the name "mongodb"
# mongodb://mongodb:27017/mydb
docker network ls # List available networks
docker network inspect my-network # Inspect a network
docker network connect my-network my-container # Connect existing container
7. Managing Containers with Docker Compose
docker-compose.yml File
version: "3.9"
services:
node:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:3000"
networks:
- webnet
environment:
- NODE_ENV=development
volumes:
- .:/var/www/app
- /var/www/app/node_modules
depends_on:
- mongodb
mongodb:
image: mongo
ports:
- "27017:27017"
networks:
- webnet
volumes:
- mongo-data:/data/db
networks:
webnet:
driver: bridge
volumes:
mongo-data:
Complete Example: Frontend + Backend + Database
version: "3.9"
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- frontend
- api
networks:
- webnet
frontend:
build:
context: ./frontend
environment:
- NODE_ENV=development
volumes:
- ./frontend:/app
- /app/node_modules
networks:
- webnet
api:
build:
context: ./api
environment:
- NODE_ENV=development
- MONGO_URI=mongodb://mongodb:27017/appdb
volumes:
- ./api:/app
- /app/node_modules
depends_on:
- mongodb
networks:
- webnet
mongodb:
image: mongo:7
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=secret
volumes:
- mongo-data:/data/db
networks:
- webnet
networks:
webnet:
driver: bridge
volumes:
mongo-data:
Docker Compose Commands
# Build all service images
docker compose build
# Start all services (background with -d)
docker compose up
docker compose up -d
# Stop and remove containers, networks, anonymous volumes
docker compose down
# Stop and remove ALSO named volumes
docker compose down -v
# View logs from all services
docker compose logs
# Stream logs from a specific service
docker compose logs -f node
# List containers managed by Compose
docker compose ps
# Rebuild images AND restart
docker compose up --build
# Execute a command in a service
docker compose exec node sh
# Stop services without removing containers
docker compose stop
# Restart a specific service
docker compose restart node
8. Reference Tables
Essential Docker Commands
| Command | Description |
|---|---|
docker pull <image> | Pull image from registry |
docker run <image> | Start a container |
docker run -d | Run in detached mode (background) |
docker run -p <host>:<container> | Port mapping |
docker run -v <host>:<container> | Volume mount |
docker run -w <path> | Set working directory |
docker run --name <name> | Assign name |
docker run --net <network> | Connect to network |
docker ps | List running containers |
docker ps -a | List all containers |
docker stop <container> | Stop container |
docker rm <container> | Remove container |
docker rm -v <container> | Remove container + volume |
docker images | List local images |
docker rmi <image> | Remove image |
docker build -t <tag> . | Build image from Dockerfile |
docker push <image> | Push to registry |
docker logs <container> | View logs |
docker exec -it <container> sh | Shell into container |
docker inspect <container> | Detailed info |
Dockerfile Instructions
| Instruction | Description | Example |
|---|---|---|
FROM | Base image | FROM node:alpine |
LABEL | Metadata | LABEL author="Dev" |
ENV | Environment variable | ENV PORT=3000 |
ARG | Build argument | ARG VERSION=1.0 |
WORKDIR | Working directory | WORKDIR /app |
COPY | Copy files | COPY . . |
RUN | Execute command (creates layer) | RUN npm install |
EXPOSE | Document port | EXPOSE 3000 |
ENTRYPOINT | Main command | ENTRYPOINT ["node", "server.js"] |
CMD | Default arguments | CMD ["npm", "start"] |
Volume Types
| Type | Syntax | Description |
|---|---|---|
| Docker-managed | -v /container/path | Docker manages host location |
| Bind mount | -v /host/path:/container/path | You specify host path |
| Named volume | -v vol-name:/container/path | Named, Docker-managed |
| Anonymous | -v /container/path | Unnamed temporary volume |
Network Types
| Type | Description | Use Case |
|---|---|---|
| bridge | Default isolated network | Containers on same host |
| host | Container shares host network | Maximum performance (Linux) |
| none | No networking | Complete isolation |
| overlay | Multi-host (Swarm/K8s) | Distributed clusters |
9. Architecture Diagrams
Development Flow with Volumes
graph LR
subgraph Host["Host Machine"]
SRC["Source code\n(working directory)"]
end
subgraph Container["Node.js Container"]
direction TB
APP["/var/www/app\n(bind mount)"]
MODS["/var/www/app/node_modules\n(anonymous volume)"]
SERVER["node server.js\n(running)"]
end
DEV["Developer\nedits files"] --> SRC
SRC -->|"docker run -v $(pwd):/var/www/app"| APP
APP --> SERVER
SERVER -->|"Auto-reload\n(nodemon/watch)"| SERVER
SERVER -->|"localhost:8080"| BROWSER["Browser"]
Multi-stage Build Flow
flowchart LR
subgraph Stage1["Stage 1: BUILD\n(node:20 or dotnet/sdk)"]
DEPS["npm install / dotnet restore"]
COMPILE["npm run build / dotnet publish"]
ARTIFACTS["Build artifacts\n(/app/dist or /app/publish)"]
DEPS --> COMPILE --> ARTIFACTS
end
subgraph Stage2["Stage 2: PRODUCTION\n(node:20-alpine or dotnet/aspnet)"]
COPY_ONLY["COPY --from=build\n(artifacts only)"]
PROD_DEPS["npm ci --only=production"]
FINAL["Final image\n(lightweight, no SDK)"]
COPY_ONLY --> PROD_DEPS --> FINAL
end
REGISTRY["Docker Hub / ACR / ECR"]
SRC["Source code"] --> Stage1
ARTIFACTS -->|"COPY --from=build"| Stage2
FINAL -->|"docker push"| REGISTRY
Search Terms
docker · web · developers · containerization · containers · kubernetes · dockerfile · volume · commands · container · custom · development · flow · image · volumes · compose · essential · images · multi-stage · network · option · types