Ravindra BagaleCourses & study guides मराठी

Chapter 47: Docker Architecture, Installation on Linux, Windows and Mac, and Docker Desktop Alternatives

47.14 Docker contexts and remote Docker over SSH

Chala mitrano! SSH-ing into every server to run docker commands is tiring. Today we learn to drive another server's Docker from our own laptop: DOCKER_HOST, docker context, SSH keys, and care so we never hit production by mistake.

1. Start from three servers

A small team at Razorpay has three EC2 servers: dev, staging and prod. Every day someone logs into each one with SSH just to type docker ps. It is slow, and once someone typed a delete command in the wrong window.

The Docker CLI and the Docker daemon do not have to be on the same machine. The CLI can send its commands to a remote daemon over SSH. A context is a saved name for that remote address, so you switch servers with one command.

Nothing new is installed on the servers. They only need Docker and an SSH login that is allowed to use Docker.

Laptop: docker CLIyou type hereSSH connectionencrypted, uses your keyRemote dockerdon dev, staging or prodOutput comes backas if it were local
Picture 1: one CLI, many daemons

2. Prepare SSH

Step 1: Make an SSH key

Why: Remote Docker uses normal SSH, and SSH should use keys, not passwords.

What: An SSH key pair has a private key that stays on your laptop and a public key that you put on the server.

Example 1: Create the key

Type this:

ssh-keygen -t ed25519 -f ~/.ssh/docker_ops -C ravi-laptop

You will see:

Generating public/private ed25519 key pair.
Your identification has been saved in /home/ravi/.ssh/docker_ops
Your public key has been saved in /home/ravi/.ssh/docker_ops.pub

What it means: Two files. Never share the one without .pub.

Example 2: Copy it to the server

Type this:

ssh-copy-id -i ~/.ssh/docker_ops.pub ubuntu@10.0.1.25

You will see:

Number of key(s) added: 1

What it means: The public key is now in the server's authorized_keys file.

Example 3: Check the key file mode

Type this:

ls -l ~/.ssh/docker_ops

You will see:

-rw------- 1 ravi ravi 411 Oct 25 11:02 /home/ravi/.ssh/docker_ops

What it means: Mode 0600: only you can read it. SSH refuses keys that others can read.

Step 2: Use an SSH config file

Why: Typing long addresses and key paths every time causes mistakes.

What: ~/.ssh/config gives short names to servers. Docker uses this file too.

Example 4: Add entries

Type this:

printf 'Host dev\n  HostName 10.0.1.25\n  User ubuntu\n  IdentityFile ~/.ssh/docker_ops\nHost prod\n  HostName 10.0.3.40\n  User ubuntu\n  IdentityFile ~/.ssh/docker_ops\n' >> ~/.ssh/config
chmod 0600 ~/.ssh/config

You will see:

(no output)

What it means: Now dev and prod are short names.

Example 5: Test the login

Type this:

ssh dev 'hostname'

You will see:

ip-10-0-1-25

What it means: SSH works without a password.

Example 6: Test Docker over SSH

Type this:

ssh dev 'docker version --format {{.Server.Version}}'

You will see:

27.3.1

What it means: The server user can run Docker. If you see permission denied, add that user to the docker group on the server.

3. DOCKER_HOST: the quick way

Step 3: Point the CLI with DOCKER_HOST

Why: For a one-off check, you do not need to save anything.

What: DOCKER_HOST is an environment variable that tells the CLI which daemon to use. ssh://dev means use SSH to the host named dev.

Example 7: For one command

Type this:

DOCKER_HOST=ssh://dev docker ps --format '{{.Names}}'

You will see:

web
api

What it means: These are the containers on dev.

Example 8: For the whole terminal

Type this:

export DOCKER_HOST=ssh://dev
docker info --format '{{.Name}}'

You will see:

ip-10-0-1-25

What it means: Every command in this terminal now goes to dev.

Example 9: Back to local

Type this:

unset DOCKER_HOST
docker info --format '{{.Name}}'

You will see:

ravi-laptop

What it means: Remove the variable to come back. Forgetting this is a common mistake.

4. Contexts: the safe, saved way

Step 4: Create contexts

Why: A saved name is safer than a variable you forget to unset.

What: docker context create NAME --docker host=ssh://... saves the address. Add a description so the team knows what it is.

Example 10: Create dev and prod

Type this:

docker context create dev --description 'Razorpay dev' --docker host=ssh://dev
docker context create prod --description 'PRODUCTION' --docker host=ssh://prod

You will see:

