Getting Started with Docker
Getting Started with Docker
This chapter covers installing Docker on your system, verifying the installation, and running your first containers. You'll learn the essential commands that form the foundation of all Docker work.
Installing Docker
Docker installation varies by operating system. For the best development experience, Docker Desktop is recommended for Mac and Windows users.
Docker Desktop for Mac
- Download Docker Desktop from docker.com
- Open the downloaded
.dmgfile - Drag Docker to your Applications folder
- Launch Docker from Applications
- Complete the setup wizard
# Verify installation
docker --version
# Docker version 25.0.3, build 4debf41
docker compose version
# Docker Compose version v2.24.5Docker Desktop for Windows
- Ensure Windows 10/11 64-bit (Pro, Enterprise, or Education) with WSL 2
- Enable WSL 2 feature and install a Linux distribution
- Download and install Docker Desktop
- Start Docker Desktop from the Start menu
# Enable WSL 2 (run as Administrator)
wsl --install
# After restart, verify Docker
docker --versionWarning
Docker Desktop for Windows requires WSL 2 (Windows Subsystem for Linux) for the best experience. The Hyper-V backend is available but not recommended for most development workflows.
Docker on Linux
On Linux, install Docker Engine directly:
# Ubuntu/Debian
# Remove old versions
sudo apt-get remove docker docker-engine docker.io containerd runc
# Install using the repository
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Set up the repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginPost-Installation (Linux)
Add your user to the docker group to run commands without sudo:
# Add your user to the docker group
sudo usermod -aG docker $USER
# Apply the new group membership
newgrp docker
# Verify it works without sudo
docker run hello-worldNote
On Linux, you may need to log out and back in for group changes to take
effect. The newgrp command applies changes to the current shell session.
Verifying Your Installation
Confirm Docker is installed and working:
# Check Docker version
docker --version
# Check detailed version information
docker version
# View system-wide information
docker info
# Run a test container
docker run hello-worldThe hello-world container should produce output like:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.Your First Container
Let's run a more practical container—an nginx web server:
# Run nginx in the foreground
docker run nginx
# In another terminal, check if it's running
docker psPress Ctrl+C to stop the container. Now let's run it properly:
# Run nginx in the background (detached mode)
docker run -d -p 8080:80 --name my-nginx nginx
# Verify it's running
docker psOpen http://localhost:8080 in your browser—you'll see the nginx welcome page.
Understanding the Run Command
docker run -d -p 8080:80 --name my-nginx nginx
│ │ │ │
│ │ │ └─ Image name
│ │ └─ Container name
│ └─ Port mapping (host:container)
└─ Detached mode (run in background)Essential Docker Commands
Managing Containers
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# Stop a container
docker stop my-nginx
# Start a stopped container
docker start my-nginx
# Restart a container
docker restart my-nginx
# Remove a stopped container
docker rm my-nginx
# Force remove a running container
docker rm -f my-nginxContainer Information
# View container logs
docker logs my-nginx
# Follow logs in real-time
docker logs -f my-nginx
# Show last 100 lines
docker logs --tail 100 my-nginx
# Inspect container details
docker inspect my-nginx
# View container resource usage
docker stats
# View running processes in a container
docker top my-nginxExecuting Commands in Containers
# Run a command in a running container
docker exec my-nginx ls /usr/share/nginx/html
# Open an interactive shell
docker exec -it my-nginx /bin/bash
# Run as a different user
docker exec -u root my-nginx whoamiNote
The -it flags are almost always used together: -i keeps STDIN open, and
-t allocates a pseudo-TTY. Together, they give you an interactive terminal
session.
Working with Container Names and IDs
Every container gets a unique ID and can have a name:
# Docker generates a random name if you don't specify one
docker run -d nginx
# Container name might be: zealous_darwin
# Specify a name explicitly
docker run -d --name webserver nginx
# Reference containers by name or ID
docker stop webserver
docker stop a1b2c3d4e5f6
# You can use just the first few characters of the ID
docker stop a1b2Container IDs and Names
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp my-nginx
f7e8d9c0b1a2 redis "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 6379/tcp redis-cachePort Mapping
Port mapping connects container ports to host ports:
# Map container port 80 to host port 8080
docker run -d -p 8080:80 nginx
# Map to a specific interface
docker run -d -p 127.0.0.1:8080:80 nginx
# Map multiple ports
docker run -d -p 80:80 -p 443:443 nginx
# Map a port range
docker run -d -p 8000-8010:8000-8010 myapp
# Publish all exposed ports to random ports
docker run -d -P nginxWarning
Binding to 0.0.0.0 (the default) exposes the port on all network interfaces.
For local development, consider binding to 127.0.0.1 to prevent external
access.
Viewing Port Mappings
# See port mappings for a container
docker port my-nginx
# 80/tcp -> 0.0.0.0:8080
# List all containers with their ports
docker ps --format "{{.Names}}: {{.Ports}}"Environment Variables
Pass configuration to containers via environment variables:
# Set a single variable
docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql
# Set multiple variables
docker run -d \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=myapp \
postgres
# Load from a file
docker run -d --env-file ./database.env postgresEnvironment File Format
# database.env
POSTGRES_USER=admin
POSTGRES_PASSWORD=secret
POSTGRES_DB=myapp
PGDATA=/var/lib/postgresql/data/pgdataInteractive Containers
Some containers are meant for interactive use:
# Run Ubuntu interactively
docker run -it ubuntu bash
# You're now inside the container
root@a1b2c3d4e5f6:/# cat /etc/os-release
root@a1b2c3d4e5f6:/# exit
# Run Python interactively
docker run -it python:3.12 python
# Run Node.js REPL
docker run -it node:20 nodeAttaching to Running Containers
# Attach to a container's main process
docker attach my-container
# Detach without stopping: Ctrl+P, Ctrl+Q
# Attach with a new shell instead
docker exec -it my-container /bin/bashNote
Use docker exec instead of docker attach when you want a new shell
session. attach connects to the container's main process, while exec
starts a new process.
Container Cleanup
Containers accumulate quickly. Keep your system clean:
# Remove a stopped container
docker rm my-nginx
# Remove multiple containers
docker rm container1 container2 container3
# Remove all stopped containers
docker container prune
# Remove all stopped containers (alternative)
docker rm $(docker ps -aq)
# Stop and remove all containers
docker stop $(docker ps -q) && docker rm $(docker ps -aq)Auto-Remove Containers
# Automatically remove container when it exits
docker run --rm nginx echo "Hello"
# Container is removed after the command completes
# Useful for one-off commands
docker run --rm -it python:3.12 python -c "print('Hello from Python')"Running Containers with Resource Limits
Control how much CPU and memory containers can use:
# Limit memory to 512MB
docker run -d --memory=512m nginx
# Limit CPU to 1.5 cores
docker run -d --cpus=1.5 nginx
# Combine limits
docker run -d \
--memory=1g \
--memory-swap=2g \
--cpus=2 \
nginx
# Reserve memory (for orchestrators)
docker run -d --memory-reservation=256m nginxResource Limits
| Flag | Description |
|---|---|
--memory | Maximum memory the container can use |
--memory-swap | Total memory + swap allowed |
--cpus | Number of CPUs available |
--cpu-shares | CPU shares (relative weight) |
--memory-reservation | Soft limit (for scheduling) |
Container Restart Policies
Control what happens when a container stops:
# Never restart (default)
docker run -d --restart=no nginx
# Always restart
docker run -d --restart=always nginx
# Restart on failure
docker run -d --restart=on-failure nginx
# Restart on failure, max 5 attempts
docker run -d --restart=on-failure:5 nginx
# Restart unless manually stopped
docker run -d --restart=unless-stopped nginxWarning
Use always carefully—it will restart containers even after a system reboot
if Docker is set to start on boot. For development, unless-stopped is often
more appropriate.
Viewing and Following Logs
Container logs are essential for debugging:
# View all logs
docker logs my-nginx
# Follow logs (like tail -f)
docker logs -f my-nginx
# Show timestamps
docker logs -t my-nginx
# Show last N lines
docker logs --tail 50 my-nginx
# Show logs since a time
docker logs --since 1h my-nginx
docker logs --since 2024-01-15T10:00:00 my-nginx
# Combine options
docker logs -f --tail 100 -t my-nginxPractical Examples
Running a Database
# PostgreSQL with persistent data
docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=mysecret \
-p 5432:5432 \
-v postgres-data:/var/lib/postgresql/data \
postgres:16
# Connect using psql
docker exec -it postgres psql -U postgresRunning a Web Application Stack
# Start Redis
docker run -d --name redis redis:7
# Start your app, linking to Redis
docker run -d \
--name myapp \
--link redis:redis \
-e REDIS_URL=redis://redis:6379 \
-p 3000:3000 \
myapp:latestQuick Development Server
# Serve static files from current directory
docker run -d \
--name devserver \
-p 8080:80 \
-v $(pwd):/usr/share/nginx/html:ro \
nginx
# Now http://localhost:8080 serves files from your current directoryCommand Quick Reference
| Command | Purpose |
|---|---|
docker run | Create and start a container |
docker ps | List running containers |
docker ps -a | List all containers |
docker stop | Stop a running container |
docker start | Start a stopped container |
docker rm | Remove a container |
docker logs | View container logs |
docker exec | Run a command in a container |
docker inspect | View detailed container info |
docker stats | View resource usage |
In the next chapter, we'll explore Docker images—the templates from which containers are created.