## Intro

Docker contexts let you switch between Docker daemons, endpoints, and environments without constantly editing configuration files or environment variables. A context bundles the Docker host endpoint, TLS settings, and default namespace so one command can point to the local engine, a remote SSH host, or a Kubernetes endpoint. When a context is missing, misconfigured, or pointing at the wrong endpoint, the failure is usually fast and confusing: the CLI reports errors like `Cannot connect to the Docker daemon`, `context not found`, or commands silently run against the wrong environment.

This guide focuses on the most common Docker context errors and the concrete fixes for them. It is written for developers, DevOps consultants, and technical startup teams who need to move from an observed problem to a verified result without guessing. Every section includes the commands to inspect current state, the expected output, and the recovery steps. The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify the result, and document how to recover if the expected state is not reached.

You do not need a lab or a deep Docker internals background. A working Docker CLI and the ability to run the commands below are enough. However, if your environment uses Docker Desktop, Docker Compose, or a remote daemon, some prerequisites apply, and those are called out in the sections where they matter.

## Version and Environment Inventory

Before fixing a context error, record the exact version and topology. This prevents you from applying a fix for Docker 20.10 to a system running Docker 24.0 and from changing the wrong context. Run these read-only commands and save the output:

```bash
docker version
```

This prints both the client and server (daemon) version. If the server line is missing, the CLI cannot reach the current context's daemon.

```bash
docker context ls
```

This lists all contexts with a marker (`*`) for the current one. For example:

```
NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT                KUBERNETES ENDPOINT   ORCHESTRATOR
default *           moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
remote-ssh          ssh                 SSH context to staging                    ssh://deploy@10.0.0.5
```

If the expected context is missing, someone may have deleted it, or the configuration directory was replaced. If the expected default endpoint is different, `DOCKER_HOST` or `DOCKER_CONTEXT` may be overriding the current context.

```bash
docker context inspect <context-name>
```

This shows the full configuration of one context. Inspect a context before changing it:

```bash
docker context inspect remote-ssh
```

```json
[
    {
        "Name": "remote-ssh",
        "Metadata": {},
        "Endpoints": {
            "docker": {
                "Host": "ssh://deploy@10.0.0.5",
                "SkipTLSVerify": false
            }
        },
        "TLSMaterial": {},
        "Storage": {
            "MetadataPath": "/home/user/.docker/contexts/meta/abc123...",
            "TLSPath": "/home/user/.docker/contexts/tls/abc123..."
        }
    }
]
```

Inspect also reveals whether TLS material is configured. If the endpoint is `https://` but the context has no TLS material, connections will fail with certificate errors.

Confirm the current context and the environment variables that can override it:

```bash
docker context show
echo $DOCKER_HOST
echo $DOCKER_CONTEXT
```

Docker's resolution order is: `--context` flag (per command) > `DOCKER_CONTEXT` environment variable > `current` context from the CLI config. `DOCKER_HOST` still applies to the `default` context but can cause confusion when other contexts are used.

For Compose projects, run `docker compose ps`, `docker compose logs -f <service>`, and `docker compose exec <service> sh` only after confirming the current context points to the machine where the Compose file's containers should run.

Data persistence across contexts is a common hidden failure. A named volume such as `app_data:/var/lib/app` is managed by Docker and is usually easier to reuse across container rebuilds. A bind mount such as `./data:/var/lib/app` maps a host directory directly and is useful for local development, but it can expose permission, portability, and backup problems if the same path does not exist on another machine. Contexts do not change volumes, but they change which daemon the containers run on. Before switching contexts, verify where the data lives:

```bash
# On the current context
docker volume ls
docker volume inspect app_data
```

A production-like local test should include a restart test: stop the container, recreate it, and confirm the application still sees the expected files. If the data disappears, the service was probably writing to the container filesystem instead of a volume or mount.

## Safe Configuration Path

Most context errors are fixed by updating the context configuration itself. The safe path is: list contexts, inspect the current one, create or update a context with explicit parameters, then switch to it and verify with a harmless command.

Start by identifying the contexts and the current one:

```bash
docker context ls
docker context show
```

If you need to connect to a remote Docker daemon over SSH, create a new context with the `ssh` type:

