Docker: Essential Commands with Examples

14.04.2026
10:00

How Docker Actually Works

Docker sits on top of Linux kernel features. Namespaces give each container its own process tree, network, and filesystem. Cgroups enforce resource limits. The practical result is that you can run many services on a single server without them interfering - different Node.js versions, conflicting Python packages, whatever.

Images are layered structures. Each instruction in a Dockerfile adds a layer. If a layer hasn't changed since the last build, Docker reuses it from cache. That's why rebuilding after a small change is fast - only the changed layers get rebuilt. A container is a running image with one thin writable layer on top. Remove the container, the image stays intact.

Working with Images

Pulling from Docker Hub. Not specifying a tag gets you latest, which is whatever the maintainer last pushed - not ideal for anything that needs to stay stable:

docker pull nginx:1.25-alpine
docker pull ubuntu:22.04

Alpine images are significantly smaller but missing bash and common tools. Fine for production, annoying when you need to debug something inside. Worth deciding upfront which matters more for your use case.

Building an image from a Dockerfile. Without -t it gets no name and becomes hard to reference later:

docker build -t myapp:1.0 .
docker build -t myapp:1.0 -f Dockerfile.prod .
docker build -t registry.example.com/myapp:latest .

Listing what's local, tagging for a private registry, pushing:

docker images
docker images nginx
docker tag myapp:1.0 registry.example.com/myapp:1.0
docker login registry.example.com
docker push registry.example.com/myapp:1.0

Removing images. If a container still references the image, remove the container first or use -f to force:

docker rmi nginx:1.25-alpine
docker rmi -f myapp:1.0

Running a Container

The most flag-rich command. Without anything extra it starts and holds the terminal. For almost everything real you want background mode plus a name - otherwise the container gets a random hash that's annoying to work with:

docker run -d --name my-nginx nginx
docker run -d -p 8080:80 --name my-nginx nginx

Port mapping is host:container. The left side is what you reach from outside the server.

A more complete example with environment variables, a mounted volume, resource limits, and restart policy:

docker run -d \
  --name my-app \
  -p 3000:3000 \
  -e NODE_ENV=production \
  -e DB_HOST=postgres \
  -v /srv/dаta:/app/data \
  --memory 512m \
  --cpus 1.5 \
  --restart unless-stopped \
  myapp:1.0

The restart policy here means the container comes back after server reboots, but not if you stopped it manually. Useful for anything that should run continuously. The opposite is --rm, which deletes the container as soon as it exits - good for one-shot commands or scripts.

Getting an interactive shell inside a freshly pulled image:

docker run -it ubuntu:22.04 bash

Container Lifecycle After Start

Listing what's running. The -a flag includes stopped containers, which pile up if you don't clean regularly:

docker ps
docker ps -a
docker ps -a --filter "status=exited"

Stopping sends SIGTERM first, then waits 10 seconds before sending SIGKILL. That window lets a well-behaved application finish open transactions and close connections. Extend the timeout if your app needs longer:

docker stop my-nginx
docker stop -t 30 my-nginx

Starting a stopped container reuses the existing one with all its settings - different from docker run which creates a new container each time:

docker start my-nginx
docker restart my-nginx

Removing containers. Force-remove a running one with -f. The subshell trick cleans up all exited containers at once:

docker rm my-nginx
docker rm -f my-nginx
docker rm $(docker ps -aq -f status=exited)

Checking What's Happening

Container logs - stdout and stderr combined. The -f flag tails them live. -t adds timestamps, which helps when you're sequencing events across multiple containers:

docker logs -f my-nginx
docker logs --tail 100 my-nginx
docker logs --since 2h my-nginx
docker logs -t my-nginx

Running commands inside a live container. Bash isn't always available in minimal images - sh usually is:

docker exec -it my-nginx bash
docker exec -it my-nginx sh
docker exec my-nginx nginx -t

