---
title: "Top 60+ Docker Commands Every Developer MUST Know in 2025"
description: "Master 60+ Docker commands in this updated 2025 guide. Covers everything from basic containers and Compose v2 to buildx, disk cleanup, and health checks."
date: 2026-07-30
categories: ["self-hosting"]
tags: ["docker","docker-compose"]
---

import Notice from "@components/widgets/Notice.astro";
import ListCheck from "@components/widgets/ListCheck.astro";
import Tabs from "@components/widgets/Tabs.astro";
import Tab from "@components/widgets/Tab.astro";

If you ship software in containers, you use Docker commands every day. Whether you run a few services on a [home server](/docker-containers-home-server/) or manage [self-hosted Docker containers for business](/docker-containers-business/), knowing the CLI saves time and prevents outages.

This guide covers 60+ Docker commands: basic image and container operations, Compose v2, buildx multi-platform builds, disk cleanup, health checks, and registry authentication. Everything is updated for Docker Engine 28.x/29.x (2025). If you're still seeing `docker-compose` with a hyphen in old tutorials, that's dead and gone.

## Quick-reference cheat sheet

| Category | Command | Description |
|----------|---------|-------------|
| **Info** | `docker version` | Show client and server version |
| **Info** | `docker info` | System-wide Docker information |
| **Images** | `docker pull <image>:<tag>` | Download image from registry |
| **Images** | `docker images` | List local images |
| **Images** | `docker rmi <image>` | Remove an image |
| **Images** | `docker image prune` | Remove dangling images |
| **Images** | `docker build -t <name> .` | Build image from Dockerfile |
| **Images** | `docker tag <src> <dst>` | Tag an image |
| **Images** | `docker push <image>` | Push image to registry |
| **Images** | `docker history <image>` | Show image layer history |
| **Containers** | `docker run -d --name <n> <img>` | Run container in background |
| **Containers** | `docker run -it <img> /bin/bash` | Run interactive container |
| **Containers** | `docker ps` | List running containers |
| **Containers** | `docker ps -a` | List all containers |
| **Containers** | `docker stop <container>` | Stop a running container |
| **Containers** | `docker start <container>` | Start a stopped container |
| **Containers** | `docker restart <container>` | Restart a container |
| **Containers** | `docker rm <container>` | Remove a stopped container |
| **Containers** | `docker exec -it <c> /bin/bash` | Run command in container |
| **Containers** | `docker logs -f <container>` | Follow container logs |
| **Containers** | `docker inspect <container>` | Low-level container info |
| **Containers** | `docker stats` | Live resource usage |
| **Containers** | `docker top <container>` | Running processes in container |
| **Containers** | `docker cp <c>:/path /host` | Copy files from container |
| **Containers** | `docker update --restart=always <c>` | Update restart policy live |
| **Networking** | `docker network ls` | List networks |
| **Networking** | `docker network create <name>` | Create a network |
| **Networking** | `docker network connect <net> <c>` | Connect container to network |
| **Volumes** | `docker volume ls` | List volumes |
| **Volumes** | `docker volume create <name>` | Create a volume |
| **Volumes** | `docker run -v <vol>:/path <img>` | Mount a volume |
| **Compose** | `docker compose up -d` | Start services (detached) |
| **Compose** | `docker compose down` | Stop and remove services |
| **Compose** | `docker compose ps` | List compose services |
| **Compose** | `docker compose logs -f` | Follow compose logs |
| **Compose** | `docker compose --profile <p> up` | Start specific profile |
| **Buildx** | `docker buildx ls` | List builder instances |
| **Buildx** | `docker buildx build --platform linux/amd64,linux/arm64` | Multi-platform build |
| **Cleanup** | `docker system df` | Show disk usage |
| **Cleanup** | `docker system prune -a` | Remove all unused resources |
| **Cleanup** | `docker system prune -a --volumes` | Nuclear cleanup (includes volumes) |
| **Registry** | `docker login` | Authenticate to registry |
| **Registry** | `docker logout` | Log out from registry |
| **Advanced** | `docker save -o file.tar <img>` | Save image to tar |
| **Advanced** | `docker load -i file.tar` | Load image from tar |
| **Advanced** | `docker export <c> > file.tar` | Export container filesystem |
| **Advanced** | `docker import file.tar` | Import container filesystem |
| **Advanced** | `docker context create` | Manage remote Docker daemons |

