blog
Managing several Hetzner servers from one desktop
Published
One server fits in your head. The second one is where the workflow quietly breaks — not because anything is hard, but because two machines that look the same are now one wrong window apart.
The moment it breaks
It is rarely dramatic. You run docker compose down in a terminal you thought was staging. You copy an .env to the right path on the wrong machine. Both servers have the same prompt, the same directories and the same container names, because you built them from the same notes.
The fixes below are cheap, and most of them are about making the machines easy to tell apart.
Make every server say its name
Give each server a hostname that says what it is, so the shell prompt says it too. A name the console suggested at creation, such as ubuntu-4gb-fsn1-1, tells you the size and the location, not the role.
sudo hostnamectl set-hostname prod-fsn1
Some people also colour the production prompt red. It sounds silly until the first time it stops you.
An SSH config alias for each server
~/.ssh/config turns addresses and options into short names. Every tool that uses your system SSH — ssh, scp, rsync, Docker contexts — picks the aliases up.
~/.ssh/config
Host prod
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host staging
HostName 203.0.113.20
User deploy
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yesIdentitiesOnly yes makes SSH offer only the key you named. Without it, an agent holding several keys offers each in turn, and the server can disconnect with “Too many authentication failures” before the right one comes up.
One key per machine you sit at
The tempting setup is one key per server. The better one is one key per computer you work from — your laptop, your desktop — added to every server that computer should reach.
The difference shows on a bad day. When a laptop is lost, you remove one public key from each server and everything else keeps working. With a key per server copied onto every laptop, a lost laptop means rotating every key you have.
Hetzner Cloud can store public keys in the project and add them when a server is created, so a new machine has the right keys from its first boot.
Docker contexts for the same commands everywhere
A Docker context per server lets you run the same command against any of them from your own shell, without logging in. The SSH alias does the connection work.
docker context create prod --docker host=ssh://prod docker context create staging --docker host=ssh://staging docker --context staging ps docker --context prod logs -f --tail 100 shop-web-1
Prefer --context on each command over docker context use. A context you switched to an hour ago is the wrong-window problem again, moved into a setting you cannot see.
Keeping the same stack in step
Two servers running the same stack drift apart one manual fix at a time. Keep the compose file in Git and apply it to every server the same way instead of editing files on each machine.
With contexts, a loop is enough for stacks built from images and named volumes. Staging goes first, and a failure stops the loop before production. Bind-mount paths refer to the remote machine and the .env is read on your side, so check those before relying on it.
for host in staging prod; do
docker --context "$host" compose -f compose.yml -p shop pull &&
docker --context "$host" compose -f compose.yml -p shop up -d --remove-orphans ||
break
doneAt around five servers, or the first time two of them need different settings, a configuration-management tool starts to earn its learning curve.
When a rebuilt server looks like an attack
Rebuild a Hetzner server from the console and it comes back with new SSH host keys. Your next connection stops with “REMOTE HOST IDENTIFICATION HAS CHANGED”. That is exactly what a man-in-the-middle attack looks like — and exactly what a rebuild looks like.
If you rebuilt it yourself, remove the old entry and check the new fingerprint before you accept it, using the server’s web console in the Hetzner panel, which does not go over SSH:
# on your computer ssh-keygen -R 203.0.113.10 # in the Hetzner web console, on the server ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Compare that output with the fingerprint SSH shows on the next connection. If you did not rebuild the server, remove nothing — find out why the key changed.
What one screen is actually worth
Measured honestly: with aliases, contexts and distinct hostnames, a terminal handles several servers well. What a single view adds is mostly not speed. It is seeing every server’s state at once — which one is low on disk, which container keeps restarting — and making the server you are acting on impossible to mistake.
If that is not a problem you have, the setup above is enough, and free.
Several servers in MihraOps
MihraOps keeps a server list with each machine’s containers and live CPU, memory and disk usage under its own name, and checks every server’s host fingerprint on each connection. A changed fingerprint is refused, not offered as a warning to click past.
The Free plan includes 1 server. Pro removes the limit — which is about where the problem this post describes begins.