docker

Docker Contexts: Manage Remote Docker Hosts Over SSH and TLS

By Shubhankar Tripathi • • 5 min read

Docker Contexts: Manage Remote Docker Hosts

Most engineers start with Docker on a single laptop. Soon there is more than one engine to manage: a rootless daemon next to the normal one, a staging server, a build machine, a production host. The usual workaround is to SSH into each machine and run docker there, or to keep exporting different DOCKERHOST values and hope you remember which one is active.

Docker contexts solve this cleanly. A context is a named connection profile that tells your local Docker CLI which Docker engine to talk to and how to reach it. You create a context once for each engine, then switch between them with one command, or target a specific engine for a single command, all from your own terminal.

This guide explains what Docker contexts are, how to create contexts for remote hosts over SSH and TLS, how to switch and share them, how Docker Compose and builds behave with a remote context, and how to work safely when one of your contexts is production. All examples are written for Docker Engine 29 and Docker Compose v5, as of October 2026.

Quick Answer

  • Create a context for a remote host over SSH: docker context create prod --docker "host=ssh://deploy@prod.example.com"

  • Switch to it: docker context use prod. Every following docker command now runs against the remote engine.

  • Run one command against it without switching: docker --context prod ps

  • Go back to your local engine: docker context use default

  • See all contexts and which one is active: docker context ls

What Is a Docker Context?

The docker command you type is only a client. It sends API requests to a Docker daemon (dockerd), which does the real work of pulling images and running containers. Normally the client and the daemon are on the same machine and talk through a local socket, but the client can just as easily talk to a daemon on another machine.

A Docker context stores everything the client needs to reach one particular daemon:

  • A name and an optional description, such as staging or prod-mumbai.

  • An endpoint: the address of the daemon, for example a Unix socket, an ssh:// address or a tcp:// address.

  • TLS material, when the endpoint uses TLS: the CA certificate, client certificate and client key.

Every installation has a built-in context named default, which points to the local daemon. On Linux this is unix:///var/run/docker.sock. Docker Desktop adds its own context, typically named desktop-linux, and the rootless setup tool adds one named rootless.

Situation

Typical context name

Endpoint

Local Docker Engine on Linux

default

unix:///var/run/docker.sock

Docker Desktop (macOS, Windows, Linux)

desktop-linux

Docker Desktop's own socket

Rootless Docker on the same machine

rootless

unix:///run/user/1000/docker.sock

Remote server over SSH

Your choice, for example staging

ssh://user@host

Remote server over TLS

Your choice, for example build

tcp://host:2376 with certificates


Why Use Docker Contexts?

  • One terminal for every engine. Manage your laptop, staging and production Docker hosts without opening a separate SSH session for each.

  • Use your local tools and files. Your editor, Compose files and scripts stay on your machine while containers run elsewhere.

  • Named, explicit targets. docker --context staging compose up -d is clearer and safer than relying on whichever DOCKERHOST happens to be exported.

  • Easy to share. Contexts can be exported to a file and imported by teammates or CI systems.

  • No exposed ports needed. SSH contexts reuse the SSH access you already have, so the Docker API never has to listen on the network.

Docker Context Commands at a Glance

Command

What it does

docker context ls

List contexts; the active one is marked with *

docker context show

Print only the name of the active context

docker context create <name> --docker "host=..."

Create a new context

docker context use <name>

Make a context the default for future commands

docker --context <name> <command>

Use a context for a single command only

docker context inspect <name>

Show a context's full configuration as JSON

docker context update <name> --description "..."

Change a context's description or endpoint

docker context rm <name>

Delete a context

docker context export <name>

Save a context to a .dockercontext file

docker context import <name> <file>

Create a context from an exported file


Method 1: Connect to a Remote Host Over SSH (Recommended)

SSH is the simplest and safest way to reach a remote Docker engine. The Docker API stays on the remote machine's local socket; the CLI opens an SSH connection and runs Docker's built-in docker system dial-stdio helper on the remote host to tunnel API requests through it. Nothing new is exposed on the network.