```bash
docker context create remote-prod --docker "host=ssh://produser@prod.example.com"
```

For a Docker daemon exposed over HTTPS with TLS, create a context and then set the TLS material:

```bash
docker context create secure-remote --docker "host=https://prod.example.com:2376"
docker context update secure-remote --docker "ca=$PWD/ca.pem,cert=$PWD/cert.pem,key=$PWD/key.pem"
```

Note: The paths in `ca`, `cert`, and `key` are stored in the context metadata. If the files are later moved, Docker still stores copies of the TLS material in the context's storage directory, and it will use those copies. To deliberately use new TLS material, run `docker context update` again with the new paths.

If you need to update the endpoint of an existing context (for example, the SSH host changed):

```bash
docker context update existing-context --docker "host=ssh://newuser@newhost"
```

To remove an obsolete context:

```bash
docker context rm old-context
```

If the context you try to remove is currently active, Docker returns an error. Switch to another context first:

```bash
docker context use default
docker context rm old-context
```

After changing a context, always verify it without altering anything:

```bash
docker context use remote-prod
docker ps
```

`docker ps` should list running containers on the remote daemon. If it fails, inspect the context and the daemon reachability.

One of the most common mistakes is to modify the `current` context entry in the `~/.docker/config.json` file by hand. The `current` key is updated by the `docker context use` command and should not be edited manually. Hand-editing can create a context name that does not match any entry in `contexts`, leading to `context not found` errors on every command. If you suspect this has happened, use `docker context use <existing-context>` to reset the current pointer.

## Verification and Diagnostics

After a context change, verification should involve both the context configuration and the ability to perform a real operation against the selected daemon. Use read-only commands first.

Check the current context and its endpoint:

```bash
docker context show
docker context inspect $(docker context show) --format '{{.Endpoints.docker.Host}}'
```

Then test the connection with a command that does not change state:

```bash
docker info
```

`docker info` prints details about the daemon, including the number of containers, images, and the server version. This is a strong signal that the CLI can communicate. If it hangs, the endpoint is unreachable. If it returns a TLS error, check the certificates in the context.

For a container that should be running, check its status:

```bash
docker ps --filter "name=my-app"
```

If the container is not listed, check all containers including stopped ones:

```bash
docker ps -a --filter "name=my-app"
```

If the container exited, inspect its logs and exit code:

```bash
docker logs my-app --tail 100
docker inspect my-app --format '{{.State.ExitCode}} {{.State.Error}}'
```

Diagnostics should always separate observation from intervention. Capture the current state and timestamps first, protect credentials and private material, then change one scoped item only when its blast radius and recovery path are understood.

For Compose projects, the equivalent checks are:

```bash
docker compose ps
docker compose logs -f <service>
docker compose exec <service> sh
```

But remember that Compose will use the current context. If `docker compose up -d` does nothing visible, run `docker compose ps` and check which daemon it reports. If it reports containers on the local engine while you expected the remote staging host, the current context is wrong.

Example of a context mismatch:

```
$ docker context ls
NAME       DESCRIPTION                               DOCKER ENDPOINT
default *  Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
staging    Staging server                            ssh://deploy@staging.example.com

$ docker compose up -d
[+] Running 2/2
 ✔ Network myapp_default  Created
 ✔ Container myapp-db-1   Started
```

But when you check the remote staging server, the containers are not there. The current context was `default`, not `staging`. The fix is to switch and re-run:

```bash
docker context use staging
docker compose up -d
```

Also verify the context for individual container operations:

```bash
docker context use staging
docker ps
```

If `docker ps` still shows local containers, check `DOCKER_HOST` or `DOCKER_CONTEXT`:

```bash
echo $DOCKER_HOST
echo $DOCKER_CONTEXT
```

If `DOCKER_HOST` is set to a non-empty value, it may override the context for the `default` context only; other contexts are unaffected. If `DOCKER_CONTEXT` is set, it overrides the current context for all commands. Unset it if it is causing confusion:

```bash
unset DOCKER_CONTEXT
```

For remote SSH contexts, SSH key authentication must be set up correctly. If `docker ps` returns `permission denied (publickey)`, check that the user and key are correct:

