Run Claude Code from anywhere via SSH. Deploy on Railway and code from your phone in under 5 minutes.
- Claude Code CLI ready to use over SSH from any device
- Persistent sessions — tmux is pre-configured for mobile. Switch WiFi, close the app, come back later. Your session is still there.
- Hardened SSH — key-only auth, modern ciphers, sshaudit.com compliant. No passwords, no weak algorithms.
- Brute-force protection — fail2ban bans attackers automatically (1 hour ban after 3 failures, 1 week for repeat offenders)
- Mobile-optimized tmux — pre-tuned for Termius, Blink, and JuiceSSH. No broken characters, no escape sequence leaks.
- Node.js 24 + pnpm — ready for full-stack development. Override versions with
NODE_VERSIONandPNPM_VERSIONenv vars. - GitHub integration — clone private repos with a fine-grained PAT scoped to specific repos only
- Persistent storage — your projects, auth, and config survive redeployments via Railway volumes
- Multi-device access — add SSH keys from your laptop, phone, and tablet. Connect from anywhere.
Click the button above, or manually create a project on railway.com and deploy from this GitHub repo.
In Railway dashboard, go to your service Variables and add:
| Variable | Value |
|---|---|
SSH_PUBLIC_KEY |
Your public key (see below for how to get it) |
From desktop — copy your existing key:
cat ~/.ssh/id_ed25519.pubFrom mobile (Termius) — generate a new key:
- Open Termius > Keychain > Keys > + > Generate
- Select Ed25519, give it a name, save
- Tap the key > Export public key > copy the text
Using multiple devices? Paste all public keys into SSH_PUBLIC_KEY, one per line:
ssh-ed25519 AAAA...laptop-key... you@laptop
ssh-ed25519 AAAA...phone-key... mobile
- Go to your service Settings > Networking
- Under TCP Proxy, click Enable on port
22 - Railway assigns you a domain and port, e.g.
shuttle.proxy.rlwy.net:15140
Save these — you need them to connect.
- In Railway dashboard, right-click on your project canvas > "Add Volume"
- Attach it to your service
- Set the mount path to
/data - Redeploy if prompted
This persists your projects, SSH host keys, tmux sessions, and Claude Code auth across redeployments. Without it, all data is lost on restart.
ssh claude@shuttle.proxy.rlwy.net -p 15140 -i ~/.ssh/id_ed25519Replace the domain and port with your values from Step 3.
Choose one method:
Option A: Claude Account (Pro/Max subscription) — recommended
claude loginThis prints a URL. Open it in any browser, sign in with your Claude account, done. No API key needed.
Option B: API Key
Add ANTHROPIC_API_KEY in Railway Variables (triggers redeploy), or set it manually:
export ANTHROPIC_API_KEY=sk-ant-your-key-here
echo 'export ANTHROPIC_API_KEY=sk-ant-your-key-here' >> ~/.bashrcAuth persists across restarts via the /data volume.
tmux new -s dev
claudeDetach anytime with Ctrl+B then D. Reattach later with tmux attach -t dev.
Use a fine-grained Personal Access Token to grant access to specific repos only.
- Go to GitHub > Settings > Developer settings > Fine-grained tokens
- Click Generate new token
- Set Resource owner to your account or org
- Set Repository access to Only select repositories and pick the repos you need
- Under Permissions > Repository permissions, grant:
- Contents: Read and write (clone, push, branches)
- Pull requests: Read and write (create/merge PRs)
- Issues: Read and write (create/comment on issues)
- Generate and copy the token
- In Railway dashboard, add it as a variable:
| Variable | Value |
|---|---|
GITHUB_TOKEN |
github_pat_xxxx... |
After redeploy, just clone:
cd /workspace
git clone https://github.com/you/your-private-repo
claudeAuth persists across restarts via the /data volume.
Need repos from multiple orgs? Create a separate Railway service per org, each with its own scoped GITHUB_TOKEN. This keeps access isolated — one container never touches another org's repos.
Alternative:
gh auth loginis also available but grants access to all repos across all your orgs. Use it only if you're comfortable with that scope.
- Generate a key (if you don't have one): Keychain > Keys > + > Generate > Ed25519
- Copy the public key: tap the key > Export public key > copy text
- Add it to Railway: paste into
SSH_PUBLIC_KEYvariable (alongside your desktop key if needed) - Create a host: Hosts > +
- Hostname: your Railway TCP domain (e.g.
shuttle.proxy.rlwy.net) - Port: your assigned port (e.g.
15140) - Username:
claude - Key: select your generated key
- Hostname: your Railway TCP domain (e.g.
- Enable keep-alive: host settings > advanced > Keep connection alive
- Connect and run
tmux new -s dev && claude
- Import your Ed25519 private key in Blink's key management
- Configure the host:
ssh claude@shuttle.proxy.rlwy.net -p 15140
- Identities > add new > import your Ed25519 private key
- Connections > add new > set host, port, username
claude - Select your identity, enable keep-alive
Your SSH connection will drop — WiFi switches, cellular handoffs, locking your phone. That's fine. tmux keeps your session alive on the server.
# Create a session
tmux new -s dev
# Detach (session keeps running): Ctrl+B, then D
# Reattach after reconnecting
tmux attach -t dev
# List all sessions
tmux lsOne-liner — auto-attach on connect (save this in your SSH client):
ssh claude@<host> -p <port> -i ~/.ssh/id_ed25519 -t "tmux attach -t dev || tmux new -s dev"The included tmux config is tuned for mobile: 256-color support, mouse/touch scrolling, low escape-time for mobile latency, and 50k line scrollback.
| Variable | Required | Default | Description |
|---|---|---|---|
SSH_PUBLIC_KEY |
Yes | — | Ed25519 public key(s) for SSH auth. Multiple keys supported, one per line. |
ANTHROPIC_API_KEY |
No | — | Claude API key. Alternative to claude login. |
GITHUB_TOKEN |
No | — | Fine-grained PAT for cloning private repos. Scoped to specific repos only. |
NODE_VERSION |
No | 24 |
Node.js version (e.g. 22, 20.18.0). Uses n to switch at boot. |
PNPM_VERSION |
No | latest |
pnpm version (e.g. 9.15.0, 10). Uses corepack to switch at boot. |
TZ |
No | UTC |
Timezone (e.g. America/New_York, Europe/London) |
- Key-only authentication — passwords completely disabled
- Modern ciphers — ChaCha20-Poly1305, AES-256-GCM (AEAD only)
- Modern key exchange — Curve25519, DH Group 16/18
- ETM-only MACs — HMAC-SHA2-512-ETM, HMAC-SHA2-256-ETM
- fail2ban — 3 failed attempts = 1 hour ban, repeat offenders = 1 week ban
- Non-root user — all operations run as unprivileged
claudeuser - DH moduli filtered — only >= 3071-bit entries
- sshaudit.com compliant — passes audit with modern-only algorithms
Host key warning after redeploy: Remove the old key:
ssh-keygen -R "[your-host]:port"Connection refused: Ensure TCP Proxy is enabled in Railway on port 22.
Permission denied (publickey): Verify SSH_PUBLIC_KEY contains your public key. On mobile, re-export from Termius and update the Railway variable.
Claude Code not authenticated: Run claude login inside the container, or set ANTHROPIC_API_KEY.
tmux session lost: Sessions persist while the container runs. If the container restarted, start a new session. Your files on /data are safe.
| Platform | Cost | Notes |
|---|---|---|
| Railway Hobby | ~$5-10/mo | Pay-as-you-go, TCP proxy included |
| Hetzner CX22 | ~$4/mo | 2 vCPU, 4GB RAM, full iptables |
| Oracle Cloud Free | $0 | ARM A1, always-free tier |
See docs/vps-deployment.md for VPS and local Docker deployment instructions.
MIT