Sshwifty is a web-based SSH (and Telnet)
client: it puts a terminal in your browser so you can reach a remote server
without an SSH client on the local machine. Handy from a locked-down laptop, a
tablet, or a borrowed computer.
A web SSH client on the public internet is a doorway into every host it can
reach. If your Docker host is on Tailscale, that’s your entire tailnet.
If Sshwifty’s own auth is off, anyone who finds the URL is one login away from
your servers. Treat this as one of the most sensitive things you’ll ever host —
gate it hard, or prefer the alternative below.
docker-compose.yml
# /srv/ssh.example.com/docker-compose.ymlname: ssh-example-comservices: ssh: image: niruix/sshwifty:latest container_name: ssh.example.com restart: always mem_limit: 256M environment: # REQUIRED. Empty = the web UI is PUBLIC (no login). Set a long random key. - SSHWIFTY_SHAREDKEY=${SSHWIFTY_SHAREDKEY} labels: - com.centurylinklabs.watchtower.enable=true - traefik.enable=true - traefik.http.routers.ssh.rule=Host(`ssh.example.com`) - traefik.http.routers.ssh.entrypoints=https - traefik.http.routers.ssh.tls=true - traefik.http.routers.ssh.tls.certresolver=lets-encrypt # Sshwifty listens on :8182 inside the container — Traefik needs this - traefik.http.services.ssh.loadbalancer.server.port=8182
Two additions to a bare setup:
loadbalancer.server.port=8182 — Sshwifty’s default port; without it Traefik has nothing to route to (502).
entrypoints=https — pin to the TLS entrypoint.
Lock it down (do all of these)
Set SSHWIFTY_SHAREDKEY. Generate one and keep it out of the compose file:
openssl rand -base64 32 # put the result in a .env / env_file as SSHWIFTY_SHAREDKEY
With it empty, Sshwifty bypasses its login page entirely — public web SSH.
Put it behind SSO too — defense in depth. Add the
Traefik forward-auth (Google SSO) middleware
to the router so a Google login + email allowlist sits in front of Sshwifty’s
own shared key. One leaked secret shouldn’t be game over.
Prefer not to expose it publicly at all. Bind it to your tailnet/VPN and
reach it privately. A public ssh.example.com is a permanent, high-value target.
On the servers behind it: key-only SSH, no root login, and fail2ban — as if the internet can knock on the door, because now it can.
A more secure alternative: Tailscale SSH
Since your host is already on Tailscale, you likely don’t need a public web SSH
client at all. Tailscale SSH
solves the same problem without the exposed attack surface:
tailscale up --ssh
Identity-based, not password-based — access is granted by your tailnet
ACLs (who can SSH to which nodes as which user), authenticated via your SSO.
No public endpoint — nothing to find, scan, or brute-force from the internet.
Re-auth on demand — ACLs can force a fresh browser check before a session.
Connect with a normal ssh user@machine-name over the tailnet — no client-side keys to manage, Tailscale handles the crypto.
Rule of thumb: use Tailscale SSH for yourself and your team (secure by
default); reach for Sshwifty only when you genuinely need a browser terminal
from an untrusted device — and even then, keep it behind SSO + a shared key.