Inspect dumps the full configuration and state as JSON. When something's wrong with networking or volumes, this is where you find out what Docker actually configured vs what you thought you configured:

docker inspect my-nginx
docker inspect --format '{{.NetworkSettings.IPAddress}}' my-nginx
docker inspect --format '{{.State.Status}}' my-nginx

Real-time resource usage. --no-stream takes a single snapshot, useful in scripts:

docker stats my-nginx
docker stats --no-stream
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

Networks

Three default networks exist: bridge handles most containers, host connects the container directly to the server's network stack, none gives the container no network access at all. For inter-container communication, a custom network is cleaner - containers find each other by name instead of IP address:

docker network create mynet
docker network ls
docker network inspect mynet
docker network connect mynet my-nginx
docker network disconnect mynet my-nginx
docker network rm mynet

Volumes for Data That Survives

Everything written inside a container is gone when the container is removed. Volumes are Docker-managed storage that persists independently. Unlike bind mounts, they're not tied to a specific path on the host filesystem - Docker manages the location:

docker volume create mydata
docker volume ls
docker volume inspect mydata
docker volume rm mydata
docker volume prune

Attaching a volume at container start, two equivalent syntaxes:

docker run -d -v mydаta:/var/lib/postgresql/data postgres:16
docker run -d --mount source=mydata,target=/var/lib/postgresql/data postgres:16

Disk Usage and Cleanup

Images, stopped containers, volumes, and build cache accumulate quietly. Check where space is going before you start deleting things:

docker system df
docker system df -v

Remove everything unused. -a extends the sweep to include images with no running containers, not just dangling ones. --volumes gets the volumes too:

docker system prune
docker system prune -a
docker system prune -a --volumes

Practical Example: Static Files Behind nginx

Serve a local directory through nginx on port 8080, with automatic restart on failure:

mkdir -p /srv/static
echo "<h1>Working</h1>" > /srv/static/index.html

docker run -d \
  --name static-web \
  -p 8080:80 \
  -v /srv/static:/usr/share/nginx/html:ro \
  --restart unless-stopped \
  nginx:1.25-alpine

curl http://localhost:8080
docker logs -f static-web

The :ro suffix makes the mount read-only inside the container. Update the nginx config without rebuilding the image - copy, validate, restart:

docker cp ./nginx.conf static-web:/etc/nginx/nginx.conf
docker exec static-web nginx -t
docker restart static-web

Common Questions

What's the actual difference between docker stop and docker kill?

docker stop sends SIGTERM first and waits up to 10 seconds. That window lets applications flush pending writes and close database connections cleanly. docker kill sends SIGKILL immediately with no waiting period. On production servers, always use stop unless the container is genuinely stuck and not responding.

How do I check which ports a container has mapped?

docker port my-container is the fastest way. The same information shows up in docker ps under the PORTS column, and in docker inspect at .NetworkSettings.Ports if you need the full detail.

Can I change environment variables on a running container?

No. Environment variables are set when the container is created and stay fixed. To change them, stop the container, remove it, and recreate it with new -e values. This is why people use config files or external secret managers for values that change.

What are dangling images and should I care about them?

Dangling images are untagged leftover layers from previous builds - what happens when you rebuild an image with the same tag. They don't cause problems but waste disk space. Clean them with docker image prune, or they'll get swept up by docker system prune.

How do I pass multiple environment variables at once?

Chain -e flags in one command: docker run -e KEY1=val1 -e KEY2=val2 myapp. Or point to a file: docker run --env-file .env myapp, where .env has one KEY=VALUE per line. The file approach is cleaner when you have more than a few variables.

How do I copy files between a container and the host?

docker cp container:/internal/path /host/path works in both directions and with stopped containers. Handy for grabbing log files or configs out of a container you're about to remove.

Other articles

14.04.2026
4
Knowledge base / Review
Understanding Why Laravel Is So Popular
14.04.2026
8
Knowledge base / Review / System
Overview of the Mailcoach Email Marketing Platform