EthicsComputerCourses
Join training +91 90532 10052

Remote Docker: Contexts & SSH

Docker & DevOps · Lesson 33 of 39 Advanced 7 min read Updated

Once your app runs on a VPS, you will often want to check its containers, read logs or run a quick docker compose ps. Logging in with SSH every time works, but there is a neater way: the docker command on your laptop can talk to the Docker daemon on the server directly, over the same SSH connection you already trust. Docker calls each daemon you can talk to a context.

This lesson shows how to create and switch contexts, run Compose against a remote server, avoid the path trap that catches almost everyone, and why you must never "solve" remote access by opening Docker's TCP port.

What a context is

The docker program you type is only a client. It sends every command to a daemon (dockerd) through an endpoint: usually a local Unix socket on Linux, or the Docker Desktop virtual machine on Windows and macOS. A context is a saved name for an endpoint, plus a few settings.

Terminal
docker context ls
Output
NAME        DESCRIPTION                               DOCKER ENDPOINT               ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock

The * marks the active context. On Docker Desktop you will also see a context such as desktop-linux, which Docker Desktop creates and selects for you.

Create an SSH context

You need three things first: SSH login to the server with a key (see SSH & Key-based Login), Docker installed on the server, and the SSH user in the server's docker group. Then:

Terminal
docker context create jaipur-prod \
  --description "Shop VPS in Jaipur" \
  --docker "host=ssh://deploy@shop.example.in"
docker --context jaipur-prod ps --format '{{.Names}}: {{.Status}}'
Output
jaipur-prod
Successfully created context "jaipur-prod"
shop-caddy-1: Up 3 days
shop-app-1: Up 3 days (healthy)
shop-db-1: Up 3 days (healthy)

The --context flag runs one command against the server; the containers listed are the ones from Deploy to a VPS with a Reverse Proxy. Behind the scenes Docker runs your normal ssh program, so everything in your ~/.ssh/config (key files, ports, jump hosts) applies. No new port is opened on the server.

Switching and checking the active context

Terminal
docker context use jaipur-prod
docker context show
docker logs --tail 5 shop-app-1
docker context use default
Output
jaipur-prod
Current context is now "jaipur-prod"
jaipur-prod
...
default
Current context is now "default"

While jaipur-prod is active, every command goes to the server, including docker system prune and docker compose down. That is the main danger of contexts: you think you are cleaning up your laptop and you delete production containers. Three habits help:

  • Prefer docker --context NAME ... for one-off commands instead of docker context use.
  • Show the active context in your shell prompt, or run docker context show before anything destructive.
  • Set the context for one terminal only with the DOCKER_CONTEXT environment variable, for example export DOCKER_CONTEXT=jaipur-prod. It overrides docker context use and disappears when you close the terminal.

The older DOCKER_HOST variable also still works, for example DOCKER_HOST=ssh://deploy@shop.example.in docker ps, and it overrides the active context too. If commands go somewhere unexpected, check whether DOCKER_HOST is set.

Compose with a remote context

Compose follows the context like every other command, so you can manage the server's stack from your laptop:

Terminal
docker --context jaipur-prod compose -p shop ps
docker --context jaipur-prod compose -p shop logs --since 15m app

Starting or updating a stack remotely needs more care, because of how files are found:

Part of the Compose fileRead from
The Compose file itself, .env, env_fileYour laptop
build: context (the source code)Your laptop; sent to the remote daemon, which builds there
Bind-mount sources such as ./Caddyfile:/etc/caddy/CaddyfileThe server's file system
Secret files (secrets: file:)The server's file system, because they are mounted like bind mounts

So running up -d remotely from a laptop folder whose ./Caddyfile does not exist at the same path on the server fails, or worse, Docker creates an empty folder called Caddyfile on the server and mounts that. For production stacks with bind mounts and secret files, keep the files on the server and run Compose there (by SSH or by a deploy script, as in the VPS lesson). Contexts are perfect for reading, checking and small operations; image-only stacks without host files can be deployed remotely too.

Contexts in CI

A deploy job can use a context instead of running a remote script. After the SSH key and known_hosts setup shown in CI/CD: Build & Push with GitHub Actions:

Terminal
docker context create prod --docker "host=ssh://deploy@shop.example.in"
docker --context prod pull ghcr.io/priya-sharma/shop-api:2.1.0
docker --context prod compose -p shop up -d --wait app

The second command makes the server pull the image from the registry; nothing is copied through the CI runner. For stacks that depend on files kept on the server, calling the server's deploy script over SSH remains simpler.

Common mistakes

  • Running destructive commands in the wrong context. Use --context for single commands and check docker context show.
  • Expecting bind mounts to use files from your laptop. They use paths on the remote host.
  • "permission denied while trying to connect to the Docker daemon socket" over SSH. The SSH user is not in the server's docker group.
  • Exposing port 2375. It gives root access to anyone; use SSH.
  • A forgotten DOCKER_HOST variable silently overriding your context.

Key points

  • A context is a named Docker endpoint; docker context ls lists them and * marks the active one.
  • docker context create NAME --docker "host=ssh://user@server" uses your normal SSH setup and opens no new ports.
  • --context runs one command remotely; docker context use and DOCKER_CONTEXT switch for longer, and DOCKER_HOST overrides both.
  • Remote Compose reads the Compose file and .env locally, but bind-mount and secret file paths refer to the server.
  • Never expose the daemon on TCP 2375; use SSH contexts.

Exercise

Isha manages two servers: staging.example.in and api.example.in, both with a deploy user. Create one context for each, list the running containers on staging only, show the last 20 log lines of results-api-1 on production without switching your default context, and set staging as the context for your current terminal only.

Show solution
Terminal
docker context create staging --docker "host=ssh://deploy@staging.example.in"
docker context create production --docker "host=ssh://deploy@api.example.in"
docker --context staging ps
docker --context production logs --tail 20 results-api-1
export DOCKER_CONTEXT=staging

--context affects only the command it is given, so the default context stays unchanged. DOCKER_CONTEXT applies to the current terminal session and overrides whatever docker context use selected.

Quick Quiz

1. What does a Docker context store?

2. Your active context is a remote server and you run docker system prune -a. What happens?

3. Using a remote context, a Compose file mounts ./nginx.conf:/etc/nginx/nginx.conf. Where must nginx.conf exist?

4. Why should you never expose the Docker daemon on TCP port 2375?

Learn Docker & DevOps with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call