Accessing internal services from a developer laptop¶
Internal services on the dev VM (Postgres, Redis, OTel collector, Kafka if reintroduced) bind only to the private interface and are firewalled from the public internet. Developer access goes through an SSH tunnel over port 22, reusing the same key-based authentication that protects the VM.
Picking the right pattern¶
Different tools and workflows want different tunnel patterns. The right default depends on whether your tool can manage its own tunnel.
| Pattern | When to use | Lifecycle |
|---|---|---|
| Tool-managed (pgAdmin / RedisInsight built-in tunnel) | GUI tools that support SSH tunnels natively | Opens on Connect, closes on Disconnect |
Ad-hoc background tunnel (ssh -fN) |
Terminal tools like psql, redis-cli |
Foreground until you kill it |
LocalForward in ~/.ssh/config |
You SSH into the VM regularly and want tunnels as a free side effect | Lifetime of the SSH session |
autossh |
"Always on" tunnels for multiple tools | Persistent, auto-reconnects |
The recommended default for GUI tools is tool-managed. It doesn't require an open SSH session in another terminal, has the same lifetime as your DB connection, and survives laptop sleep / network changes because the tool just re-opens the tunnel on its next connect.
1. pgAdmin — built-in SSH tunnel (recommended)¶
Add Server → fill three tabs:
General
- Name: dev
Connection
- Host: 127.0.0.1
- Port: 5432
- Maintenance DB / Username / Password: per your dev credentials
SSH Tunnel
- Use SSH tunneling: on
- Tunnel host: dev.grintest.pl
- Tunnel port: 22
- Username: filip
- Authentication: Identity file → ~/.ssh/grintest_hetzner_dev
- Identity file passphrase: leave empty if your key is unlocked via
ssh-agent / macOS Keychain.
pgAdmin opens the tunnel when you click Connect and closes it when you disconnect from the server. No background process to manage.
2. RedisInsight — built-in SSH tunnel (recommended)¶
When adding the dev connection:
- Database alias:
dev - Host:
127.0.0.1 - Port:
6379 - Tick Use SSH Tunnel:
- SSH host:
dev.grintest.pl - SSH port:
22 - SSH username:
filip - Auth: Private Key → upload
~/.ssh/grintest_hetzner_dev
Same lifecycle as pgAdmin: opens on Connect, closes on Disconnect.
3. Terminal tools — ad-hoc background tunnel¶
For psql, redis-cli, pgcli, etc. — anything without built-in SSH
support — start a background tunnel for the duration of your work:
# Postgres
ssh -fNL 5432:localhost:5432 dev.grintest.pl
# Redis
ssh -fNL 6379:localhost:6379 dev.grintest.pl
-f backgrounds the SSH process, -N skips command execution. Now any
local tool sees the dev services on localhost:
Kill the tunnels when done:
4. LocalForward in ~/.ssh/config (optional)¶
If you SSH into the dev VM regularly anyway, you can fold the tunnels into your SSH config so they open automatically whenever you start an SSH session:
Host dev.grintest.pl
User filip
IdentityFile ~/.ssh/grintest_hetzner_dev
IdentitiesOnly yes
LocalForward 5432 127.0.0.1:5432
LocalForward 6379 127.0.0.1:6379
The tunnels live for the lifetime of the SSH session — close the terminal and they're gone. Worth it only if "I always have a shell open to dev" is your reality.
5. autossh for persistent tunnels (advanced)¶
If you want tunnels that survive laptop sleep, Wi-Fi changes, and
intermittent VM restarts without thinking about it, install autossh:
brew install autossh
autossh -M 0 -f -N \
-o "ServerAliveInterval 30" \
-o "ServerAliveCountMax 3" \
-L 5432:localhost:5432 \
-L 6379:localhost:6379 \
dev.grintest.pl
autossh monitors the SSH connection and auto-reconnects on failure.
Wrap it in a launchd plist (macOS) or systemd --user unit (Linux) to
start on login.
This is overkill for casual use but a worthwhile setup if multiple GUI tools are hitting localhost simultaneously and you want one tunnel shared between them.
What this gives you¶
- Zero new firewall holes — port 22 is the only public ingress, and you already trust it for SSH.
- Same key-based authentication as your regular shell access; passphrase
unlocked once per session by
ssh-agent/ Keychain. - Tools see
localhostconnections, so connection strings stay portable between laptop and CI without secret rewriting.
What this is not¶
- Not a production access pattern. Production access goes through separate audit-logged channels (TBD when prod lands).
- Not a substitute for least-privilege DB users. Tunnel access still authenticates against whatever Postgres role you log in as — keep those scoped.