```bash
ssh -T produser@prod.example.com
```

If SSH works but Docker commands fail, the remote user may not have permission to access the Docker socket. On the remote host, add the user to the `docker` group or use an SSH user with the right permissions.

## Failure Modes and Recovery

This section covers the most frequently observed failure modes for Docker contexts and the recovery steps.

### 1. Context not found

**Symptom:** Running any Docker command returns an error like:

```
context not found: my-context
```

**Cause:** The `current` context in `~/.docker/config.json` refers to a context name that does not exist in the `contexts` map. This can happen after a manual edit, a partial migration, or a tool that saved an invalid config.

**Recovery:** List the available contexts and switch to a valid one:

```bash
docker context ls
docker context use default
```

If the context you need is genuinely missing, recreate it from known parameters, as shown in Safe Configuration Path.

### 2. Cannot connect to the Docker daemon

**Symptom:**

```
error during connect: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json: dial unix /var/run/docker.sock: connect: no such file or directory
```

or for remote contexts:

```
error during connect: Get https://prod.example.com:2376/v1.24/containers/json: net/http: TLS handshake timeout
```

**Cause:** The current context points to a daemon that is not running, not reachable, or has a network firewall blocking the port. For local contexts, the Docker daemon may not be started (common on Linux after a reboot). For remote contexts, the host may be down, or the TLS port is blocked.

**Recovery:**

- For local: start Docker (`sudo systemctl start docker` on Linux, or start Docker Desktop).
- For remote: test network connectivity and daemon status on the remote host.

```bash
# From local
ping prod.example.com
nc -zv prod.example.com 2376

# On remote host
sudo systemctl status docker
```

If the port is blocked, adjust firewall rules or use an SSH context instead.

### 3. TLS certificate verification failed

**Symptom:**

```
x509: certificate signed by unknown authority
```

**Cause:** The context is configured for HTTPS with TLS verification, but the CA certificate is missing, incorrect, or expired. The server certificate may not match the hostname.

**Recovery:** Inspect the context TLS material:

```bash
docker context inspect <context-name>
```

Check the CA certificate expiration:

```bash
openssl x509 -in /path/to/ca.pem -noout -dates
```

Update the context with the correct certificates:

```bash
docker context update <context-name> --docker "ca=/new/ca.pem,cert=/new/cert.pem,key=/new/key.pem"
```

Do not set `SkipTLSVerify` to true in production; that disables verification and allows man-in-the-middle attacks. If the endpoint is internal and uses self-signed certs, add the correct CA to the context.

### 4. Wrong context silently used

**Symptom:** Commands succeed but affect the wrong daemon. You might deploy to localhost despite intending to deploy to staging.

**Cause:** The current context is not what you assumed, or an environment variable overrides it.

**Recovery:** Before any destructive action, check the current context:

```bash
docker context show
```

If it is wrong, switch:

```bash
docker context use <intended-context>
```

To prevent surprises, adopt a practice of always running `docker context show` at the start of a deployment script, or use the `--context` flag explicitly on every Docker command in scripts:

```bash
docker --context staging ps
```

This flag takes precedence over the current context and environment variables, making the target explicit.

### 5. Context created with wrong endpoint type

**Symptom:** You created a context with `host=tcp://...` but the remote daemon requires SSH. Connection fails with protocol errors.

**Cause:** The endpoint type or protocol is incorrect.

**Recovery:** Recreate the context with the correct type. For SSH:

```bash
docker context rm wrong-context
docker context create right-context --docker "host=ssh://user@host"
```

If the daemon supports both TCP and SSH, prefer SSH for built-in encryption and key authentication.

## Operations Checklist

Use this checklist before and after any context-related change. Each item names the accountable owner (an individual, not a group) and the recommended frequency of review.

