## Intro

When Docker works, it feels invisible: a container starts, serves traffic, and shuts down on command. When it breaks, the error message can read like a riddle. This guide collects the Docker CLI errors most often seen in development and production, explains what each message actually means, and gives you the exact fix plus the command to verify it.

We cover Docker for Linux, macOS, and Windows, including Docker Desktop and Docker Engine, with special attention to docker compose and common image, container, network, and volume failures. Each section starts with the error text you will see, then shows the cause, the fix, and a later check to confirm the problem is gone.

This is not a reference manual. It is a troubleshooting path: observe first, change one thing, verify, then move on. You can use it during an incident or as a checklist before your next deploy.

## 1. Environment and Version Inventory

Before fixing anything, know your version and context. Many errors are version-specific: a flag that works in Docker 23 may not exist in Docker 20, and Docker Desktop behaves differently from Docker Engine on Linux.

Start with a read-only inventory:

```bash
docker version
docker info
```

`docker version` shows client and server versions. If the server version is missing, your client cannot reach the daemon. `docker info` prints the storage driver, logging driver, root directory, and importantly the number of running containers and images. It also shows warnings: no swap limit support, no cgroup v2, or a missing bridge network.

For Docker Context, which can silently target a remote or rootless daemon, list contexts:

```bash
docker context ls
```

If you see a context named `remote-prod` that you forgot to switch away from, every `docker run` command will try to use that remote daemon. Switch back with `docker context use default`.

Practical inventory commands:

```bash
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
docker images | head -n 10
docker system df
```

The first shows running containers with names, status, and ports; the second shows recently built or pulled images; the third shows disk usage by images, containers, volumes, and build cache. `docker system df` is often the first hint that a build is failing because the disk is full (error: `no space left on device`).

For a Compose project:

```bash
docker compose ps
docker compose config
```

The `config` command validates and prints the merged compose file, showing environment variable substitution and default values. A common mistake is to edit `docker-compose.yml` but run `docker compose` in a different directory; `docker compose ps` will list no containers, and you may think the service is down.

As a rule: record the output of these commands before changing anything. You need a baseline to compare against.

## 2. Safe Configuration Path

Configuration errors are the most common source of Docker CLI failures. The fix is to change one configuration file or one environment variable at a time, then validate.

### 2.1 Docker daemon configuration (`daemon.json`)

Most daemon settings live in `/etc/docker/daemon.json` on Linux or under Settings > Docker Engine in Docker Desktop. A missing comma or wrong key will prevent the daemon from starting. On Linux, after editing, run:

```bash
sudo dockerd --validate
```

This checks the syntax without starting the daemon. Then restart Docker:

```bash
sudo systemctl restart docker
```

If the daemon fails to start, read the journal:

```bash
sudo journalctl -u docker --since "10 minutes ago" -f
```

A typical error: `failed to start daemon: error initializing graphdriver: /var/lib/docker contains several valid graphdrivers`. This means you changed the storage driver in `daemon.json` but existing data uses another driver. The safe path is to revert the change and plan a migration.

### 2.2 Compose files

Compose files are YAML, and indentation and version fields matter. Always run:

```bash
docker compose config --quiet
```

No output means the file is valid. If there is an error, it prints the line and reason. `--quiet` is non-destructive and fast.

Common mistakes:

- Using a mapping for `environment` like `VAR: value` instead of a list. Compose v3 expects a list:
  ```yaml
  environment:
    - POSTGRES_USER=myuser
    - POSTGRES_PASSWORD=mypass
  ```
- Forgetting to quote strings with special characters, e.g. `$` in passwords. Unquoted `$VAR` will try to expand the host environment.
- Using `version: "3.9"` but the installed Compose plugin only supports up to 3.8. Remove the `version` field entirely for recent Docker Compose versions; it is optional and can cause a warning.

### 2.3 Environment variables in CLI

When passing variables to a container, `docker run -e` or `--env-file` must use `key=value`. A trailing space after a comma in `--env-file` will break parsing. Prefer:

```bash
docker run --env-file ./config.env myapp
```

And inside `config.env`, one variable per line, no spaces around `=`.

## 3. Verification and Diagnostics

After any fix, verify the component works as expected. Use read-only commands first, then logs, then an interactive shell if needed.

### 3.1 Check container state

```bash
docker ps --filter "status=exited" --format "{{.Names}}\t{{.ExitCode}}"
```

This lists exited containers and their exit codes. A non-zero exit code often points to the reason. Then inspect one:

```bash
docker inspect <container> --format '{{.State.ExitCode}} {{.State.Error}} {{.State.OOMKilled}}'
```

If `OOMKilled` is true, the container hit a memory limit. Check the limit with `docker inspect <container> | grep -i memory`, then raise it in the run command (`--memory=2g`) or in compose (`mem_limit: 2g`).

### 3.2 Read logs

```bash
docker logs <container> --tail 100 --timestamps
```