Prerequisites

  • Docker Engine installed on the remote host, including the docker CLI, because the SSH connection runs it there. Our guide How to Install Docker on Ubuntu, Windows (WSL2) and macOS covers this.

  • An SSH user on the remote host that can access the Docker socket, normally by being a member of the docker group, or a user running rootless Docker.

  • Key-based SSH authentication. Password prompts do not work well for the many short connections the CLI makes, so use an SSH key, ideally loaded into ssh-agent.

  • The remote host key already trusted in your ~/.ssh/knownhosts file. Connecting once with plain ssh takes care of this.

Step 1: Test Plain SSH Access

ssh deploy@staging.example.com docker version

If this prints both the Client and Server sections without asking for a password, the remote side is ready. If it reports permission denied while trying to connect to the Docker daemon socket, add the user to the docker group on the remote host:

ssh admin@staging.example.com "sudo usermod -aG docker deploy"

Security reminder: Membership of the docker group is equivalent to root access on that host. Use a dedicated deployment user, protect its SSH key carefully, and consider rootless Docker on shared servers.

Step 2: Create the Context

docker context create staging \

  --description "Staging server (Mumbai)" \

  --docker "host=ssh://deploy@staging.example.com"

If SSH listens on a non-standard port, include it in the address, for example host=ssh://deploy@staging.example.com:2222. You can also use a host alias defined in ~/.ssh/config, which lets you keep the port, user and key file there:

# ~/.ssh/config

Host staging

  HostName staging.example.com

  User deploy

  Port 2222

  IdentityFile ~/.ssh/ided25519deploy

docker context create staging --docker "host=ssh://staging"

Step 3: Use the Context

docker context ls

docker context use staging

docker ps

docker info --format "{{.Name}}"

docker info now reports the remote host's name, which confirms that commands go to the remote engine. Switch back to your local engine when you are done:

docker context use default

Step 4: Make SSH Contexts Fast

Each docker command opens a new SSH connection, which adds a noticeable delay. SSH connection multiplexing reuses one connection for many commands. Add these lines to ~/.ssh/config, either globally or inside the relevant Host block:

ControlMaster  auto

ControlPath    ~/.ssh/control-%C

ControlPersist yes

After the first command, later commands reuse the open connection and respond almost instantly. ControlPersist also accepts a duration such as 10m if you prefer connections to close after a period of inactivity.

Method 2: Connect to a Remote Host Over TLS

In some environments SSH access is not available or not allowed, for example on dedicated CI build hosts. In that case the Docker daemon can listen on a TCP port protected by mutual TLS: the client verifies the server certificate and the server accepts only clients presenting a certificate signed by the same certificate authority (CA). By convention Docker over TLS uses port 2376.

Never expose an unencrypted Docker API: A daemon listening on TCP without TLS client verification (commonly on port 2375) gives anyone who can reach that port full root control of the host. Attackers actively scan the internet for such endpoints. Always use SSH or mutual TLS, and restrict the port with a firewall.

Step 1: Prepare Certificates

You need a CA certificate plus a server certificate and key for the daemon, and a client certificate and key for each user or system. Docker's documentation page "Protect the Docker daemon socket" walks through generating them with OpenSSL. You end up with these files:

Where

Files

Purpose

Docker host

ca.pem, server-cert.pem, server-key.pem

Prove the server's identity and verify clients

Client machine

ca.pem, cert.pem, key.pem

Verify the server and prove the client's identity


Protect the private keys with chmod 0400 and the certificates with chmod 0444. Treat client keys like a root password: whoever holds them controls the Docker host.

Step 2: Configure the Daemon to Listen With TLS

On the Docker host, add the TLS settings and listening addresses to /etc/docker/daemon.json:

{

  "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"],

  "tls": true,

  "tlsverify": true,

  "tlscacert": "/etc/docker/certs/ca.pem",

  "tlscert": "/etc/docker/certs/server-cert.pem",

  "tlskey": "/etc/docker/certs/server-key.pem"

}

On systemd-based distributions such as Ubuntu, the packaged docker.service already passes a -H option to dockerd, which conflicts with the hosts setting in daemon.json and prevents the daemon from starting. Override the start command so that daemon.json is the only source of listening addresses:

sudo systemctl edit docker.service

[Service]

ExecStart=

ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock

sudo systemctl daemon-reload

sudo systemctl restart docker

The empty ExecStart= line clears the original command before setting the new one. Then allow TCP port 2376 in your firewall only from the client addresses that need it.

Step 3: Create a TLS Context on the Client