## Prerequisites

Before running these commands:

<ListCheck>
- Docker Engine 24+ installed (28.x or 29.x recommended)
- Your user is in the `docker` group, or you're using `sudo`
- Verify the daemon is running: `docker version` should show both Client and Server sections
</ListCheck>

```sh
$ docker version
Client:
 Version:           28.1.1
 API version:       1.49
 Go version:        go1.23.8
 OS/Arch:           linux/amd64

Server:
 Engine:
  Version:          28.1.1
  API version:      1.49 (minimum version 1.24)
  Go version:       go1.23.8
  OS/Arch:          linux/amd64
```

<Notice type="warning" title="Common startup errors">
"Cannot connect to the Docker daemon" means the service isn't running. Fix with `sudo systemctl start docker && sudo systemctl enable docker`. "permission denied" means your user isn't in the docker group. Fix with `sudo usermod -aG docker $USER` then log out and back in.
</Notice>

Output shown is from Docker 28.x. Your version numbers will differ: that's fine. The commands are stable across versions.

## Section 1: Basic Docker Commands

### 1.1 Docker Version and Info

Understanding what version of Docker you're running and the system-wide configuration is the first step.

- **`docker --version`**: quick check that Docker is installed and which version.

  ```sh
  $ docker --version
  Docker version 28.1.1, build 4eba7b2
  ```

- **`docker version`**: detailed client and server version info (shown in Prerequisites above).

- **`docker info`**: system-wide overview: container/image counts, storage driver, logging driver, kernel version, total memory.

  ```sh
  $ docker info
  Containers: 5
   Running: 3
   Paused: 0
   Stopped: 2
  Images: 12
  Storage Driver: overlay2
  Logging Driver: json-file
  Cgroup Driver: systemd
  Cgroup Version: 2
  Kernel Version: 6.8.0-49-generic
  Operating System: Ubuntu 24.04.1 LTS
  CPUs: 4
  Total Memory: 7.771GiB
  ```

  If `Server:` section is missing, the Docker daemon isn't running.

### 1.2 Docker Help

Docker has built-in help for every command:

- **`docker --help`**: lists all available commands and management commands.

- **`docker <command> --help`**: detailed options for a specific command. This is the most useful one. For example:

  ```sh
  $ docker run --help

  Usage:  docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

  Run a command in a new container

  Options:
    -d, --detach                         Run container in background
    --name string                        Assign a name to the container
    -p, --publish list                   Publish a container's port(s) to the host
    -v, --volume list                    Bind mount a volume
    --rm                                 Remove container when it exits
    -i, --interactive                    Keep STDIN open
    -t, --tty                            Allocate a pseudo-TTY
    --restart string                     Restart policy (no, always, unless-stopped, on-failure)
    --env list                           Set environment variables
    --network string                     Connect to a network
    --health-cmd string                  Health check command
    --init                               Run an init process inside the container
    --gpus gpu-request                   GPU devices to add to the container
  ```

  When in doubt, `docker <cmd> --help` is faster than searching the web.

## Section 2: Working with Docker Images

### 2.1 Pulling Images

Pulling images from a registry is usually the first step.

- **`docker pull <image>`**: downloads the `latest` tag by default.

  ```sh
  $ docker pull ubuntu
  Using default tag: latest
  latest: Pulling from library/ubuntu
  6d28e14ab8c8: Pull complete
  Digest: sha256:abc123...
  Status: Downloaded newer image for ubuntu:latest
  docker.io/library/ubuntu:latest
  ```

- **`docker pull <image>:<tag>`**: pull a specific version.

  ```sh
  $ docker pull nginx:alpine
  alpine: Pulling from library/nginx
  Digest: sha256:def456...
  Status: Downloaded newer image for nginx:alpine
  docker.io/library/nginx:alpine
  ```

<Notice type="warning" title="Docker Hub rate limits">
Unauthenticated pulls are limited to 100 per 6 hours per IPv4 address (or IPv6 /64 subnet). Free authenticated accounts get 200 per 6 hours. If you hit rate limits in CI/CD, run `docker login` first. Paid subscriptions get unlimited pulls.
</Notice>

### 2.2 Listing Images

