Master the fundamentals of Docker images and containers - learn to pull, run, manage, and inspect containers while understanding image layers and the container lifecycle.
Docker images and containers are the fundamental building blocks of containerization. An image is a read-only template containing application code, libraries, and dependencies, while a container is a running instance of an image. Understanding how these components work together is essential for effective Docker usage. In this lesson, you will learn how to work with Docker images from registries, run and manage containers, and use Docker commands to inspect and troubleshoot containerized applications.
Docker images are built using a layered filesystem that provides efficiency, reusability, and fast distribution:
Understanding Image Layers:
Each Docker image consists of multiple read-only layers stacked on top of each other:
1┌─────────────────────────────────────┐
2│ Layer 4: Application code │ ← Your application
3├─────────────────────────────────────┤
4│ Layer 3: npm install dependencies │ ← Dependencies
5├─────────────────────────────────────┤
6│ Layer 2: Node.js runtime │ ← Runtime
7├─────────────────────────────────────┤
8│ Layer 1: Base OS (Alpine Linux) │ ← Base image
9└─────────────────────────────────────┘How Layers Work:
Layer Benefits:
1# View image layers
2docker image history nginx:latest
3
4# Output shows each layer and its size:
5IMAGE CREATED CREATED BY SIZE
6605c77e624dd 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
7<missing> 2 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
8<missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
9<missing> 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
10<missing> 2 weeks ago /bin/sh -c set -x && groupadd --system -… 62.1MB
11<missing> 2 weeks ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bookworm 0B
12...Layer Sharing:
Multiple images can share common base layers:
1# Both images share the same node:18-alpine base layer
2docker pull node:18-alpine
3docker pull node:20-alpine
4
5# Check storage efficiency
6docker system dfImage Digests:
1# View image digest (immutable identifier)
2docker images --digests
3
4# Pull specific image by digest
5docker pull nginx@sha256:abc123...Docker Hub is the default public registry containing millions of images. Learn to effectively search, pull, and manage images:
Searching for Images:
1# Search Docker Hub from command line
2docker search nginx
3
4# Search with filters
5docker search --filter is-official=true nginx
6docker search --filter stars=100 node
7
8# Output:
9NAME DESCRIPTION STARS OFFICIAL
10nginx Official build of Nginx 18234 [OK]Image Naming Convention:
1[registry/][username/]repository[:tag][@digest]
2
3Examples:
4- nginx # Official image, latest tag
5- nginx:1.25 # Specific version
6- nginx:1.25-alpine # Version with variant
7- mycompany/myapp:v1.0.0 # User/org namespace
8- gcr.io/project/image:tag # Google Container Registry
9- 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:latest # AWS ECRPulling Images:
1# Pull latest version
2docker pull nginx
3
4# Pull specific tag
5docker pull nginx:1.25-alpine
6
7# Pull from different registry
8docker pull gcr.io/google-containers/nginx:latest
9
10# Pull all tags for an image
11docker pull --all-tags nginx
12
13# View pull progress
14docker pull ubuntu:22.04
15# Output:
1622.04: Pulling from library/ubuntu
17aece8493d397: Pull complete
18Digest: sha256:abc123...
19Status: Downloaded newer image for ubuntu:22.04Managing Local Images:
1# List all images
2docker images
3docker image ls
4
5# List with formatting
6docker images --format "table {{.Repository}} {{.Tag}} {{.Size}}"
7
8# Filter images
9docker images --filter "dangling=true" # Untagged images
10docker images --filter "reference=nginx" # Images matching pattern
11
12# Remove images
13docker rmi nginx:latest
14docker image rm nginx:1.25
15
16# Remove all unused images
17docker image prune
18
19# Remove ALL unused images (including tagged)
20docker image prune -a
21
22# Force remove (even if containers exist)
23docker rmi -f nginx:latestImage Tagging:
1# Tag an existing image
2docker tag nginx:latest myregistry.com/nginx:v1.0.0
3
4# Tag for pushing to Docker Hub
5docker tag myapp:latest username/myapp:v1.0.0
6
7# Multiple tags for same image
8docker tag myapp:latest myapp:v1.0.0
9docker tag myapp:latest myapp:stableLearn the various ways to run containers with different configurations:
Basic Container Execution:
1# Run a simple container
2docker run nginx
3
4# Run in detached mode (background)
5docker run -d nginx
6
7# Run with custom name
8docker run -d --name my-nginx nginx
9
10# Run interactively
11docker run -it ubuntu bash
12
13# Run and automatically remove when stopped
14docker run --rm nginxPort Mapping:
1# Map container port 80 to host port 8080
2docker run -d -p 8080:80 nginx
3
4# Map to random host port
5docker run -d -P nginx
6
7# Map multiple ports
8docker run -d -p 8080:80 -p 8443:443 nginx
9
10# Bind to specific interface
11docker run -d -p 127.0.0.1:8080:80 nginx
12
13# View port mappings
14docker port my-nginxEnvironment Variables:
1# Set single variable
2docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql
3
4# Set multiple variables
5docker run -d \
6 -e MYSQL_ROOT_PASSWORD=secret \
7 -e MYSQL_DATABASE=myapp \
8 -e MYSQL_USER=appuser \
9 mysql
10
11# Load from file
12docker run -d --env-file ./env.list mysql
13
14# env.list format:
15# MYSQL_ROOT_PASSWORD=secret
16# MYSQL_DATABASE=myappResource Constraints:
1# Limit memory
2docker run -d --memory=512m nginx
3
4# Limit CPU
5docker run -d --cpus=0.5 nginx
6
7# Combined limits
8docker run -d \
9 --memory=512m \
10 --memory-swap=1g \
11 --cpus=1.5 \
12 nginx
13
14# CPU shares (relative weight)
15docker run -d --cpu-shares=512 nginxRestart Policies:
1# Never restart (default)
2docker run -d --restart=no nginx
3
4# Always restart
5docker run -d --restart=always nginx
6
7# Restart on failure (with max attempts)
8docker run -d --restart=on-failure:5 nginx
9
10# Restart unless manually stopped
11docker run -d --restart=unless-stopped nginxWorking Directory and User:
1# Set working directory
2docker run -d -w /app node:18 npm start
3
4# Run as specific user
5docker run -d --user 1000:1000 nginx
6
7# Run as non-root
8docker run -d --user node node:18 node app.jsEffective debugging requires understanding how to inspect container state, logs, and configuration:
Viewing Container Status:
1# List running containers
2docker ps
3
4# List all containers (including stopped)
5docker ps -a
6
7# List with custom format
8docker ps --format "table {{.ID}} {{.Names}} {{.Status}} {{.Ports}}"
9
10# Show only container IDs
11docker ps -q
12
13# Filter containers
14docker ps --filter "status=running"
15docker ps --filter "name=nginx"
16docker ps --filter "ancestor=nginx:latest"Container Logs:
1# View logs
2docker logs my-nginx
3
4# Follow logs in real-time
5docker logs -f my-nginx
6
7# Show timestamps
8docker logs -t my-nginx
9
10# Tail last N lines
11docker logs --tail 100 my-nginx
12
13# Logs since specific time
14docker logs --since 2024-01-01T00:00:00 my-nginx
15docker logs --since 10m my-nginx # Last 10 minutes
16
17# Combined options
18docker logs -f --tail 50 -t my-nginxContainer Inspection:
1# Full container details (JSON)
2docker inspect my-nginx
3
4# Specific information using Go templates
5docker inspect --format='{{.State.Status}}' my-nginx
6docker inspect --format='{{.NetworkSettings.IPAddress}}' my-nginx
7docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-nginx
8
9# View environment variables
10docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' my-nginx
11
12# View mounted volumes
13docker inspect --format='{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}' my-nginxExecuting Commands in Running Containers:
1# Run command in running container
2docker exec my-nginx ls -la /etc/nginx
3
4# Interactive shell
5docker exec -it my-nginx bash
6
7# Execute as different user
8docker exec -u root my-nginx apt update
9
10# Set environment variable for exec
11docker exec -e DEBUG=true my-nginx ./debug-script.sh
12
13# Working directory for exec
14docker exec -w /var/log my-nginx ls -laDebugging Techniques:
1# View container processes
2docker top my-nginx
3
4# View resource usage
5docker stats my-nginx
6
7# View file changes in container
8docker diff my-nginx
9# Output:
10# A /var/log/nginx/access.log (Added)
11# C /etc (Changed)
12# D /tmp/test (Deleted)
13
14# Copy files from container
15docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf
16
17# Copy files to container
18docker cp ./custom.conf my-nginx:/etc/nginx/conf.d/
19
20# View container events
21docker events --filter container=my-nginxUnderstanding the container lifecycle is crucial for effective container management:
Container States:
1┌─────────────────────────────────────────────────────────────┐
2│ Container Lifecycle │
3├─────────────────────────────────────────────────────────────┤
4│ │
5│ ┌──────────┐ docker run ┌──────────┐ │
6│ │ Image │ ───────────────► │ Created │ │
7│ └──────────┘ └────┬─────┘ │
8│ │ │
9│ docker start │
10│ │ │
11│ ▼ │
12│ ┌──────────┐ │
13│ docker restart │ Running │ ◄───────┐ │
14│ │ └────┬─────┘ │ │
15│ │ │ │ │
16│ │ docker stop/kill │ │
17│ │ │ │ │
18│ │ ▼ │ │
19│ │ ┌──────────┐ │ │
20│ └──────────────► │ Stopped │─────────┘ │
21│ └────┬─────┘ │
22│ │ │
23│ docker rm │
24│ │ │
25│ ▼ │
26│ ┌──────────┐ │
27│ │ Removed │ │
28│ └──────────┘ │
29│ │
30└─────────────────────────────────────────────────────────────┘Lifecycle Commands:
1# Create container without starting
2docker create --name my-app nginx
3
4# Start a stopped/created container
5docker start my-app
6
7# Stop a running container (graceful - SIGTERM)
8docker stop my-app
9
10# Stop with custom timeout
11docker stop -t 30 my-app # Wait 30 seconds before SIGKILL
12
13# Force stop (SIGKILL)
14docker kill my-app
15
16# Restart container
17docker restart my-app
18
19# Pause container (freeze processes)
20docker pause my-app
21
22# Unpause container
23docker unpause my-app
24
25# Remove stopped container
26docker rm my-app
27
28# Force remove running container
29docker rm -f my-app
30
31# Remove container and its volumes
32docker rm -v my-appBatch Operations:
1# Stop all running containers
2docker stop $(docker ps -q)
3
4# Remove all stopped containers
5docker container prune
6
7# Remove all containers (running and stopped)
8docker rm -f $(docker ps -aq)
9
10# Remove containers older than 24h
11docker container prune --filter "until=24h"Container Commit (Creating Images from Containers):
1# Make changes in a container
2docker run -it --name my-ubuntu ubuntu bash
3# Inside container: apt update && apt install -y vim
4
5# Commit changes to new image
6docker commit my-ubuntu my-custom-ubuntu:v1.0
7
8# Commit with author and message
9docker commit \
10 --author "John Doe <[email protected]>" \
11 --message "Added vim editor" \
12 my-ubuntu my-custom-ubuntu:v1.0
13
14# View commit history
15docker history my-custom-ubuntu:v1.0Export and Import:
1# Export container filesystem to tar
2docker export my-container > container-backup.tar
3
4# Import tar as new image
5docker import container-backup.tar my-imported-image:latest
6
7# Save image to tar (preserves layers)
8docker save -o nginx-backup.tar nginx:latest
9
10# Load image from tar
11docker load -i nginx-backup.tarBuild a practical workflow deploying a web application with database, demonstrating all container management concepts.
1#!/bin/bash
2# Complete Docker Container Workflow
3# Deploy a Node.js application with PostgreSQL database
4
5set -e # Exit on error
6
7echo "=============================================="
8echo "Docker Container Management - Complete Workflow"
9echo "=============================================="
10
11# Clean up any existing containers
12cleanup() {
13 echo "\n=== Cleaning up previous deployment ==="
14 docker rm -f app-postgres app-backend app-frontend 2>/dev/null || true
15 docker network rm app-network 2>/dev/null || true
16}
17
18cleanup
19
20# Step 1: Create a custom network
21echo "\n=== Step 1: Creating Docker Network ==="
22docker network create app-network
23docker network ls --filter name=app-network
24
25# Step 2: Pull required images
26echo "\n=== Step 2: Pulling Docker Images ==="
27docker pull postgres:15-alpine
28docker pull node:18-alpine
29docker pull nginx:alpine
30
31# Verify images
32docker images --filter reference='postgres*' --filter reference='node*' --filter reference='nginx*'
33
34# Step 3: Run PostgreSQL database
35echo "\n=== Step 3: Starting PostgreSQL Database ==="
36docker run -d \
37 --name app-postgres \
38 --network app-network \
39 -e POSTGRES_USER=appuser \
40 -e POSTGRES_PASSWORD=apppassword \
41 -e POSTGRES_DB=myappdb \
42 -v pgdata:/var/lib/postgresql/data \
43 --health-cmd="pg_isready -U appuser -d myappdb" \
44 --health-interval=10s \
45 --health-timeout=5s \
46 --health-retries=5 \
47 --restart unless-stopped \
48 postgres:15-alpine
49
50echo "Waiting for PostgreSQL to be healthy..."
51until docker inspect --format='{{.State.Health.Status}}' app-postgres | grep -q healthy; do
52 echo " Waiting..."
53 sleep 2
54done
55echo "PostgreSQL is healthy!"
56
57# Step 4: Create Node.js backend application
58echo "\n=== Step 4: Creating Backend Application ==="
59mkdir -p /tmp/docker-demo/backend
60cat > /tmp/docker-demo/backend/server.js << 'EOF'
61const http = require('http');
62const { Pool } = require('pg');
63
64const pool = new Pool({
65 host: process.env.DB_HOST || 'app-postgres',
66 port: process.env.DB_PORT || 5432,
67 user: process.env.DB_USER || 'appuser',
68 password: process.env.DB_PASSWORD || 'apppassword',
69 database: process.env.DB_NAME || 'myappdb',
70});
71
72const server = http.createServer(async (req, res) => {
73 if (req.url === '/health') {
74 res.writeHead(200, { 'Content-Type': 'application/json' });
75 res.end(JSON.stringify({ status: 'healthy' }));
76 return;
77 }
78
79 if (req.url === '/api/status') {
80 try {
81 const result = await pool.query('SELECT NOW() as time, version() as version');
82 res.writeHead(200, { 'Content-Type': 'application/json' });
83 res.end(JSON.stringify({
84 status: 'connected',
85 database: result.rows[0]
86 }));
87 } catch (err) {
88 res.writeHead(500, { 'Content-Type': 'application/json' });
89 res.end(JSON.stringify({ error: err.message }));
90 }
91 return;
92 }
93
94 res.writeHead(200, { 'Content-Type': 'application/json' });
95 res.end(JSON.stringify({
96 message: 'Backend API',
97 endpoints: ['/health', '/api/status']
98 }));
99});
100
101const PORT = process.env.PORT || 3000;
102server.listen(PORT, () => {
103 console.log(\`Backend server running on port \${PORT}\`);
104});
105EOF
106
107cat > /tmp/docker-demo/backend/package.json << 'EOF'
108{
109 "name": "docker-demo-backend",
110 "version": "1.0.0",
111 "main": "server.js",
112 "dependencies": {
113 "pg": "^8.11.0"
114 },
115 "scripts": {
116 "start": "node server.js"
117 }
118}
119EOF
120
121# Run backend container
122echo "\n=== Step 5: Starting Backend Container ==="
123docker run -d \
124 --name app-backend \
125 --network app-network \
126 -e DB_HOST=app-postgres \
127 -e DB_USER=appuser \
128 -e DB_PASSWORD=apppassword \
129 -e DB_NAME=myappdb \
130 -e PORT=3000 \
131 -p 3000:3000 \
132 -v /tmp/docker-demo/backend:/app \
133 -w /app \
134 --health-cmd="wget -q --spider http://localhost:3000/health || exit 1" \
135 --health-interval=10s \
136 --health-timeout=5s \
137 --health-retries=3 \
138 --restart unless-stopped \
139 node:18-alpine sh -c "npm install && npm start"
140
141# Step 6: Create frontend with Nginx
142echo "\n=== Step 6: Creating Frontend Application ==="
143mkdir -p /tmp/docker-demo/frontend
144cat > /tmp/docker-demo/frontend/index.html << 'EOF'
145<!DOCTYPE html>
146<html lang="en">
147<head>
148 <meta charset="UTF-8">
149 <meta name="viewport" content="width=device-width, initial-scale=1.0">
150 <title>Docker Demo App</title>
151 <style>
152 body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
153 .status { padding: 20px; border-radius: 8px; margin: 20px 0; }
154 .healthy { background: #d4edda; color: #155724; }
155 .error { background: #f8d7da; color: #721c24; }
156 button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
157 pre { background: #f4f4f4; padding: 15px; border-radius: 4px; overflow-x: auto; }
158 </style>
159</head>
160<body>
161 <h1>Docker Container Demo</h1>
162 <p>This application demonstrates Docker container management.</p>
163
164 <button onclick="checkStatus()">Check Backend Status</button>
165
166 <div id="result"></div>
167
168 <script>
169 async function checkStatus() {
170 const resultDiv = document.getElementById('result');
171 resultDiv.innerHTML = '<p>Checking...</p>';
172
173 try {
174 const response = await fetch('/api/status');
175 const data = await response.json();
176 resultDiv.innerHTML = \`
177 <div class="status healthy">
178 <h3>✅ Backend Connected</h3>
179 <pre>\${JSON.stringify(data, null, 2)}</pre>
180 </div>
181 \`;
182 } catch (err) {
183 resultDiv.innerHTML = \`
184 <div class="status error">
185 <h3>❌ Connection Error</h3>
186 <p>\${err.message}</p>
187 </div>
188 \`;
189 }
190 }
191 </script>
192</body>
193</html>
194EOF
195
196cat > /tmp/docker-demo/frontend/nginx.conf << 'EOF'
197server {
198 listen 80;
199 server_name localhost;
200
201 location / {
202 root /usr/share/nginx/html;
203 index index.html;
204 }
205
206 location /api/ {
207 proxy_pass http://app-backend:3000/api/;
208 proxy_http_version 1.1;
209 proxy_set_header Host $host;
210 proxy_set_header X-Real-IP $remote_addr;
211 }
212}
213EOF
214
215# Run frontend container
216echo "\n=== Step 7: Starting Frontend Container ==="
217docker run -d \
218 --name app-frontend \
219 --network app-network \
220 -p 8080:80 \
221 -v /tmp/docker-demo/frontend/index.html:/usr/share/nginx/html/index.html:ro \
222 -v /tmp/docker-demo/frontend/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
223 --restart unless-stopped \
224 nginx:alpine
225
226# Step 7: Verify deployment
227echo "\n=== Step 8: Verifying Deployment ==="
228echo "\nContainers:"
229docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
230
231echo "\nNetwork connections:"
232docker network inspect app-network --format '{{range .Containers}}{{.Name}} {{end}}'
233
234echo "\nContainer resource usage:"
235docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
236
237# Step 8: Test the application
238echo "\n=== Step 9: Testing Application ==="
239sleep 5 # Wait for services to start
240
241echo "\nBackend health check:"
242curl -s http://localhost:3000/health | jq .
243
244echo "\nBackend API status:"
245curl -s http://localhost:3000/api/status | jq .
246
247echo "\nFrontend accessible:"
248curl -s -o /dev/null -w "%{http_code}" http://localhost:8080
249
250# Step 9: Demonstrate inspection commands
251echo "\n=== Step 10: Container Inspection ==="
252echo "\nPostgreSQL container details:"
253docker inspect app-postgres --format '
254 Name: {{.Name}}
255 State: {{.State.Status}}
256 IP Address: {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}
257 Health: {{.State.Health.Status}}
258'
259
260echo "\nBackend container logs (last 10 lines):"
261docker logs --tail 10 app-backend
262
263echo "\nBackend environment variables:"
264docker exec app-backend env | grep -E "^(DB_|PORT|NODE)"
265
266echo "\n=============================================="
267echo "Deployment Complete!"
268echo "=============================================="
269echo "Frontend: http://localhost:8080"
270echo "Backend API: http://localhost:3000"
271echo "\nUseful commands:"
272echo " docker logs -f app-backend # Follow backend logs"
273echo " docker exec -it app-postgres psql -U appuser -d myappdb # Connect to database"
274echo " docker stats # Monitor resource usage"
275echo " docker network inspect app-network # Inspect network"
276echo "\nCleanup command:"
277echo " docker rm -f app-postgres app-backend app-frontend && docker network rm app-network"Your progress is saved in this browser, on this device only.