docker context create build \

  --description "CI build host" \

  --docker "host=tcp://build.example.com:2376,ca=$HOME/.docker/build/ca.pem,cert=$HOME/.docker/build/cert.pem,key=$HOME/.docker/build/key.pem"

Docker copies the certificate files into its own context storage, so the context keeps working even if you later move the original files. Verify the connection:

docker --context build version

Switching Between Contexts

There are four ways to choose which engine a command uses. Pick the one that matches how long you want the choice to last:

Method

Example

Scope

docker context use

docker context use staging

All future commands in every terminal, until changed (saved in ~/.docker/config.json)

DOCKERCONTEXT variable

export DOCKERCONTEXT=staging

The current terminal session only

--context flag

docker --context staging ps

A single command

DOCKERHOST variable or -H flag

DOCKERHOST=ssh://deploy@host docker ps

Bypasses contexts entirely and connects to the given address


Watch out for DOCKERHOST: If DOCKERHOST is set in your environment, it overrides the context selected with docker context use, and the CLI warns you about it. Many "my context switch did nothing" problems come from an old DOCKERHOST line in ~/.bashrc or a CI variable. Run unset DOCKERHOST and check docker context ls again.

For production, prefer the --context flag or DOCKERCONTEXT in a dedicated terminal over a global docker context use. A globally selected production context is easy to forget, and the next routine docker system prune runs on production.

Show the Active Context in Your Shell Prompt

Seeing the active context at all times is one of the best ways to avoid accidents. For Bash, add this to ~/.bashrc:

dockerctx() {

  docker context show 2>/dev/null

}

PS1='[docker:$(dockerctx)] \u@\h:\w\$ '

Your prompt now begins with [docker:default] or [docker:staging]. Prompt frameworks such as Starship and Oh My Zsh themes also offer a ready-made Docker context indicator. Note that docker context show reports the context from docker context use or DOCKERCONTEXT; it does not reflect a DOCKERHOST override.

Using Docker Compose With a Remote Context

Docker Compose uses the active context automatically, so you can deploy a stack to a remote host from your laptop:

docker --context staging compose up -d

docker --context staging compose ps

docker --context staging compose logs -f api

Keep in mind where things happen: the Compose file is read on your machine, but containers, volumes and networks are created on the remote engine. This has some important consequences:

Feature

Behaviour with a remote context

Bind mounts (./data:/data)

The path refers to the remote host's filesystem, not your laptop. Your local files are not copied.

Named volumes

Created and stored on the remote host.

Published ports (8080:80)

Opened on the remote host, not on your laptop.

build: sections

Your local build context is uploaded to the remote engine and built there, which can be slow over a slow network.

envfile and variable interpolation

Read from your local machine when Compose parses the file.


For production deployments, the most reliable pattern is to build and push images in CI, then reference them by tag in the Compose file. The remote host only pulls images, and no source code or build context travels over the connection.

Building Images on a Remote Engine

docker build also follows the active context. This is useful when your laptop is slow or has a different CPU architecture from the target servers. For example, from an Apple silicon Mac you can build a native amd64 image on a remote x86 server:

docker --context build build -t registry.example.com/myapp:1.4.0 .

docker --context build push registry.example.com/myapp:1.4.0

The image is built and stored on the remote engine, not on your machine, which is why it is pushed from there. For a fuller remote build setup, Buildx can create builders on top of contexts, for example docker buildx create --name remote-builder build.

Sharing Contexts With Your Team and CI

Export a context to a file and import it on another machine:

docker context export staging

# creates staging.dockercontext in the current directory

 

docker context import staging staging.dockercontext

An exported TLS context contains the client certificate and private key, so treat the file as a secret and share it only through a secure channel. An exported SSH context contains only the address; each person still needs their own SSH key and access on the remote host, which is usually the better model for teams.

In CI pipelines, the simplest approach is to create the context during the job from secrets, then remove it when the job ends:

docker context create target --docker "host=ssh://deploy@prod.example.com"

docker --context target compose pull

docker --context target compose up -d

docker context rm target

Where Docker Stores Contexts

  • Context metadata (name, description, endpoint) is stored in meta.json files under ~/.docker/contexts/meta/.

  • TLS certificates and keys for TLS contexts are stored under ~/.docker/contexts/tls/.

  • The currently selected context is recorded as currentContext in ~/.docker/config.json.