- **`docker images`**: list all local images.

  ```sh
  $ docker images
  REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
  nginx        alpine    a1b2c3d4e5f6   2 weeks ago    43MB
  ubuntu       latest    f6e5d4c3b2a1   3 weeks ago    77.9MB
  ```

- **`docker images -a`**: include intermediate build layers.

- **`docker images --filter "dangling=true"`**: show untagged images not referenced by any container. These are safe to remove.

### 2.3 Removing Images

- **`docker rmi <image>`**: remove an image by name or ID.

  ```sh
  $ docker rmi nginx:alpine
  Untagged: nginx:alpine
  Deleted: sha256:a1b2c3d4e5f6...
  ```

  If a container is still using the image, you'll get "image is being used by running container": stop and remove the container first.

- **`docker image prune`**: remove all dangling images (untagged). Add `-a` to remove all unused images.

For a deeper dive into reclaiming disk space from Docker's storage layer, see [how to reclaim disk space from Docker overlay2](/clean-docker-overlay2-dir/).

### 2.4 Building Images

- **`docker build -t <name>:<tag> .`**: build an image from a Dockerfile in the current directory.

  ```sh
  $ docker build -t myapp:1.0 .
  [+] Building 12.5s (8/8) FINISHED
  ```

<Notice type="info" title="BuildKit is now the default">
Since Docker Engine 23.0 (February 2023), BuildKit is the default builder. You no longer need `DOCKER_BUILDKIT=1`. BuildKit gives you faster builds, better caching, and multi-stage build support out of the box.
</Notice>

- **`docker build --no-cache -t myapp:1.0 .`**: rebuild from scratch, ignoring layer cache. Useful when dependency versions change.

- **`docker build -f Dockerfile.prod -t myapp:prod .`**: use a specific Dockerfile.