Use `--since 10m` to see only recent entries. If the container restarts rapidly, follow with `docker logs -f <container>` to catch the failure before the next restart (the container may be scheduled to restart by a restart policy).

For a Compose service:

```bash
docker compose logs <service> --tail 200
```

### 3.3 Run an interactive debug shell

Sometimes you need to see the filesystem or test a network connection inside the container. Do not modify the image; run a new shell with the same configuration:

```bash
docker run --rm -it --entrypoint sh <image>
```

This starts a fresh container from the image with an overridden entrypoint. Then you can `ls`, `cat` config files, or `curl` a local endpoint.

For a running container, use `docker exec`:

```bash
docker exec -it <container> sh
```

Be careful: any changes you make in an exec session are not saved to the image and will be lost when the container restarts. Use this for observation only.

### 3.4 Network diagnostics

To test connectivity between containers, run a tiny Alpine container with `ping` or `curl`:

```bash
docker run --rm --network <mynetwork> alpine ping <other-container-name>
```

If the ping fails, check that both containers are on the same network: `docker network inspect <mynetwork>` and look at the `Containers` map. Names are only resolvable on user-defined networks, not on the default bridge.

## 4. Failure Modes and Recovery

Here are the most common Docker CLI errors, their causes, and fixed recovery steps.

### 4.1 `Cannot connect to the Docker daemon at unix:///var/run/docker.sock`

Cause: the Docker daemon is not running, or your user lacks permission to access the socket.

Fix:

- Check if the daemon is running: `systemctl status docker` on Linux, or open Docker Desktop on macOS/Windows.
- On Linux, if the daemon is running but you get permission denied, add your user to the `docker` group:
  ```bash
  sudo usermod -aG docker $USER
  ```
  Log out and back in, or run `newgrp docker` to refresh group membership without logging out.
- If you use rootless Docker, set the `DOCKER_HOST` environment variable to the user socket: `export DOCKER_HOST=unix:///run/user/1000/docker.sock`.

Verify: `docker info` should return without error.

### 4.2 Pull access denied / repository does not exist

Error examples: `pull access denied for myimage, repository does not exist or may require 'docker login'`.

Fix:

- For public images, ensure the name is correct: `docker pull alpine` (not `Alpine` or `alpine:latest` if the tag is wrong).
- For private registries, log in first:
  ```bash
  docker login registry.example.com
  ```
  Then pull with the full path: `docker pull registry.example.com/team/myimage:tag`.
- If you are using Docker Desktop and the registry is on your host (e.g. `localhost:5000`), you may need to add an insecure registry entry in daemon settings or configure a TLS cert.

Verify: `docker pull` succeeds and the image appears in `docker images`.

### 4.3 Port already in use

Error: `docker: Error response from daemon: driver failed programming external connectivity on endpoint ... Bind for 0.0.0.0:8080 failed: port is already allocated.`

Fix:

- Find what is using the port on the host: `sudo ss -ltnp | grep 8080` or `sudo lsof -i :8080`.
- Stop the conflicting process (if possible) or change the host port: `docker run -p 8081:8080` or in compose:
  ```yaml
  ports:
    - "8081:8080"
  ```
- If the conflict is with another container, remove or stop that container, or use a dynamic port: `docker run -P` (maps container port to a random high host port).

Verify: `docker ps` shows your container with the chosen host port, and a `curl http://localhost:8081` returns the expected response.

### 4.4 Container exits immediately

Your `docker run` command started, but `docker ps` shows no container. `docker ps -a` shows it exited with code 0 or 1.

Common causes:

- The container's main process finished. For example, a one-shot script needs a foreground process to stay alive.
- The command is missing. If you run `docker run myimage` and the image has no `CMD`, the container exits.
- The app crashes due to missing environment variables, configuration, or dependency.

Fix:

- Always run with `-it` for interactive testing: `docker run -it myimage sh` and manually start the app to see errors.
- Check logs: `docker logs <container>`.
- For a service that should stay up, ensure the command is a long-running process like `nginx -g 'daemon off;'` or `tail -f /dev/null` for testing.
- In compose, set `restart: unless-stopped` to auto-restart after crashes, but still check logs for root cause.

### 4.5 Can't find file or directory

Errors: `no such file or directory` when mounting a volume, or inside the container the expected file is missing.

Cause: host path does not exist, or the path inside the container is wrong, or the file is not copied because it's in `.dockerignore`.

Fix:

- For bind mounts, ensure the host directory exists before `docker run`. Docker will create it as root-owned if missing, leading to permission issues. Create it yourself: `mkdir -p ./data`.
- For COPY in a Dockerfile, check `.dockerignore` does not exclude the file.
- For volumes, check if the volume has data: `docker volume inspect myvolume` and look at the mountpoint. You can copy data into the volume by running a one-off container with the volume mounted.
- Inside the container, use `ls` and `pwd` to verify the working directory. The default working directory is `/` unless set with `WORKDIR` in the Dockerfile or `-w` on the CLI.