Back up or copy these locations with care, especially the TLS directory, which contains private keys.

Security Best Practices for Remote Docker Access

  • Prefer SSH contexts. They reuse hardened SSH access and keep the Docker API off the network.

  • Never expose the Docker API without TLS client verification, and firewall port 2376 to known client addresses only.

  • Use a dedicated, non-personal deployment user with key-only SSH authentication on each host.

  • Separate environments clearly. Use explicit names such as prod-mumbai and descriptions, and show the active context in your prompt.

  • Use --context for production commands instead of switching your global context.

  • Rotate and revoke credentials when people leave: remove their SSH keys or client certificates from the hosts.

  • Remember that Docker access is root access. Anyone who can use a context to reach an engine can control that host.

Troubleshooting Docker Contexts

Error or symptom

Likely cause

Fix

Context switch has no effect

DOCKERHOST is set and overrides the context

Run unset DOCKERHOST and remove it from shell startup files

Host key verification failed

The remote host is not in knownhosts

Connect once with ssh user@host and accept the host key

Permission denied (publickey)

SSH key not loaded or not authorised

Load the key with ssh-add and check authorizedkeys on the host

permission denied while trying to connect to the Docker daemon socket

The remote user cannot access the Docker socket

Add the user to the docker group on the remote host, then reconnect

docker: command not found (over SSH)

Docker CLI not installed on the remote host, or not on the non-interactive PATH

Install Docker Engine on the remote host and check ssh user@host which docker

Every command takes several seconds

A new SSH connection is opened for each command

Enable ControlMaster multiplexing in ~/.ssh/config

x509: certificate signed by unknown authority

Wrong CA certificate in the TLS context

Recreate the context with the correct ca= file

dockerd fails to start after adding hosts to daemon.json

Conflict with the -H flag in docker.service

Override ExecStart with systemctl edit docker.service

Bind-mounted files are missing in a remote container

Bind mounts use paths on the remote host

Copy the files to the host, use volumes, or bake them into the image


To see exactly which endpoint a context uses, run docker context inspect <name> and check the Endpoints section.

Frequently Asked Questions

What is the difference between DOCKERHOST and a Docker context?

DOCKERHOST is a single environment variable holding one address, and it applies to whatever shell it is set in. A context is a saved, named profile that can include TLS certificates and a description, and you can keep many of them and switch between them. If both are present, DOCKERHOST takes precedence.

Do I need to open a port on the server to use an SSH context?

No. An SSH context only needs SSH access (normally port 22). The Docker API stays on the server's local Unix socket.

Does docker compose work with remote contexts?

Yes. docker compose uses the active context or the --context flag. Remember that bind mount paths and published ports refer to the remote host.

Can I use Docker contexts with Docker Desktop?

Yes. Docker Desktop creates its own context, usually named desktop-linux, and you can add SSH or TLS contexts for remote hosts alongside it. Switch back with docker context use desktop-linux.

Are Docker contexts the same as Kubernetes contexts?

No. Docker contexts select a Docker engine for the docker CLI. Kubernetes contexts, managed with kubectl config use-context, select a Kubernetes cluster, user and namespace for kubectl. The idea is similar, but they are separate configurations.

Is it safe to manage production with Docker contexts?

It can be, if you use SSH or mutual TLS, a dedicated deployment user, clear context names, and per-command --context for production. For larger teams, deploying through a CI/CD pipeline that uses a context is safer than individuals running commands against production by hand.

Conclusion and Next Steps

Docker contexts turn your local CLI into a control panel for every Docker engine you work with. Create an SSH context for each remote host, switch with docker context use or target single commands with --context, keep DOCKERHOST out of the way, and show the active context in your prompt. With multiplexed SSH connections, managing a remote engine feels just as fast as working locally, without ever exposing the Docker API to the network.

Continue the Docker course with these tutorials on BitCodeMatrix:

  • How to Install Docker on Ubuntu, Windows (WSL2) and macOS — prepare the remote hosts you will connect to

  • Rootless Docker: Setup and Limitations — safer engines to manage through a rootless context

  • Docker Daemon Explained — what the CLI is actually talking to

  • Docker Commands Cheat Sheet (start of Module 3) — every command you can now run against any context