Docker Contexts and Remote Hosts
Drive a Docker host across the network from your own machine, using SSH rather than an exposed daemon port. Includes the host-key failure you will hit first, and why opening port 2375 is the mistake to avoid.
Operations and Troubleshooting Guide 35 of 46 Intermediate
- OSUbuntu 26.04 LTS (resolute)
- Docker Engine29.7.2
- Docker Compose5.4.0
- Architectureamd64
- TimeAbout 12 min
- Reviewed22 August 2026
Tested on the versions above. Captured between two real hosts - commands were issued on ahm-docker03 and executed by the daemon on ahm-docker02.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| DOCKER01 | 192.168.0.21 | Ubuntu 26.04 LTS | Docker Host (client) | 2 Core | 4 GB | 50 GB |
| DOCKER02 | 192.168.0.22 | Ubuntu 26.04 LTS | Remote Docker Host (daemon) | 2 Core | 4 GB | 50 GB |
Before you start
- A second Docker host you can reach over SSH with key-based authentication.
- Comfort with the CLI - guide 6 in this path.
-
What a context is
A context is a named endpoint the CLI talks to. You always have one -
default, pointing at the local Unix socket. Adding contexts lets one CLI drive several hosts, and switching between them changes where every subsequent command runs.bash docker context lsNAME DESCRIPTION DOCKER ENDPOINTdefault * Current DOCKER_HOST based configuration unix:///var/run/docker.sockdocker context inspect default --format "{{.Endpoints.docker.Host}}"unix:///var/run/docker.sockExpected resultOne context, marked active with an asterisk, pointing at the local socket.
Success conditionYou can see where your commands currently go. The asterisk is the active context.
-
Create a context for a remote host over SSH
SSH is the right transport: it authenticates, encrypts, and reuses access you already manage. There is no daemon port to expose and no certificate to issue. The remote host needs nothing configured beyond an SSH login that can reach its Docker socket.
bash docker context create cg-remote --docker host=ssh://sysadmin@192.168.0.22 --description "docker02 over SSH"cg-remoteSuccessfully created context "cg-remote"Expected resultThe context created immediately - note it does not connect yet.
Success conditionCreation always succeeds because nothing is validated at this point. The first command that uses it is where problems surface.
-
The failure you will hit first
Creating the context does not test it. The first real command does, and on a host that has never connected to the target this is what you get. It is SSH's host-key check, not a Docker problem at all - which is why the message is easy to misread.
bash docker --context cg-remote info --format "{{.Name}}"error during connect: ... command [ssh -l sysadmin -o ConnectTimeout=30 -T -- 192.168.0.22 docker system dial-stdio] has exited with exit status 255, make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=Host key verification failed.# the operative words are at the very end: Host key verification failedExpected resultA long error whose real cause is the last line.
Success conditionYou read to the end. Docker shells out to
ssh, so anything SSH cannot do becomes a Docker error - fix it by making plainsshto that host work first. -
Make plain SSH work, then retry
The rule for every context problem: if
ssh user@host docker versiondoes not work from a terminal, the context will not work either. Fix the SSH layer on its own, then come back.bash ssh sysadmin@192.168.0.22 docker version --format "{{.Server.Version}}"# make this succeed first - key auth, known host, user in the docker groupdocker --context cg-remote info --format "remote: {{.Name}} / Docker {{.ServerVersion}} / {{.OperatingSystem}}"remote: ahm-docker02 / Docker 29.7.2 / Ubuntu 26.04 LTSExpected resultThe remote host identifying itself.
Success conditionThe name returned is the REMOTE host, not yours. That is the confirmation the context is genuinely connected.
-
Run commands against the remote host
--contextapplies to a single command, which is the safest way to work: no ambiguity about where it landed. Note the local hostname and the reported Docker host differ - that contrast is the whole point.bash hostnameahm-docker03docker --context cg-remote info --format "{{.Name}}"ahm-docker02docker --context cg-remote images --format "{{.Repository}}:{{.Tag}}" | head -3nginx:alpinenginx:alpinealpine:3.22Expected resultLocal hostname one thing, Docker host another, and the remote host's images listed.
Success conditionYou are administering another machine with no shell session open on it. The images listed are the remote host's.
-
Switching the active context, and why to be careful
docker context usechanges the default for every subsequent command in every terminal. It is convenient and it is how people accidentally rundocker rm -fagainst production. Prefer--contextper command, and if you do switch, switch back.bash docker context use cg-remoteCurrent context is now "cg-remote"# every plain docker command now targets the REMOTE hostdocker info --format "commands now run against: {{.Name}}"commands now run against: ahm-docker02docker context use defaultback to: ahm-docker03Expected resultThe active context switching the target of an otherwise identical command.
Success conditionYou switched and switched back.
docker context lsshows the asterisk - check it before anything destructive. -
Do not expose the daemon on a TCP port
The alternative to SSH is binding the daemon to a TCP port, and
2375is plain HTTP with no authentication whatsoever. Anyone who can reach that port has root on the host, because they can mount the root filesystem into a container. Confirm yours is not listening.bash sudo ss -ltn | grep -E ":2375|:2376" || echo "no TCP daemon socket listening - correct"no TCP daemon socket listening - correctExpected resultNothing listening on either port.
Success conditionYour daemon is not reachable over the network except through SSH. If you find 2375 open, treat it as an incident rather than a configuration preference.
-
Clean up
Contexts persist in your CLI configuration until removed. Remove any you created for a one-off task, particularly ones pointing at production.
bash docker context rm cg-remotecg-remotedocker context ls --format "{{.Name}}"defaultExpected resultOnly the default context remaining.
Success conditionThe context is gone. You cannot remove the context that is currently active - switch to default first, which is why the previous step mattered.
Troubleshooting
Host key verification failed
Why: The local machine has never connected to that host and cannot verify its identity.
Fix:Connect once with plain ssh and accept the key, or add it deliberately with ssh-keyscan after checking the fingerprint.
bash ssh-keyscan -H 192.168.0.22 >> ~/.ssh/known_hostsPermission denied connecting to the remote daemon
Why: The SSH user can log in but is not in the docker group on the remote host, so it cannot read the socket.
Fix:Add the user to the docker group there - remembering that this grants root-equivalent access on that machine.
bash ssh sysadmin@192.168.0.22 'id -nG | tr " " "\n" | grep -x docker'Commands ran against the wrong host
Why: An active context was left switched, or DOCKER_HOST is set in the environment and overrides it.
Fix:Check both. An environment variable silently wins over the selected context, which is a nasty way to find out.
bash docker context lsecho "DOCKER_HOST=$DOCKER_HOST"The SSH context is noticeably slow
Why: Every command opens a new SSH connection and repeats the handshake.
Fix:Enable SSH connection multiplexing so subsequent commands reuse an open channel.
bash # ~/.ssh/configHost 192.168.0.22 ControlMaster auto ControlPath ~/.ssh/cm-%r@%h:%p ControlPersist 10m