## 5. Common Pitfalls and How to Recover

Even experienced Docker users hit these surprises.

### 5.1 Editing files inside a container without a volume

You `docker exec` into a container, modify a config file, restart the container, and the change is gone. Why? The container filesystem is ephemeral and does not persist by default.

Avoid: never make manual changes inside a running container that you expect to survive. Instead, either:

- Build the change into the image via Dockerfile, or
- Mount a volume or bind mount for that file/directory, and edit the file on the host.

If you already made an emergency change and need it to survive until the next deploy, commit the container to a temporary image:

```bash
docker commit <container> myapp:hotfix
```

But treat this as a stopgap, not a workflow. The image lacks the build context and documentation.

### 5.2 Using `latest` tag in production

`docker pull myapp:latest` may get a different version tomorrow, and the deployment breaks.

Recover: always pin versions in compose files and CI/CD. Use specific tags like `myapp:1.2.3` or a digest. If you used `latest` in a running deployment, you can inspect the exact image with `docker inspect <container> --format '{{.Image}}'`, then find the digest and recreate the container with that digest.

### 5.3 Forgetting to clean up resources

Over time, dangling images, stopped containers, unused networks, and volumes accumulate and fill the disk.

Fix: schedule regular cleanup. Run these manually:

```bash
docker container prune   # remove stopped containers
docker image prune       # remove dangling images
docker network prune     # remove unused networks
docker volume prune      # remove unused volumes
```

Or use `docker system prune -a` to remove all unused resources, but be careful: this deletes all stopped containers, unused networks, and images not used by any container. It does not delete named volumes by default; add `--volumes` to do so. This is a destructive operation; run it only when you understand the impact.

### 5.4 Ignoring health checks

A container may be "running" but not ready to serve requests. Docker's `HEALTHCHECK` instruction in the Dockerfile or `healthcheck` in compose can mark a container as unhealthy.

Recover: add a health check. Example for a web service:

```yaml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
  interval: 10s
  timeout: 5s
  retries: 3
  start_period: 30s
```

Then use `docker inspect --format '{{.State.Health.Status}}' <container>` to see `healthy`, `unhealthy`, or `starting`. In compose, `docker compose ps` shows the health status.

### 5.5 Not using a restart policy in production

If a container dies and nothing restarts it, your service is down until someone manually intervenes.

Add a restart policy:

```bash
docker run --restart unless-stopped myapp
```

Or in compose:

```yaml
restart: unless-stopped
```

`unless-stopped` restarts the container unless you explicitly stopped it. `always` also restarts after the daemon restarts, but `unless-stopped` is usually safer because it respects manual maintenance.

## 6. Operations Checklist

Use this checklist before and after any Docker change in a shared environment.

### Before the change

- [ ] Run `docker context ls` and confirm you are targeting the correct daemon (default, not a remote prod).
- [ ] Record current state: `docker ps`, `docker images`, `docker system df`, `docker compose ps` (if applicable). Save output to a file with timestamp.
- [ ] Identify the exact resource you will change: image, container, network, volume, or configuration file.
- [ ] Determine the blast radius: is this a single container or a whole stack? What depends on it?
- [ ] Prepare a rollback path: can you revert the config change, redeploy the previous image tag, or restore a volume snapshot?
- [ ] For config file changes, validate syntax first: `docker compose config --quiet` or `dockerd --validate`.

### After the change

- [ ] Run the verification command that proves the fix worked, e.g. `curl http://localhost:8080/health` or `docker logs <container> --tail 50` shows no errors.
- [ ] Check dependent services: do they still connect? Is the network intact? `docker network inspect` shows expected containers.
- [ ] Monitor for a few minutes: `docker stats <container>` shows CPU and memory, `docker events` shows lifecycle changes.
- [ ] Update the runbook or incident document with the error, cause, fix, and verification.
- [ ] Schedule a review if the fix is temporary: set a reminder to revisit the issue in, for example, one week or before the next release.

Single accountable owner: in a team context, assign one person (e.g. the on-call engineer or the developer who made the change) to be responsible for completing the checklist and documenting the outcome. For recurring operational tasks, rotate this ownership weekly.

## Conclusion

Docker CLI errors are usually caused by a small set of recurring issues: daemon connectivity, image pull failures, port conflicts, exiting containers, and file or volume misconfigurations. The path to recovery is the same each time: observe the state, isolate the failing component, make one surgical change, and verify with a specific command.

Keep this guide handy as a reference for the next error you see. Start with `docker version` and `docker info`, then proceed to the matching error section. After the fix, run the verification command and record what you did. That turns an incident into a lesson.

For further reading, explore the official Docker documentation on CLI commands, daemon configuration, and Compose specifications. The more you practice the observe-change-verify loop, the faster you will solve future Docker problems.