The previous lesson covered images. This lesson is about running containers — turning images into live processes, configuring them, and managing their lifecycle.
Anatomy of docker run
docker run [OPTIONS] IMAGE [COMMAND] [ARGS...]
A complete example:
docker run -d \
--name web \
-p 8080:80 \
-e NGINX_HOST=example.com \
--restart unless-stopped \
nginx:1.27
This: starts in detached mode (-d), names the container web, publishes container port 80 to host port 8080, sets an environment variable, restarts the container automatically unless explicitly stopped, and uses the nginx:1.27 image.
The Most Useful Flags
| Flag | Purpose |
|---|---|
-d / --detach | Run in the background |
-it | Interactive + TTY — for shells and interactive CLIs |
--name NAME | Assign a friendly name (otherwise Docker generates one) |
-p HOST:CONTAINER | Publish a port to the host |
-e KEY=VALUE | Set an environment variable |
--env-file PATH | Load env vars from a file |
-v HOST:CONTAINER | Mount a host directory or named volume |
--rm | Auto-remove the container when it exits |
--restart POLICY | Restart policy: no, on-failure, always, unless-stopped |
--memory 512m | Memory limit |
--cpus 1.5 | CPU limit (1.5 cores) |
--network NET | Attach to a specific network |
Interactive Shells
Run a one-shot interactive Ubuntu shell:
docker run --rm -it ubuntu:24.04 bash
The --rm ensures the container is cleaned up when you exit. -it gives you a real terminal.
The Container Lifecycle
docker run docker stop / kill docker start
│ │ │
▼ ▼ ▼
created ─▶ running ─▶ exited ─▶ running ─▶ ...
│
▼
docker rm ─▶ gone
Useful management commands:
docker ps # running containers
docker ps -a # all containers (incl. stopped)
docker stop web # graceful stop (SIGTERM, then SIGKILL)
docker kill web # immediate SIGKILL
docker start web # restart a stopped container
docker restart web # stop + start
docker rm web # remove a stopped container
docker rm -f web # force-remove a running container
docker logs -f web # tail logs
docker exec -it web bash # open a shell inside the container
docker stats # live CPU/mem usage of running containers
docker inspect web # full JSON metadata
Publishing Ports
By default, a container's network ports are private to the container. -p opens a hole in the host:
docker run -p 8080:80 nginx # host:8080 → container:80
docker run -p 127.0.0.1:8080:80 nginx # bind only to loopback
docker run -P nginx # publish all EXPOSEd ports to random hosts
Environment Variables and Secrets
Twelve-factor apps configure themselves through environment variables, which is exactly how container images expect to be configured:
docker run -e DATABASE_URL=postgres://... -e LOG_LEVEL=info myapp
docker run --env-file .env myapp
Never bake secrets into images. Use environment variables, secret managers (AWS Secrets Manager, Azure Key Vault), or Docker secrets / Kubernetes Secrets. Anything in an image is visible to anyone who can pull it.
Restart Policies
| Policy | Behaviour |
|---|---|
no (default) | Don't restart |
on-failure[:N] | Restart only on non-zero exit, optionally up to N times |
always | Restart on any exit, including manual docker stop after daemon reboot |
unless-stopped | Restart unless you explicitly stopped it (the production default) |
Resource Limits
By default, a container can use all the host's CPU and memory — a runaway container can take down the host. In production, always set limits:
docker run --memory 512m --memory-swap 1g --cpus 1.5 myapp
Kubernetes makes these limits explicit (requests and limits in pod specs) — but the same kernel-level cgroups enforce them.