Chapter 3 — Essential Docker Commands
This is your practical reference chapter. You've already seen several Docker
commands in action — now we cover each one properly, with all the useful flags,
real annotated output, and the patterns you'll reach for every single day.
Keep this chapter handy while you work through the rest of the course.
We'll cover commands in logical groups: running containers, monitoring them,
getting inside them, moving files, and cleaning up. At the end there's a
condensed cheat sheet you can refer back to at a glance.
1. docker run — Create and Start a Container
docker run is the command you'll use most. It creates a new
container from an image and starts it. Every flag you attach shapes how the
container behaves for its entire lifetime.
docker run [flags] IMAGE [COMMAND]
Create and start a new container
| Flag | What it does | Example |
| -d |
Detached — run in background, return container ID |
docker run -d nginx |
| -it |
Interactive terminal — keep STDIN open and allocate a TTY. Almost always used together |
docker run -it ubuntu bash |
| -p HOST:CONT |
Map a host port to a container port |
-p 8080:80 |
| --name NAME |
Give the container a memorable name instead of a random one |
--name my-site |
| -e KEY=VALUE |
Set an environment variable inside the container |
-e MYSQL_ROOT_PASSWORD=secret |
| -v HOST:CONT |
Mount a volume or host directory into the container |
-v /my/data:/var/lib/mysql |
| --rm |
Automatically remove the container when it exits — great for one-off tasks |
docker run --rm alpine echo hi |
| --restart POLICY |
Restart policy: no (default), always, unless-stopped, on-failure |
--restart unless-stopped |
| --network NAME |
Connect to a specific Docker network |
--network my-net |
| -w DIR |
Set the working directory inside the container |
-w /app |
| --memory 512m |
Limit RAM the container can use |
--memory 256m |
Terminal — common docker run patterns
# Background web server, port 8080, auto-restart on reboot
$ docker run -d -p 8080:80 --name site --restart unless-stopped nginx:alpine
# One-off task — container removed automatically when done
$ docker run --rm alpine echo "Hello from Alpine"
Hello from Alpine
# Interactive Python session in a container
$ docker run -it --rm python:3.12-slim python
>>> print("Running inside Docker!")
Running inside Docker!
>>> exit()
# Pass environment variable (e.g. database password)
$ docker run -d --name mydb \
-e MYSQL_ROOT_PASSWORD=mysecret \
-e MYSQL_DATABASE=myapp \
mysql:8.0
--rm is your best friend for experimentation. Any time you
want to try something — run a quick Python script, test a CLI tool, check a
config — add --rm and the container vanishes the moment it
finishes. No cleanup needed.
2. docker ps — List Containers
docker ps [flags]
List containers (running only by default)
| Flag | What it does |
| -a | Show all containers, including stopped ones |
| -q | Quiet — only output container IDs (useful in scripts) |
| --filter status=exited | Filter by status: created, running, paused, exited, dead |
| --format | Custom output format using Go templates |
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
a91b3c2d4e5f nginx:alpine "nginx..." 5 minutes ago Up 5 minutes site
b82c4d3e5f6a ubuntu "bash" 2 hours ago Exited (0) 2 hours ago loving_einstein
c73d5e4f6a7b hello-world "/hello" 1 day ago Exited (0) 1 day ago hopeful_morse
# Stop ALL running containers in one go
$ docker stop $(docker ps -q)
# Clean up ALL stopped containers
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
b82c4d3e5f6a
c73d5e4f6a7b
Total reclaimed space: 24B
3. docker logs — View Container Output
docker logs [flags] CONTAINER
Fetch the logs (stdout/stderr) of a container
| Flag | What it does | Example |
| -f |
Follow — stream new log lines in real time (like tail -f). Press Ctrl+C to stop following without stopping the container |
docker logs -f site |
| --tail N |
Only show the last N lines |
docker logs --tail 50 site |
| --since TIME |
Show logs since a timestamp or relative time |
docker logs --since 5m site |
| -t |
Add timestamps to each log line |
docker logs -t site |
# Watch live access log as requests come in
$ docker logs -f site
172.17.0.1 - - [16/Jun/2024:10:45:22 +0000] "GET / HTTP/1.1" 200 615
172.17.0.1 - - [16/Jun/2024:10:45:25 +0000] "GET /favicon.ico HTTP/1.1" 404 555
^C
# Last 10 lines with timestamps — useful for diagnosing a crash
$ docker logs --tail 10 -t site
2024-06-16T10:44:01.234567890Z 2024/06/16 10:44:01 [notice] nginx started
2024-06-16T10:45:22.123456789Z 172.17.0.1 - GET / 200
Where do logs come from? Docker captures whatever the container's
main process writes to stdout and stderr. Well-behaved applications write their
logs there by default (nginx, Apache, MySQL all do). If you're building your own
image and logs aren't appearing, make sure your app logs to stdout rather than
to a file.
4. docker exec — Run Commands Inside a Running Container
docker exec [flags] CONTAINER COMMAND
Execute a command in a running container
| Flag | What it does |
| -it | Interactive terminal — needed when opening a shell |
| -d | Detached — run the command in background |
| -e KEY=VALUE | Set environment variable for this command only |
| -u USER | Run as a specific user (e.g. -u root) |
| -w DIR | Set working directory for this command |
# Open an interactive shell inside a running container
$ docker exec -it site sh
/ # nginx -v
nginx version: nginx/1.27.0
/ # exit
# Run a single command without opening a shell
$ docker exec site nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# Reload nginx config without restarting the container
$ docker exec site nginx -s reload
# Connect to MySQL inside a running database container
$ docker exec -it mydb mysql -u root -p
Enter password:
Welcome to the MySQL monitor...
exec vs run: docker exec runs inside an
already running container. docker run creates a brand
new container. If you want to poke around inside your nginx container without
stopping it, always use exec.
5. docker cp — Copy Files To and From Containers
docker cp SRC DEST
Copy files between host and container. Works on running or stopped containers.
Use CONTAINER:PATH syntax to refer to a path inside a container.
| Direction | Command |
| Host → Container |
docker cp ./index.html site:/usr/share/nginx/html/ |
| Container → Host |
docker cp site:/etc/nginx/nginx.conf ./nginx.conf |
| Copy a directory |
docker cp ./mysite/ site:/usr/share/nginx/html/ |
# Copy a custom HTML page into a running nginx container
$ echo "<h1>Hello from Docker!</h1>" > index.html
$ docker cp index.html site:/usr/share/nginx/html/index.html
Successfully copied 2.05kB to site:/usr/share/nginx/html/index.html
# Now refresh http://localhost:8080 — you'll see "Hello from Docker!"
# Extract the default nginx config as a starting point
$ docker cp site:/etc/nginx/nginx.conf ./nginx.conf
Successfully copied 2.77kB to /home/user/nginx.conf
docker cp vs volumes: docker cp is great for
one-off file transfers and extracting config files. For ongoing work — where
you want file changes on your host to be immediately visible inside the container
— use a volume mount (-v). Chapter 4 covers volumes in detail.
6. docker inspect — Full Container Details
docker inspect returns everything Docker knows about a container
or image as a JSON object. It's invaluable for debugging — finding the IP address
of a container, checking what volumes are mounted, seeing environment variables,
and more.
$ docker inspect site
[
{
"Id": "a91b3c2d4e5f...",
"Name": "/site",
"State": { "Status": "running", "Running": true, ... },
"NetworkSettings": {
"IPAddress": "172.17.0.2",
"Ports": { "80/tcp": [{ "HostPort": "8080" }] }
},
"Mounts": [],
"Config": {
"Env": ["PATH=/usr/local/sbin:/usr/local/bin:..."],
"Image": "nginx:alpine"
}
}
]
The output is large. Use --format with Go template syntax to
extract just what you need:
# Get just the container's IP address
docker inspect --format '{{.NetworkSettings.IPAddress}}' site
172.17.0.2
# Get the restart policy
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' site
unless-stopped
# Get all environment variables
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' site
# Works on images too — see the exposed ports
docker inspect --format '{{.Config.ExposedPorts}}' nginx:alpine
map[80/tcp:{}]
7. Stopping, Starting, and Removing Containers
# Graceful stop (sends SIGTERM, waits 10s, then SIGKILL)
docker stop site
# Immediate kill — no grace period
docker kill site
# Change the grace period to 30 seconds
docker stop --time 30 site
# Restart a running or stopped container
docker restart site
# Remove a stopped container
docker rm site
# Force remove a running container (stop + rm in one)
docker rm -f site
# Remove all stopped containers
docker container prune
# Rename an existing container
docker rename old-name new-name
8. Image Commands
# List all local images
docker images
docker image ls # same thing, newer syntax
# Pull without running
docker pull nginx:alpine
# Remove an image (container must be removed first)
docker rmi nginx:alpine
docker image rm nginx:alpine # same thing
# Remove ALL unused images (not used by any container)
docker image prune -a
# Show image layers and how they were built
docker image history nginx:alpine
# Tag an image with a new name
docker tag nginx:alpine my-nginx:v1
# Search Docker Hub from the CLI
docker search --filter is-official=true python
9. docker system — Disk Usage and Global Cleanup
# See how much disk Docker is using
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 6 2 1.23GB 890MB (72%)
Containers 3 1 12.5kB 12.5kB
Local Volumes 2 1 145MB 0B
Build Cache 18 0 234MB 234MB
# Remove everything unused: stopped containers, unused images,
# unused networks, build cache. The nuclear option — use with care.
$ docker system prune -a
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all images without at least one container associated to them
- all build cache
Are you sure you want to continue? [y/N]
docker system prune -a removes all images not currently in
use by a running container — including images you might want again soon. It's
useful for freeing up disk space, but you'll have to re-pull those images next
time. Leave out -a to only remove dangling (untagged) images and
be a bit more conservative.
10. Quick-Reference Cheat Sheet
Running Containers
docker run -d IMAGEStart in background
docker run -it IMAGE shInteractive shell
docker run --rm IMAGEAuto-remove on exit
docker run -p 8080:80Map host:container port
docker run -e KEY=VALSet env variable
docker run -v /h:/cMount directory
--name NAMEName the container
--restart unless-stoppedAuto-restart on reboot
Monitoring
docker psList running containers
docker ps -aAll containers (inc. stopped)
docker logs NAMEView output
docker logs -f NAMEFollow live output
docker statsLive CPU/RAM usage
docker inspect NAMEFull container details (JSON)
docker top NAMEProcesses inside container
docker system dfDisk usage summary
Container Control
docker stop NAMEGraceful stop
docker start NAMEStart stopped container
docker restart NAMEStop then start
docker rm NAMERemove stopped container
docker rm -f NAMEForce remove (running)
docker exec -it NAME shShell into container
docker cp src NAME:destCopy file in
docker cp NAME:src destCopy file out
Images and Cleanup
docker imagesList local images
docker pull IMAGE:TAGDownload image
docker rmi IMAGERemove image
docker image prune -aRemove unused images
docker container pruneRemove stopped containers
docker system prune -aRemove everything unused
docker search TERMSearch Docker Hub
docker image history IMGShow image layers
Exercises
- Run a one-off command with --rm. Use
docker run --rm python:3.12-slim python -c "import sys; print(sys.version)" to print the Python version from inside a container, then verify with docker ps -a that no container was left behind.
- Use docker cp to serve a custom page. Start an nginx container with
docker run -d -p 8080:80 --name site nginx:alpine. Create a simple index.html on your host with any content you like. Copy it into the container using docker cp and verify you can see your page at http://localhost:8080.
- Follow the logs. With your nginx container running, open a second terminal and run
docker logs -f site. In your browser, visit http://localhost:8080 several times. Watch the access log entries appear in real time in the second terminal.
- Use docker inspect to find the IP. Run
docker inspect --format '{{.NetworkSettings.IPAddress}}' site to get the container's internal IP address. Then use docker exec -it site sh to open a shell and run ip addr to confirm it matches.
- Check disk usage and clean up. Run
docker system df to see your current Docker disk usage. Stop and remove the nginx container, then run docker image prune -a to remove any images not in use. Run docker system df again to see how much space you reclaimed.
Next: Chapter 4 — Volumes and Persistent Data
You've seen that containers lose their data when removed. Chapter 4 solves this
with volumes — Docker's mechanism for keeping data alive independent of containers.
You'll learn the difference between bind mounts (pointing at a folder on your host)
and named volumes (managed by Docker), and see how to use each one to persist a
database and serve website files that you can edit directly from your machine.