For advanced multi-platform builds and build secrets, see [Section 8: Modern Build with Docker Buildx](#section-8-modern-build-with-docker-buildx). To learn about build args and [Docker environment variables (ARG vs ENV)](/docker-env-vars/), check the dedicated guide. You can also learn how to [copy multiple files in one Dockerfile layer](/copy-multiple-files-in-one-layer-using-a-dockerfile/) to reduce image size.

## Section 3: Managing Docker Containers

### 3.1 Running Containers

The `docker run` command is the most important Docker command. Here are the essential variations:

- **`docker run <image>`**: create and start a container. Runs the image's default command.

- **`docker run -d <image>`**: detached mode (background). Returns the container ID.

- **`docker run -it <image> /bin/bash`**: interactive mode with a TTY. Drops you into a shell inside the container.

- **`docker run --name myapp -d -p 8080:80 nginx`**: named container with port mapping (host 8080 → container 80).

- **`docker run --rm -it ubuntu /bin/bash`**: auto-remove the container when it exits. Good for throwaway debugging.

- **`docker run -e MY_VAR=value -d <image>`**: pass environment variables. See [Docker environment variables (ARG vs ENV)](/docker-env-vars/) for the full breakdown.

<Notice type="success" title="Production-ready run command">
Here's a `docker run` that covers the basics for a production service:

```sh
docker run -d \
  --name myapp \
  --restart unless-stopped \
  --health-cmd="curl -f http://localhost:3000/health || exit 1" \
  --health-interval=30s \
  --health-retries=3 \
  -p 3000:3000 \
  myapp:latest
```

This gives you: auto-restart on failure, health monitoring, and port mapping. For more on running apps in Docker, see [deploy FileBrowser with Docker](/deploy-filebrowser-docker/) as a practical example.
</Notice>

Additional flags worth knowing:

| Flag | What it does |
|------|-------------|
| `--restart unless-stopped` | Auto-restart on crash or reboot (stops only if you explicitly stop it) |
| `--health-cmd`, `--health-interval`, `--health-retries` | Define container health checks |
| `--init` | Run tini as PID 1: handles zombie processes cleanly |
| `--shm-size 256m` | Increase shared memory (needed for browsers, PostgreSQL, some ML workloads) |
| `--add-host host.docker.internal:host-gateway` | Let container reach the host machine |
| `--gpus all` | Pass GPU access to container (requires nvidia-container-toolkit) |
| `--user 1000:1000` | Run as a specific UID/GID instead of root |

For managing users inside containers, see [how to add users to a Docker container](/add-users-to-docker-container/). To [run Python apps in Docker](/docker-run-python/), we have a dedicated guide.

### 3.2 Listing Containers

- **`docker ps`**: list running containers.

  ```sh
  $ docker ps
  CONTAINER ID   IMAGE          COMMAND                  STATUS         NAMES
  a1b2c3d4e5f6   nginx:alpine   "/docker-entrypoint.…"   Up 2 hours     web-server
  f6e5d4c3b2a1   postgres:16    "docker-entrypoint.s…"   Up 5 hours     db
  ```

- **`docker ps -a`**: list all containers (including stopped).

- **`docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"`**: custom output format. Cleaner than the default.

- **`docker ps --filter "status=exited"`**: filter by status. Useful for finding containers to clean up.

### 3.3 Stopping and Starting Containers

- **`docker stop <container>`**: graceful stop (SIGTERM, then SIGKILL after timeout). Default timeout is 10 seconds.

- **`docker stop --timeout 30 <container>`**: wait 30 seconds before force-killing. Give apps more time to shut down cleanly.

<Notice type="warning" title="--time is deprecated">
The `--time` flag on `docker stop` and `docker restart` was deprecated in Docker 28.4. Use `--timeout` instead. The old flag still works for now but will be removed in v30.
</Notice>

- **`docker start <container>`**: start a stopped container (preserves its configuration).

- **`docker restart <container>`**: stop then start. Equivalent to `docker stop` + `docker start`.

- **`docker kill <container>`**: immediate force stop (SIGKILL). No graceful shutdown. Use when a container is stuck.

### 3.4 Removing Containers

- **`docker rm <container>`**: remove a stopped container.

- **`docker rm -f <container>`**: force-remove a running container (sends SIGKILL first).

- **`docker container prune`**: remove all stopped containers at once. The interactive version asks for confirmation.

  ```sh
  $ docker container prune --force
  Deleted Containers:
  a1b2c3d4e5f6...
  f6e5d4c3b2a1...

  Total reclaimed space: 152.3MB
  ```

## Section 4: Inspecting and Logging

### 4.1 Inspecting Containers and Images

`docker inspect` returns detailed JSON about any Docker object. The raw output is huge: use Go templates to extract what you need.

- **`docker inspect <container>`**: full JSON output (usually hundreds of lines). Pipe to `jq` for readability.

- **`docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container>`**: get the container's IP address.

- **`docker inspect --format='{{.State.Status}}' <container>`**: get container state (running, exited, etc.).

- **`docker inspect --format='{{json .State.Health}}' <container> | jq`**: get health check status.

- **`docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' <container>`**: check restart policy.

### 4.2 Viewing Logs

- **`docker logs <container>`**: dump all logs.

- **`docker logs -f <container>`**: follow (tail) logs in real time. Press Ctrl+C to stop.

- **`docker logs --tail 100 <container>`**: show last 100 lines only.

- **`docker logs --since 1h <container>`**: logs from the last hour.

- **`docker logs --since 2025-01-15T10:00:00 --until 2025-01-15T11:00:00 <container>`**: logs for a specific time window.

- **`docker logs --timestamps <container>`**: prefix each line with a timestamp.

Combine flags: `docker logs -f --tail 50 --timestamps myapp` is a common pattern for live debugging.

## Section 5: Networking

### 5.1 Managing Networks

Docker networks let containers talk to each other by name (DNS resolution on user-defined bridge networks).

- **`docker network ls`**: list all networks.

  ```sh
  $ docker network ls
  NETWORK ID     NAME      DRIVER    SCOPE
  abc123def456   bridge    bridge    local
  789ghi012jkl   host      host      local
  345mno678pqr   none      null      local
  ```

- **`docker network create mynet`**: create a user-defined bridge network.

- **`docker network create --driver bridge --subnet 172.20.0.0/16 mynet`**: with custom subnet.

- **`docker network rm mynet`**: remove a network.

- **`docker network inspect mynet`**: detailed info about a network (connected containers, IP addresses).

<Notice type="warning" title="Docker bypasses your firewall">
Docker manipulates iptables directly to route container traffic. This means published ports (`-p 8080:80`) can bypass UFW and other host firewalls. If you're exposing containers to the internet, understand the implications. See [Docker bypassing your firewall](/docker-bypasses-firewall/) for the full explanation and workarounds.
</Notice>

### 5.2 Connecting and Disconnecting Containers

- **`docker network connect mynet mycontainer`**: attach a running container to an additional network. The container now has interfaces on both networks.

- **`docker network disconnect mynet mycontainer`**: detach from a network.

- **`docker run --network mynet --name app -d myimage`**: start a container directly on a specific network.

## Section 6: Volumes and Data Management

### 6.1 Managing Volumes

Volumes are the preferred way to persist data. They survive container removal and are managed by Docker.

- **`docker volume ls`**: list all volumes.

- **`docker volume create mydata`**: create a named volume.

- **`docker volume inspect mydata`**: show volume details (mount point on host, driver, labels).

- **`docker volume rm mydata`**: remove a volume. Only works if no container is using it.

### 6.2 Using Volumes

There are two syntaxes for mounting volumes:

- **`-v` (bind mount shorthand)**:

  ```sh
  docker run -v mydata:/var/lib/postgresql/data -d postgres:16
  docker run -v /host/path:/container/path -d myimage
  ```

- **`--mount` (explicit, recommended)**:

  ```sh
  docker run --mount type=volume,source=mydata,target=/var/lib/postgresql/data -d postgres:16
  docker run --mount type=bind,source=/host/path,target=/container/path -d myimage
  ```

Use `--mount` for production: it's more explicit, supports more options, and gives clearer error messages. The `-v` shorthand is fine for quick local dev.

## Section 7: Docker Compose (v2)

<Notice type="error" title="Docker Compose v1 is dead">
`docker-compose` (with a hyphen) was the v1 Python binary. It was deprecated in July 2023 and removed from all Docker Desktop versions. Use `docker compose` (with a space): the v2 Go plugin. If you see old tutorials using `docker-compose`, mentally translate to `docker compose`. The `version:` key in compose.yaml is also obsolete: Compose v2 ignores it.
</Notice>

### 7.1 Compose v2 Basic Commands

<Tabs>
<Tab name="Old v1 (deprecated)">
```sh
docker-compose up -d
docker-compose down
docker-compose ps
docker-compose logs -f
```
</Tab>
<Tab name="New v2 (use this)">
```sh
docker compose up -d
docker compose down
docker compose ps
docker compose logs -f
```
</Tab>
</Tabs>

- **`docker compose up -d`**: start all services in detached mode. Creates containers, networks, and volumes as defined in `compose.yaml`.

- **`docker compose down`**: stop and remove containers and networks. Add `--volumes` to also remove named volumes (data loss!).

- **`docker compose ps`**: list services and their status.

- **`docker compose logs -f`**: follow logs from all services. Add a service name to filter: `docker compose logs -f web`.

- **`docker compose config`**: validate and print the resolved compose file. Useful for catching YAML errors before deploying.

For managing Docker Compose apps in production, tools like [Dockge](/dockge-install/) or [Dokploy](/dokploy-docker-compose-app/) give you a web UI. For [managing secrets in Docker Compose](/docker-compose-secrets/), see the dedicated guide.

### 7.2 Managing Services

- **`docker compose build`**: build or rebuild services that have a `build:` section.

- **`docker compose pull`**: pull the latest images for all services.

- **`docker compose restart`**: restart all services (or a specific one: `docker compose restart web`).

- **`docker compose start` / `docker compose stop`**: start/stop without removing containers. Unlike `up`/`down`, containers persist.

- **`docker compose top`**: display running processes inside each service container.

- **`docker compose exec web /bin/bash`**: exec into a running service container.

- **`docker compose run --rm web python manage.py migrate`**: run a one-off command in a service. `--rm` removes the container after it finishes.

### 7.3 Compose v2 New Features

<Notice type="info" title="Compose watch for development">
`docker compose up --watch` monitors your source files and automatically syncs or rebuilds when they change. This replaces complex bind-mount + nodemon/fswatch setups. Define what to watch in the `develop.watch` section of your compose.yaml. Available since Compose 2.22.0 (GA).
</Notice>

- **`docker compose up --watch`**: start services with file-watch mode. Source changes trigger automatic sync (for static files) or rebuild (for code that affects the image).

- **`docker compose --profile dev up`**: only start services tagged with the `dev` profile. Useful for optional services like debug tools or test databases.

  ```yaml
  services:
    web:
      build: .
    debug-tools:
      image: busybox
      profiles: ["dev"]
  ```

- **`docker compose --env-file .env.staging up`**: use a custom environment file instead of the default `.env`.

## Section 8: Modern Build with Docker Buildx

`docker buildx` is Docker's modern build system. Since Docker 23.0, `docker build` is essentially an alias for `docker buildx build`. But for multi-platform builds, remote caching, and build secrets, you need the full `buildx` interface.

### 8.1 Buildx Basics

- **`docker buildx ls`**: list available builder instances.

  ```sh
  $ docker buildx ls
  NAME/NODE     DRIVER/ENDPOINT     STATUS    BUILDKIT   PLATFORMS
  default       docker                                    linux/amd64, linux/arm64
  ```

- **`docker buildx create --use --name mybuilder`**: create and switch to a new builder. Required for multi-platform builds.

- **`docker buildx build -t myapp:latest .`**: build using the active builder. Same as `docker build` but with buildx features available.

### 8.2 Multi-Platform Builds

Build images for multiple architectures in a single command:

```sh
docker buildx build --platform linux/amd64,linux/arm64 \
  -t myuser/myapp:latest \
  --push .
```

This uses QEMU emulation to cross-compile. It works but emulated builds are slow: an ARM64 build on an x86 host can be 5-10x slower than native.

<Notice type="info" title="Native ARM builders are faster">
If you're building ARM64 images regularly, consider using a native ARM builder. [Hetzner Cloud](https://go.bitdoze.com/hetzner) ARM64 instances (starting around EUR 4/month) work well as remote buildx builders and are much faster than QEMU emulation on x86.
</Notice>

Without `--push`, the image stays local (multi-platform images need a registry; they can't be loaded locally). You can also use `--load` for single-platform builds.

### 8.3 Build Secrets and Remote Cache

- **`docker buildx build --secret id=mytoken,src=./token.txt -t myapp .`**: pass secrets during build without baking them into layers. In your Dockerfile: `RUN --mount=type=secret,id=mytoken cat /run/secrets/mytoken`.

- **`docker buildx build --cache-to type=registry,ref=myuser/myapp:cache --cache-from type=registry,ref=myuser/myapp:cache -t myapp .`**: export and import build cache via a registry. Essential for CI/CD to avoid rebuilding from scratch every time.

- **`docker buildx bake`**: build multiple targets from a `docker-bake.hcl`, JSON, or compose file. Useful for projects with many related images.

- **`docker buildx prune`**: clean the build cache. Add `--all` to nuke everything.

## Section 9: Docker Disk Space and Cleanup

This is the section VPS operators need most. Docker accumulates images, containers, volumes, and build cache fast: especially on cheap VPS instances with limited disk.

### 9.1 Checking Disk Usage

- **`docker system df`**: show disk usage breakdown.

  ```sh
  $ docker system df
  TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
  Images          12        5         3.45GB    2.1GB (60%)
  Containers      8         3         152MB     98MB (64%)
  Local Volumes   6         2         1.2GB     800MB (66%)
  Build Cache     15        0         512MB     512MB (100%)
  ```

- **`docker system df -v`**: verbose breakdown per image, container, volume, and cache entry.

Run this weekly. On a [EUR 5/month Hetzner VPS](https://go.bitdoze.com/hetzner) with 40GB disk, Docker can easily eat 10-15GB if left unchecked.

### 9.2 Pruning Unused Resources

<Notice type="warning" title="Volume prune deletes data permanently">
`docker volume prune` and `docker system prune -a --volumes` permanently delete volume data: databases, uploads, everything stored in volumes. Always verify what will be removed before confirming. Back up volumes first if the data matters.
</Notice>

```sh
# Remove all stopped containers
docker container prune --force

# Remove dangling images (untagged, unreferenced)
docker image prune --force

# Remove ALL unused images (not just dangling)
docker image prune -a --force

# Remove unused volumes (DATA LOSS: be careful)
docker volume prune --force

# Remove unused networks
docker network prune --force

# Remove build cache
docker builder prune --force

# Remove ALL unused resources (images, containers, networks, cache: NOT volumes)
docker system prune -a --force

# The nuclear option: everything unused, including volumes
docker system prune -a --volumes --force
```

### 9.3 Safe Cleanup Strategies for VPS

The safest approach: time-based pruning. Keep recent stuff, clean the rest.

```sh
# Remove unused resources older than 7 days
docker system prune -a --filter "until=168h" --force
```

For a weekly cron job:

```sh
# /etc/cron.d/docker-cleanup
0 3 * * 0 root docker system prune -a --filter "until=168h" --force > /var/log/docker-prune.log 2>&1
```

On budget VPS hosting like [Hetzner](https://go.bitdoze.com/hetzner) or [Hostinger VPS](https://go.bitdoze.com/hostinger-vps), disk is the constraint, not CPU or RAM. A cleanup cron job is cheap insurance. For more approaches, see [how to clean up all Docker resources](/cleanup-all-docker-things/) and [reclaim disk space from Docker overlay2](/clean-docker-overlay2-dir/). If you're running Docker on a managed panel, check the [best self-hosted server panels](/best-self-hosted-panels/) for built-in cleanup tools.

## Section 10: Docker Health Checks and Production Readiness

Health checks let Docker (and orchestrators) know if your application is actually working: not just "the process is running."

### 10.1 Adding Health Checks to Containers

In a Dockerfile:

```dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1
```

Or via `docker run`:

```sh
docker run -d \
  --health-cmd="curl -f http://localhost:3000/health || exit 1" \
  --health-interval=30s \
  --health-timeout=3s \
  --health-start-period=10s \
  --health-retries=3 \
  --name myapp \
  -p 3000:3000 \
  myapp:latest
```

Key parameters:
- `--interval`: how often to run the check (default: 30s)
- `--timeout`: how long to wait for a response (default: 30s)
- `--start-period`: grace period before health checks count as failures (default: 0s)
- `--retries`: consecutive failures before marking unhealthy (default: 3)

In Compose:

```yaml
services:
  web:
    image: myapp:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 3s
      start_period: 10s
      retries: 3
```

### 10.2 Monitoring Container Health

```sh
# Check health status via inspect
docker inspect --format='{{json .State.Health}}' myapp | jq

# Filter containers by health status
docker ps --filter "health=healthy"
docker ps --filter "health=unhealthy"
docker ps --filter "health=starting"
```

Since Docker 29.5.0, `docker ps --format` supports `.HealthStatus` directly:

```sh
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.HealthStatus}}"
```

Health checks are critical when using `--restart` policies. Without them, Docker restarts a broken app that's stuck in a crash loop indefinitely. With a health check, the container reports `unhealthy` and you can act on it.

## Section 11: Docker Hub and Registry Authentication

### 11.1 Docker Login

- **`docker login`**: interactive login to Docker Hub (prompts for username and password).

- **`docker login --username myuser --password-stdin`**: non-interactive for CI/CD:

  ```sh
  echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin
  ```

- **`docker login ghcr.io --username myuser --password-stdin`**: log in to GitHub Container Registry.

  ```sh
  echo "$GITHUB_TOKEN" | docker login ghcr.io --username "$GITHUB_USERNAME" --password-stdin
  ```

- **`docker logout`** / **`docker logout ghcr.io`**: remove stored credentials.

### 11.2 Rate Limits and Registry Mirrors

<Notice type="warning" title="Docker Hub rate limits">
Pull limits (per 6 hours per IP/account):
- **Unauthenticated**: 100 pulls
- **Authenticated free account**: 200 pulls
- **Paid subscription**: Unlimited (fair use)

If your CI/CD pipelines fail with "toomanyrequests" or "rate limit exceeded", authenticate first with `docker login`. For high-volume environments, consider a pull-through cache or registry mirror.
</Notice>

To check your current rate limit status, inspect the response headers:

```sh
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/ubuntu:pull" | jq -r .token)
curl -s -I -H "Authorization: Bearer $TOKEN" "https://registry-1.docker.io/v2/library/ubuntu/manifests/latest" | grep -i ratelimit
```

## Section 12: Advanced Commands

### 12.1 Docker Exec

Run commands inside a running container:

- **`docker exec -it mycontainer /bin/bash`**: interactive shell. Most common usage.

- **`docker exec -it mycontainer sh`**: use `sh` if bash isn't available (Alpine images).

- **`docker exec -u root -it mycontainer /bin/bash`**: exec as a different user.

- **`docker exec mycontainer cat /etc/hosts`**: run a one-off command without interactive mode.

### 12.2 Docker Export and Import

Export a container's filesystem as a tar archive. This captures the filesystem state but **not** volumes, metadata, or the image history.

- **`docker export mycontainer > mycontainer.tar`**: export filesystem.

- **`docker import mycontainer.tar myimage:restored`**: import as a new image.

Use case: migrating a container's filesystem to another host, or creating a minimal image snapshot. For image-level backup, use `save`/`load` instead (preserves metadata).

### 12.3 Docker Save and Load

Save an image (with all layers and metadata) to a tar file:

- **`docker save -o myimage.tar myimage:latest`**: save an image.

- **`docker load -i myimage.tar`**: load an image from a tar file.

Unlike `export`/`import`, `save`/`load` preserves the full image: layers, tags, metadata. Use this for offline transfers or air-gapped environments.

### 12.4 Docker Contexts

Manage connections to multiple Docker daemons from a single machine. No more juggling `DOCKER_HOST` environment variables.

```sh
# List contexts
docker context ls

# Create a context for a remote VPS
docker context create my-vps --docker "host=ssh://user@vps-ip"

# Switch to it
docker context use my-vps

# Now all docker commands target the remote daemon
docker ps
docker compose up -d

# Switch back to local
docker context use default
```

This is useful if you run containers on a remote VPS but develop locally. SSH-based contexts work out of the box: just make sure your SSH key is set up.

### 12.5 Docker Init, Scout, and Other Modern Commands

<Notice type="info" title="Desktop-only commands">
`docker init` and `docker scout` require Docker Desktop or separate plugin installation on headless Linux servers. On a typical VPS, they may not be available out of the box.
</Notice>

- **`docker init`**: scaffold a Dockerfile, compose.yaml, and .dockerignore for your project. Walks you through an interactive wizard. Only available in Docker Desktop (4.18+).

- **`docker scout quickview <image>`**: quick vulnerability scan of an image.

- **`docker scout cves <image>`**: list CVEs found in an image. Requires the Docker Scout plugin.

- **`docker cp <container>:/path /host/path`**: copy files from a container to the host. Works both directions: `docker cp /host/file container:/path`.

- **`docker update --restart=always <container>`**: change the restart policy on a running container without recreating it.

- **`docker update --cpus 2 --memory 512m <container>`**: adjust CPU and memory limits on a running container.

- **`docker port <container>`**: list port mappings for a container.

- **`docker rename <old-name> <new-name>`**: rename a container.

- **`docker history <image>`**: show the build history of an image (each layer's size and command).

- **`docker stats --no-stream`**: one-shot resource usage snapshot. Useful in scripts:

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

For running AI CLI tools and coding agents safely inside containers, see [safe Docker environments for AI CLI tools](/docker-podman-ai-cli-tools-safe-environment/).

## Troubleshooting Common Docker Errors

| Error | Cause | Fix |
|-------|-------|-----|
| `Cannot connect to the Docker daemon` | Daemon not running | `sudo systemctl start docker` |
| `permission denied while trying to connect` | User not in docker group | `sudo usermod -aG docker $USER` then re-login |
| `no space left on device` | Disk full | Run `docker system df` then `docker system prune -a`. See [Docker cleanup guide](/cleanup-all-docker-things/) |
| `port is already allocated` | Port in use | `docker ps` to find the container using it, then `docker stop` or change `-p` mapping |
| `toomanyrequests` / rate limit | Too many unauthenticated pulls | `docker login` first, or use a registry mirror |
| `network xxx not found` | Compose project name changed | Run `docker compose down` cleanly before renaming, check `docker network ls` |
| `image is being used by running container` | Can't remove an image in use | Stop and remove the container first: `docker rm -f <container>` |

## Wrapping Up

You now have a reference covering 60+ Docker commands: from daily container management to Compose v2, buildx multi-platform builds, disk cleanup, health checks, and registry authentication. Bookmark the quick-reference cheat sheet table at the top for fast lookups.

Docker changes fast. The biggest shifts since 2023: Compose v1 is dead, BuildKit is default, and the containerd image store is the new default for fresh Docker 29 installs. If you're still running old habits, now is a good time to update.

For more Docker guides, check out [100+ best Docker containers for a home server](/docker-containers-home-server/): it's a solid starting point for figuring out what to actually run.