| Step | Action | Command / Signal | Owner | Review Frequency |
|------|--------|------------------|-------|------------------|
| 1 | Record current context and Docker version | `docker context show && docker version` | DevOps lead (e.g., Priya Shah, Engineering Lead) | Every release or monthly |
| 2 | List all contexts and inspect the one you plan to change | `docker context ls && docker context inspect <name>` | Same as above | Every change |
| 3 | Create or update context with explicit endpoint and TLS material | `docker context create/update ...` | DevOps lead | Every change |
| 4 | Switch to the new context and verify with a read-only command | `docker context use <name> && docker ps` | Developer performing the change | Every change |
| 5 | Unset or document environment overrides | `unset DOCKER_CONTEXT DOCKER_HOST` or update team docs | DevOps lead | Monthly |
| 6 | Test data persistence with a restart on the target daemon | `docker stop <container> && docker start <container>` | Developer | Before any production migration |
| 7 | Run a full application smoke test against the chosen context | `docker compose up -d && curl -f http://localhost:health` | QA owner | Every deployment |
| 8 | Document the current context topology in the team wiki | Markdown page with context names and endpoints | DevOps lead | Quarterly |

Each checklist item should be performed in order. If any verification fails, stop and diagnose before proceeding. The owner listed is accountable for ensuring the step is performed correctly and re-performed at the stated frequency.

## Common Pitfalls and How to Avoid Them

This section summarizes the most common mistakes that lead to Docker context errors, along with practical advice to avoid or recover from them.

### Pitfall 1: Editing `~/.docker/config.json` by hand

**Why it happens:** It seems straightforward to change the `current` context or add a context entry directly in the JSON file.

**How to avoid:** Always use `docker context` commands (`create`, `update`, `use`, `rm`, `inspect`). They validate the input and maintain consistency.

**Recovery if done:** Run `docker context ls` to see if Docker can parse the config. If not, restore from backup or delete the file (losing all contexts but allowing a fresh start). Then recreate contexts.

### Pitfall 2: Ignoring environment variable overrides

**Why it happens:** `DOCKER_HOST` and `DOCKER_CONTEXT` are often set in shell profiles or CI pipelines and forgotten.

**How to avoid:** Check these variables before running Docker commands, especially in scripts. Use `--context` flag to force the intended context.

**Recovery:** `unset DOCKER_HOST DOCKER_CONTEXT` or set them to the correct values. In CI, ensure the environment is clean.

### Pitfall 3: Using `SkipTLSVerify=true` to silence certificate errors

**Why it happens:** It immediately gets rid of TLS errors.

**How to avoid:** Always provide correct CA certificates and server certificates. Use `SkipTLSVerify=false` in any environment with real data.

**Recovery:** Update the context with proper TLS material and set `SkipTLSVerify=false`.

### Pitfall 4: Assuming data volumes move across contexts

**Why it happens:** The same container name and image may be running on different daemons, but volumes are daemon-local.

**How to avoid:** Before switching contexts, backup volumes or use a storage driver that can be attached to the target daemon.

**Recovery:** Copy data from the old daemon's volume to the new one using `docker run --rm -v app_data:/data alpine tar czf - /data | ssh target 'docker run --rm -i -v app_data:/data alpine tar xzf -'`.

### Pitfall 5: Creating a context with a misspelled endpoint or user

**Why it happens:** Typos in the host string or username are common.

**How to avoid:** Test SSH connectivity before creating the context: `ssh -T user@host`.

**Recovery:** Update the context with the correct endpoint or delete and recreate.

## Conclusion

Docker context errors are usually straightforward to fix once you know how to inspect the current configuration and verify the connection to the intended daemon. The key is to move deliberately: record the current state, identify the error precisely, apply the smallest change that could fix it, and verify with a read-only command before doing anything destructive.

This guide covered the version and environment inventory needed to establish a baseline, the safe configuration path for creating and updating contexts, diagnostics for verification, specific failure modes with recovery steps, an operations checklist with clear ownership and review cadence, and common pitfalls with avoidance strategies.

As a next step, choose one low-risk verification for your own environment. Run `docker context show` and `docker ps` on each context you use regularly. Compare the results with your expectation. If any context points to the wrong endpoint or fails to connect, follow the recovery steps in this guide. Then review your team's use of environment variables like `DOCKER_CONTEXT` and `DOCKER_HOST` to prevent silent mismatches.

A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. With the commands and checks in this guide, you can keep Docker contexts working reliably and avoid the common traps that waste hours of debugging.