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.
चला मित्रांनो! प्रत्येक server वर SSH करून docker commands चालवणं कंटाळवाणं आहे. आज आपल्या laptop वरूनच दुसऱ्या server चा Docker कसा चालवायचा ते शिकू: DOCKER_HOST, docker context, SSH keys, आणि production ला चुकून धक्का लागू नये म्हणून काळजी.
चलो दोस्तों! हर server पर SSH करके docker commands चलाना थकाने वाला है. आज अपने laptop से ही दूसरे server का Docker चलाना सीखेंगे: DOCKER_HOST, docker context, SSH keys, और production को गलती से न छूने की सावधानी.
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.
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.
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.
Production साठी 'docker context use prod' पेक्षा 'docker --context prod' वापरा. Prompt मध्ये context दाखवा, म्हणजे prod वर चुकून delete होणार नाही.
Production के लिए 'docker context use prod' की जगह 'docker --context prod' इस्तेमाल कीजिए. Prompt में context दिखाइए, ताकि prod पर गलती से कुछ delete न हो.
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.
मित्रांनो, एक laptop आणि अनेक servers: SSH key, ssh config आणि docker context. Prod साठी नेहमी --context आणि prompt मध्ये नाव. समजलं का? चला, पुन्हा एकदा करून पाहू. नसेल तर Picture 2 परत बघा.
दोस्तों, एक laptop और कई servers: SSH key, ssh config और docker context. Prod के लिए हमेशा --context और prompt में नाम. समझ आया? चलिए, एक बार फिर करके देखते हैं। नहीं तो Picture 2 फिर देखिए.
Lab: try at home
- Create an ed25519 key and copy it to a test EC2.
- Add the server to ~/.ssh/config and run
ssh dev docker ps. - Try DOCKER_HOST=ssh://dev for one command.
- Create dev and prod contexts, list containers on both with one loop.
- 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.
Revision card
Interview card