dev
Successfully created context "dev"
prod
Successfully created context "prod"

What it means: Two saved targets.

Example 11: List them

Type this:

docker context ls

You will see:

NAME        DESCRIPTION                 DOCKER ENDPOINT
default *   Current DOCKER_HOST based   unix:///var/run/docker.sock
dev         Razorpay dev                ssh://dev
prod        PRODUCTION                  ssh://prod

What it means: The star still marks default.

Example 12: Inspect one

Type this:

docker context inspect prod --format '{{.Endpoints.docker.Host}}'

You will see:

ssh://prod

What it means: Shows exactly where commands will go.

Step 5: Switch with docker context use

Why: You want to work on dev for an hour without typing the name every time.

What: docker context use changes the default target for every terminal, until you switch again.

Example 13: Switch to dev

Type this:

docker context use dev
docker ps --format '{{.Names}} {{.Status}}'

You will see:

dev
web Up 3 hours
api Up 3 hours

What it means: All commands go to dev now.

Example 14: A new terminal also uses dev

Type this:

# open a new terminal
docker context show

You will see:

dev

What it means: The choice is saved in ~/.docker/config.json, so it applies everywhere.

Example 15: Switch back

Type this:

docker context use default

You will see:

default

What it means: Make this a habit when you finish.

Step 6: Use --context for a single command

Why: For production, switching the default is risky. A single, named command is safer.

What: docker --context NAME sends just one command to that target. Your default does not change.

Example 16: Look at prod once

Type this:

docker --context prod ps --format '{{.Names}}'

You will see:

shop-web
shop-api
shop-worker

What it means: Your default context is still the laptop.

Example 17: Compare two servers

Type this:

for c in dev prod; do printf '%s: ' $c; docker --context $c images -q | wc -l; done

You will see:

dev: 18
prod: 6

What it means: One loop, two servers, no SSH windows.

Example 18: Read logs remotely

Type this:

docker --context prod logs --tail 3 shop-api

You will see:

GET /health 200
GET /orders 200
POST /pay 201

What it means: Logs stream over SSH to your laptop.

docker context showalways check firstPrefer --context prodfor one commandShow context in promptyou always see itRead-only habitsps, logs, inspect
Picture 2: stay safe with prod

Step 7: Show the context in your prompt

Why: At Zomato, an engineer once cleaned up prod while thinking he was on dev.

What: Adding the current context to your shell prompt keeps it in front of your eyes.

Example 19: Add to bash

Type this:

printf 'PS1="[$(docker context show)] \\w$ "\n' >> ~/.bashrc
PS1='[$(docker context show)] \w$ '

You will see:

[default] ~$

What it means: The prompt now shows the context.

Example 20: Switch and watch

Type this:

docker context use prod

You will see:

[prod] ~$

What it means: PROD is now in front of you before every command.

Example 21: A safety alias

Type this:

printf 'alias dprod="docker --context prod"\n' >> ~/.bashrc
source ~/.bashrc
dprod ps -q | wc -l

You will see:

3

What it means: dprod makes it clear every time you touch prod.

Step 8: Build and run on the remote server

Why: You can build on a strong server instead of your slow laptop.

What: docker build with a remote context sends your folder over SSH and builds on the server. docker compose also follows the context.

Example 22: Build remotely

Type this:

docker --context dev build -t api:dev .

You will see:

[+] Building 22.1s (10/10) FINISHED
 => [internal] load build context  2.1MB

What it means: The 2.1 MB build context travelled to dev, and the image lives on dev.

Example 23: Compose on dev

Type this:

docker --context dev compose up -d

You will see:

[+] Running 3/3
 ✔ Container api-db-1   Started
 ✔ Container api-api-1  Started

What it means: The stack runs on dev. Bind mounts point to folders on dev, not on your laptop.

Example 24: Check where the image is

Type this:

docker images api:dev -q; docker --context dev images api:dev -q

You will see:

7f3a9c2e1b0d

What it means: Empty locally, present on dev.

Step 9: Check every server's health from one place

Why: On Monday morning the team lead at Swiggy wants to know: are all servers fine, and is any disk full?

What: Read-only commands such as docker info, docker system df and docker ps are safe to run on any context. Run them in a loop to get one report.

Example 25: Versions everywhere

Type this:

for c in default dev prod; do printf '%s ' $c; docker --context $c version --format '{{.Server.Version}}'; done

You will see:

default 27.3.1
dev 27.3.1
prod 26.1.4

What it means: prod is one version behind. Plan an upgrade window.

Example 26: Disk use everywhere

Type this:

for c in dev prod; do printf '%s: ' $c; docker --context $c system df --format '{{.Type}}={{.Size}}' | tr '\n' ' '; echo; done

You will see:

dev: Images=9.8GB Containers=310MB Local Volumes=4.1GB Build Cache=6.2GB
prod: Images=1.9GB Containers=40MB Local Volumes=12.4GB Build Cache=0B

What it means: dev has 6.2 GB of build cache to clean. prod volumes hold real data, so leave them.

Example 27: Unhealthy containers everywhere

Type this:

for c in dev prod; do docker --context $c ps --filter health=unhealthy --format "$c {{.Names}}"; done

You will see:

dev api

What it means: Only one problem, on dev. You found it without logging in anywhere.

Step 10: Copy files and run one-off jobs remotely

Why: Sometimes you must run a database migration or pull a log file from a remote container.

What: docker exec, docker cp and docker run --rm all work through a context. Files from docker cp land on your laptop.

Example 28: Run a migration on dev

Type this:

docker --context dev exec api npm run migrate

You will see:

> migrate
Batch 4 run: 2 migrations

What it means: The command ran inside the container on dev.

Example 29: Copy a log to your laptop

Type this:

docker --context prod cp shop-api:/app/logs/error.log ./prod-error.log
wc -l prod-error.log

You will see:

12 prod-error.log

What it means: The file came from prod to your laptop over SSH.

Example 30: A one-off container

Type this:

docker --context dev run --rm alpine df -h / | tail -1

You will see:

overlay  50G  21G  29G  43% /

What it means: It ran on dev and was removed after.

Step 11: Fix common errors

Why: Remote Docker fails in a few known ways.

What: Most errors come from SSH, from user permissions or from a wrong host name.

Example 31: Wrong key

Type this:

docker --context dev ps

You will see:

error during connect: ... Permission denied (publickey)

What it means: Run ssh dev alone first. Fix the key before blaming Docker.

Example 32: User not in docker group

Type this:

docker --context dev ps

You will see:

permission denied while trying to connect to the Docker daemon socket

What it means: On the server, run sudo usermod -aG docker ubuntu and log in again.

Example 33: Slow commands

Type this:

time docker --context prod ps -q

You will see:

real 0m2.9s

What it means: Each command opens SSH. Turn on SSH reuse with ControlMaster auto and ControlPersist 5m in ~/.ssh/config to make it fast.

Step 12: Export, import and remove contexts

Why: A new team member needs the same contexts.

What: docker context export saves a context to a file. It has no private key inside, so each person still uses their own key.

Example 34: Export

Type this:

docker context export dev dev.dockercontext

You will see:

Written file "dev.dockercontext"

What it means: A small file with the name and address.

Example 35: Import on another laptop

Type this:

docker context import dev dev.dockercontext

You will see:

dev
Successfully imported context "dev"

What it means: Their own SSH config must also define dev.

Example 36: Remove

Type this:

docker context use default
docker context rm dev prod

You will see:

default
dev
prod

What it means: Only the saved names go. The servers keep running.

Ravindra Bagale's Tip

For production, use 'docker --context prod' instead of 'docker context use prod'. Show the context in your prompt, so nothing on prod gets deleted by mistake.

Check yourself

Q1. What does DOCKER_HOST=ssh://dev do?

It sends Docker commands to the daemon on dev over SSH.

Q2. Do you need to install anything on the server?

No. Only Docker and an SSH user allowed to use Docker.

Q3. What is the difference between context use and --context?

use changes the default for all terminals; --context affects one command.

Q4. Where does a remote build run?

On the remote server; the build context is sent over SSH.

Q5. Does docker context export include your private key?

No. Each person uses their own SSH key.

Friends, one laptop and many servers: SSH key, ssh config and docker context. For prod always use --context and show the name in the prompt. Did you understand? Let us try it once more. If not, look at Picture 2 again.

Lab: try at home

  1. Create an ed25519 key and copy it to a test EC2.
  2. Add the server to ~/.ssh/config and run ssh dev docker ps.
  3. Try DOCKER_HOST=ssh://dev for one command.
  4. Create dev and prod contexts, list containers on both with one loop.
  5. Put the context in your prompt, then build an image remotely.

Recap

  • The CLI can talk to a remote daemon over SSH.
  • DOCKER_HOST is quick; contexts are saved and safer.
  • Use --context for one-off production commands.
  • Remote builds and Compose run on the server.
  • Most errors are SSH keys or docker group permissions.

Ravi Sir's Cards for this lesson

मराठी cards