Unofficial community project. Not affiliated with Coder.
Coder workspaces give you a fresh, consistent dev
environment on demand. Using one from your laptop is easy: install the
coder CLI, log in, and coder ssh you're in.
But the CLI has to be installed and authenticated on every machine you want to work from — and those machines aren't always yours to install things on. An iPad with a keyboard case. A borrowed laptop. A locked-down work machine. A client's server you're debugging from.
All of those devices already speak SSH. This gateway sits in front of your Coder deployment and speaks plain SSH to them: your SSH key proves which device you are, and the workspace name is the username:
ssh general@gateway.example.com
That's the whole client-side story. No Coder CLI, no extensions, no sync apps — any SSH client from the last decade works, including mobile apps like Blink and Termius.
You set up a host block once (client setup),
then connect with the special login user. The gateway proves your key,
asks for a Coder token, and links the two:
$ ssh login@gateway.example.com
New device enrollment. Provide your Coder token to link this key.
Coder SSH Gateway
New device enrollment.
Open https://coder.example.com/cli-auth, sign in, and paste the token below.
The token links this key to your Coder account. After verification
this connection will close; reconnect with your workspace connection.
Coder token:
Enrolled. Coder user alicia — key linked, token saved.
Reconnect using your workspace connection (<workspace>@<gateway>).
Enrollment complete. This connection will now close;
reconnect with your workspace connection (<workspace>@<gateway>).
Connection to gateway.example.com closed.The token is never echoed and never touches your command line or shell history. Now every workspace is just a username:
$ ssh general@gateway.example.com
coder@alicia-general ~/code % whoami
coder
coder@alicia-general ~/code % uname -sm
Linux x86_64
coder@alicia-general ~/code % exit
Connection to gateway.example.com closed.Managing your enrolled keys works the same way: a second special
username, default login-admin, opens a line-based UI on the session
channel. Authenticate with one of your enrolled keys, list the keys on
your account, remove the ones you no longer trust behind a two-step
confirmation, or delete the account (every key plus the stored Coder
token) behind a typed DELETE. Workspaces and tunnels are impossible
in this mode; the audit log captures every removal and every
deletion.
$ ssh login-admin@gateway.example.com
Coder SSH Gateway -- key management for alicia
1 SHA256:3nIPhhXCP54ETGXAAakA9Myxp13NNtONA/ZFgiThTvQ ssh-ed25519 "ada-laptop" added 2026-08-12
2 SHA256:9aBcdEF... ssh-ed25519 "ada-phone" added 2026-08-13
Enter a key number to remove it, d to delete your account, r to refresh, q to quit:
q
Bye.It's your workspace — your shell, your dotfiles, your files. When a stored token eventually expires, your next connection simply prompts for a fresh one and continues on through (renewal).
Three paths, simplest first. All of them take a few minutes.
Build the image (or pull a published one), then run one container per
lifecycle step: init once to lay down keys and config, doctor to
verify, serve to run:
docker build -f deploy/Dockerfile -t coder-ssh-gateway .
docker run --rm -v csgw-state:/var/lib/coder-ssh-gateway \
coder-ssh-gateway init coder.example.com
docker run --rm -v csgw-state:/var/lib/coder-ssh-gateway \
coder-ssh-gateway doctor
docker run -d -p 2222:2222 -v csgw-state:/var/lib/coder-ssh-gateway \
coder-ssh-gateway serveFull walkthrough including config edits: Operator Guide → Container.
The chart self-provisions — it generates the encryption key (injected via the environment, never written to disk next to your data), and the host key is generated into the state volume on first boot. No cloning required; install straight from the OCI registry:
helm install csgw \
oci://ghcr.io/hamstudy/charts/coder-ssh-gateway \
--version 0.2.1 \
-n csgw --create-namespace --set coder.domain=coder.example.com(Or from a checkout: helm install csgw deploy/helm/coder-ssh-gateway -n csgw --create-namespace --set coder.domain=coder.example.com.)
Chart values, upgrades, and maintenance: Operator Guide → Kubernetes.
Build a single static binary, initialize a state directory, and hand it to the systemd unit:
make && sudo install -m 0755 bin/coder-ssh-gateway /usr/local/bin/
sudo useradd --system --home /var/lib/coder-ssh-gateway \
--shell /usr/sbin/nologin coder-ssh-gateway
sudo install -d -o coder-ssh-gateway -g coder-ssh-gateway -m 0700 \
/var/lib/coder-ssh-gateway
sudo -u coder-ssh-gateway coder-ssh-gateway \
--state-dir /var/lib/coder-ssh-gateway init coder.example.com
sudo install -m 0644 deploy/systemd/coder-ssh-gateway.service \
/etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now coder-ssh-gatewayHost hardening, backups, and every flag: Operator Guide → Native install.
One paragraph per moving part:
- Your SSH key identifies the device. On first connect, the gateway verifies you possess the key, then asks for a one-time Coder token and binds the two together. After that, the key alone logs you in — no token prompt, no passwords.
- Tokens are stored encrypted. The gateway keeps one Coder session
token per user, encrypted at rest with a key that lives in the state
directory's
secrets/folder. That makes the gateway a credential broker: treat the host accordingly. - Each session becomes a Coder session. For every accepted SSH
channel the gateway spawns an isolated
coder ssh --stdiochild and bridges your terminal to it. Shell, exec, PTY, signals, and window changes pass through, plus SFTP/scp, agent forwarding (ssh -A), and port forwarding (-L/-R/-D) — forwarding targets resolve inside the workspace, so-L 8080:localhost:8080reaches your workspace's dev server. ProxyJump syntax also works for anything the bridge does not carry. - The username routes the connection.
generalis your workspace,general.mainnames a specific agent. To reach a teammate's workspace, prefix the owner:alicia/generaloralicia/general/main(owner/workspace/agent). The gateway never decides access — Coder's own permissions do. Details in client setup.
- One instance per state directory. A second writer fails on
startup by design. In Kubernetes:
replicas: 1andRecreate— the chart sets both. - Back up state and secrets separately. State without the encryption key recovers nothing; together they recover everything, so they must never share a bucket. Backup procedure.
- Pin the host key fingerprint before the first client connects.
initprints it; publish it through a channel you trust. - Every key the gateway accepts is documented, with defaults and
security annotations, in
config.example.yaml.
make test # go test ./... -count=1
make test-race # go test ./... -race -count=1
make ci # test, race, vet, staticcheck, govulncheckLICENSE. Unofficial community project, not affiliated
with Coder. Coder is a trademark of its respective owners; references
to coder ssh and the Coder CLI describe compatibility, not
endorsement.