Images & Containers
Images and containers are the two objects you interact with most. An image is the immutable build artifact; a container is a running instance of it. This page covers the day-to-day commands for pulling images, controlling container lifecycles, and wiring containers to ports, storage, and configuration.
Pulling and running images
Images live in registries and are addressed by name:tag. Pull one explicitly, or let docker run pull it on demand:
docker pull nginx:1.27
docker run -d --name web -p 8080:80 nginx:1.27
The -d flag runs the container detached (in the background). --name assigns a stable name so you do not have to reference the generated container ID.
Inspecting and controlling containers
List running containers, or add -a to include stopped ones:
docker ps -a
Output:
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
3f7a9c2b1e44 nginx:1.27 "/docker-entrypoint.…" Up 2 minutes 0.0.0.0:8080->80/tcp web
The full lifecycle is managed with a small set of verbs:
docker stop web # graceful stop (SIGTERM, then SIGKILL)
docker start web # restart a stopped container
docker logs -f web # stream stdout/stderr
docker exec -it web sh # open a shell inside the container
docker rm web # remove a stopped container
docker rm -f web # force-remove a running container
Containers are ephemeral. Anything written to a container’s writable layer is lost when you
docker rmit. Persist important data in volumes instead.
Mapping ports
Containers run on an isolated network. To reach a service from the host, publish its port with -p HOST:CONTAINER:
docker run -d -p 8080:80 nginx:1.27 # host 8080 -> container 80
docker run -d -p 127.0.0.1:5432:5432 postgres:16 # bind to localhost only
Without -p, the port is reachable only from other containers on the same Docker network.
Persisting data with volumes
Volumes store data outside the container’s lifecycle. Use a named volume for managed storage, or a bind mount to map a host directory:
docker volume create pgdata
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16 # named volume
docker run -d -v "$(pwd)/site:/usr/share/nginx/html" nginx:1.27 # bind mount
Named volumes are portable and managed by Docker; bind mounts are ideal for local development where you want live source edits reflected in the container.
Passing environment variables
Configure containers at runtime with -e, or load many from a file with --env-file:
docker run -d -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=app postgres:16
docker run -d --env-file ./.env myapp:1.0
Image layers and tags
Images are built from stacked, read-only layers, each produced by an instruction in the build. Layers are content-addressed and cached: if two images share a base, that base is stored once and reused. This is why pulling a second image from the same base is fast.
Inspect the layer history of any image:
docker history nginx:1.27
Tags are mutable pointers to a specific image version. nginx:1.27 is precise; nginx:latest floats to whatever was most recently published.
Avoid deploying
:latestto production — it is non-deterministic. Pin explicit versions (or even digests likenginx@sha256:...) so deployments are reproducible.
Best Practices
- Pin image tags to explicit versions; never rely on
:latestin production. - Treat containers as disposable — store state in volumes or external services.
- Use
--nameand labels so containers are easy to identify and clean up. - Bind sensitive ports to
127.0.0.1unless they must be externally reachable. - Prune unused resources regularly with
docker system pruneto reclaim disk space. - Prefer official or verified base images and keep them updated for security patches.