21.7. Running Containers
Table of Contents
Basic Idea Of Running Containers
Running a container means starting an isolated process from an image.
You can think of it as:
- Image: a template or class.
- Container: a running instance of that template, like an object.
You always:
- Choose an image.
- Run
docker runwith some options. - Docker creates a container, starts it, and attaches or detaches your terminal.
In this chapter we focus on how to run containers in different ways and what the most common options mean.
Key rule:
You build or pull an image once, then you can run many containers from that image.
The `docker run` Command
The main command:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]Typical structure:
| Part | Example | Meaning |
|---|---|---|
docker run | docker run | Start a new container |
OPTIONS | -it -p 8080:80 | Configure networking, environment, etc. |
IMAGE | nginx | Base image to use |
COMMAND | bash | Override default command (optional) |
ARG... | -l | Arguments passed to the command |
Example:
docker run ubuntu echo "Hello from container"- Image:
ubuntu - Command:
echo "Hello from container" - The container runs the command and then exits.
Running Simple Commands
You can use containers just to run a short command.
docker run ubuntu lsThis:
- Starts a container from the
ubuntuimage. - Runs
lsinside it. - Shows the result in your terminal.
- Stops the container.
Some common examples:
# Print OS version inside Ubuntu
docker run ubuntu cat /etc/os-release
# Use Alpine as a small Linux to run 'echo'
docker run alpine echo "Hi from Alpine"
# Use Python image to run a small one-line script
docker run python:3.12-alpine python -c "print('Hello, Python in Docker')"Every time you run these, Docker creates a new container instance.
Interactive Containers (`-it`)
To get a shell inside a container you use:
-ikeep STDIN open.-tallocate a pseudo-TTY so it looks like a normal terminal.
Often combined as -it:
docker run -it ubuntu bashThis gives you an interactive shell:
root@<some-id>:/# ls
bin boot dev etc home ...
root@<some-id>:/# pwd
/
When you type exit (or press Ctrl+D), the shell exits and the container stops.
More examples:
# Alpine image uses 'sh' by default
docker run -it alpine sh
# Python REPL inside a container
docker run -it python:3.12-alpine python
Important:
If the main process (like bash, sh, or python) exits, the container stops.
A container lives as long as its main process runs.
Detached Mode (`-d`)
If you do not want to keep the container attached to your current terminal, run it in detached mode:
docker run -d nginx-dmeans "run in the background".- Docker prints a container ID and returns you to your prompt.
You can then:
docker ps # list running containers
docker logs <id> # view output
docker stop <id> # stop containerExample with a specific name:
docker run -d --name my-nginx nginx
docker ps
docker logs my-nginx
docker stop my-nginxNaming Containers (`--name`)
If you do not specify a name, Docker generates one like elegant_panda.
To work with containers more easily, name them:
docker run --name backend -d python:3.12-alpine sleep 3600
Now you can use backend instead of the long ID:
docker logs backend
docker stop backend
docker rm backendCommon pattern for backend work:
# Example: run PostgreSQL with a fixed name
docker run -d \
--name my-postgres \
-e POSTGRES_PASSWORD=secret \
postgres:16Publishing Ports (`-p`)
By default, container ports are not reachable from your host.
To make a container port accessible, you publish it:
docker run -d -p HOST_PORT:CONTAINER_PORT IMAGEExample with Nginx:
docker run -d -p 8080:80 nginx- Port
80in the container is Nginx’s HTTP port. - Port
8080on your host maps to container port80. - You can open:
http://localhost:8080.
Another example, typical for a backend:
# Suppose your app listens on port 8000 inside the container
docker run -d -p 8000:8000 my-backend-imageTable of examples:
| Command | Meaning |
|---|---|
-p 8080:80 | Host 8080 → Container 80 |
-p 5000:5000 | Host 5000 → Container 5000 |
-p 127.0.0.1:5432:5432 | Only on localhost, 5432 → Container 5432 |
If you specify IP like 127.0.0.1:8080:80, the port is only bound to localhost, not all network interfaces.
Environment Variables in Containers (`-e`)
Backends often configure:
- Database URLs
- Secrets
- Debug flags
through environment variables.
You can set them when running a container:
docker run -d \
-e APP_ENV=production \
-e DEBUG=false \
my-backend-imageTo check environment variables inside a container:
docker run --rm -e FOO=bar alpine sh -c 'echo $FOO'Examples for common services:
# PostgreSQL with password
docker run -d \
--name db \
-e POSTGRES_PASSWORD=secret \
postgres:16
# Redis with a custom password (example)
docker run -d \
-e REDIS_PASSWORD=somepass \
redis:7-alpineYou can also load environment variables from a file:
docker run -d --env-file .env my-backend-image
.env format:
APP_ENV=development
DATABASE_URL=postgresql://user:pass@db:5432/appVolumes and Mounts (`-v` / `--mount`)
Containers have their own filesystem.
When a container is removed, its filesystem is usually removed too.
To keep data or share files with the host, you use volumes or bind mounts.
Anonymous volume
Simple example:
docker run -d -v /var/lib/postgresql/data postgres:16
Docker creates a volume and mounts it at /var/lib/postgresql/data inside the container.
Named volume
Better for databases and important data:
docker volume create pgdata
docker run -d \
--name db \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16Now database data persists even if you remove the container.
Bind mount (share host directory)
Very useful during development to share your code with the container:
docker run -it \
-v $(pwd):/app \
-w /app \
python:3.12-alpine \
python main.pyExplanation:
-v $(pwd):/app
Mount current host directory into/appin the container.-w /app
Set working directory inside the container.python main.py
Run your app using container’s Python interpreter.
Table of mount patterns:
| Type | Example | Use case |
|---|---|---|
| Named volume | -v pgdata:/var/lib/postgresql/data | Persistent DB data |
| Bind mount | -v $(pwd):/app | Share source code in development |
| Read-only | -v $(pwd):/app:ro | Share code without write access |
Working With Container Lifecycle
Listing containers
docker ps # running only
docker ps -a # all, including stoppedImportant columns:
CONTAINER IDNAMESSTATUSPORTSIMAGE
Example:
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
abc123def456 nginx "/docker-entrypoin..." Up 2 minutes 0.0.0.0:8080->80/tcp my-nginxStopping and starting
docker stop my-nginx # send SIGTERM then SIGKILL after timeout
docker start my-nginx # start a stopped container
stop and start keep the container’s filesystem and configuration.
Restarting
docker restart my-nginx
This is like stop then start.
Removing containers
When you no longer need a container:
docker rm my-nginxIf it is running, you can force:
docker rm -f my-nginx
Important:
docker rm removes the container, not the image.
If you did not use volumes, data inside the container filesystem is lost.
You can also automatically remove a container after it exits:
docker run --rm alpine echo "one-time task"Inspecting Containers
To inspect what is happening inside or about a container:
Logs
docker logs my-nginx
docker logs -f my-nginx # follow logs like 'tail -f'Executing commands in a running container
docker exec -it my-nginx bashor for Alpine:
docker exec -it my-app sh
This gives you a shell in an already running container.
You can also run a single command:
docker exec my-nginx ls /etc/nginxInspect metadata
docker inspect my-nginxThis prints detailed JSON about:
- Environment variables
- Mounted volumes
- Configured ports
- Networks
- Image used
- And more
Restart Policies
For backend services in production, you often want containers to restart automatically if they crash or when the host reboots.
Use --restart:
docker run -d \
--name my-backend \
--restart unless-stopped \
my-backend-imageCommon restart policies:
| Policy | Behavior |
|---|---|
no | Never restart (default) |
on-failure | Restart only if exit code is non-zero |
always | Always restart, even after docker daemon restart |
unless-stopped | Restart unless you manually stop the container |
Example for a database:
docker run -d \
--name db \
--restart unless-stopped \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16Practical Backend Examples
Here are some realistic commands you might use as a backend developer.
Run a local PostgreSQL database
docker volume create pgdata
docker run -d \
--name postgres \
-e POSTGRES_USER=app \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=app_db \
-v pgdata:/var/lib/postgresql/data \
-p 5432:5432 \
postgres:16
Then in your backend app you can connect to localhost:5432.
Run Redis for caching or sessions
docker run -d \
--name redis \
-p 6379:6379 \
redis:7-alpine
Connect from your app to localhost:6379.
Run a FastAPI app image
Assume you built my-fastapi-app:latest which listens on port 8000.
docker run -d \
--name fastapi \
-p 8000:8000 \
-e APP_ENV=development \
my-fastapi-app:latest
You can now open http://localhost:8000/docs.
Summary
In this chapter you learned how to:
- Use
docker runwith images and commands. - Start containers interactively with
-it. - Run long‑lived services in the background with
-d. - Name containers with
--nameand manage them withps,stop,start,rm. - Expose container ports using
-p. - Pass configuration using environment variables
-eand--env-file. - Persist and share data using volumes and bind mounts.
- Inspect, log, and exec into running containers.
- Use restart policies for resilient backend services.
These are the day-to-day Docker commands you will use when developing and running backend applications.
Views: 10
KAHIBARO