Client Setup
Configure the .pod namespace and multi-server routing
The podspawn client binary is optional. Without it, you SSH directly to your server's hostname and get a container. The client adds two things: the .pod namespace for friendly hostnames, and multi-server routing so work.pod and gpu.pod can point to different machines.
The .pod namespace
Instead of remembering server hostnames:
ssh alice@devbox.company.com
ssh alice@gpu-server.company.comYou type:
ssh alice@work.pod
ssh alice@gpu.podThe .pod suffix is not a real TLD -- it never hits DNS. Your SSH client's ProxyCommand intercepts it before resolution and routes to the real server.
Setup
Install the client binary
podspawn setupThis appends a block to ~/.ssh/config:
Host *.pod
ProxyCommand podspawn connect %r %h %p
SetEnv PODSPAWN_PROJECT=%n
SendEnv PODSPAWN_PROJECT
UserKnownHostsFile /dev/null
StrictHostKeyChecking noIf the block already exists, setup skips it:
$ podspawn setup
~/.ssh/config already has Host *.pod block, skippingsetup creates ~/.ssh/ and ~/.ssh/config if they don't exist. It appends to the file -- it never overwrites existing config.
After running setup, your SSH directory looks like this:
Configure server routing
Create ~/.podspawn/config.yaml to tell the connect command which server backs each .pod hostname:
servers:
default: devbox.company.com
mappings:
work.pod: devbox.company.com
gpu.pod: gpu-server.company.com
personal.pod: my-homelab.ddns.netConnect
ssh alice@work.podClient configuration
Mappings can also use an object format to include the server's session mode. This is purely informational on the client side (the server controls the actual behavior), but it shows up in podspawn servers output so you know which servers persist your files:
servers:
default: devbox.company.com
mappings:
work.pod:
host: devbox.company.com
mode: persistent
gpu.pod:
host: gpu-server.company.com
mode: ephemeral
personal.pod: my-homelab.ddns.netBoth formats can be mixed in the same config. The string format (personal.pod: my-homelab.ddns.net) is shorthand for {host: my-homelab.ddns.net}.
The mode field is informational: ephemeral means the container is destroyed after disconnect, persistent means it stays alive and files survive. The server controls actual behavior.
How routing works
When you run ssh alice@work.pod:
- SSH matches
Host *.podand runspodspawn connect alice work.pod 22 connectreads~/.podspawn/config.yaml- It checks
servers.mappingsforwork.pod-- findsdevbox.company.com - Opens a TCP connection to
devbox.company.com:22and relays the SSH traffic
If a .pod hostname isn't in mappings, the default server is used. If neither matches, connect exits with an error:
no server configured for "staging.pod" (add it to servers.mappings or set servers.default)Single-server shortcut
If all your .pod hostnames point to one server, you only need default:
servers:
default: devbox.company.comNow ssh alice@anything.pod routes to devbox.company.com. The hostname before .pod is passed to the server as the project name -- so work.pod and frontend.pod still create separate containers.
The client config file must exist at ~/.podspawn/config.yaml for .pod routing to work. Without it, podspawn connect fails with "client config not found."
The project name
The part before .pod is the project name. When you SSH to backend.pod, the server receives "backend" as the project identifier. The server uses this to:
- Look up project-to-repo mappings in its config
- Create separate containers per project (alice's
work.podandbackend.podare different containers) - Resolve Podfile environments from the mapped repository
The SetEnv PODSPAWN_PROJECT=%n line in the SSH config block passes the full hostname (e.g., backend.pod) as an environment variable. The server's AcceptEnv PODSPAWN_PROJECT directive (added by server-setup) allows it through.
Verifying the setup
# Check SSH config
grep -A5 "Host \*.pod" ~/.ssh/config
# Check client config
cat ~/.podspawn/config.yaml
# List servers with connectivity and mode
podspawn servers (default) devbox.company.com ok (12ms)
gpu.pod gpu-server.company.com ephemeral ok (45ms)
work.pod devbox.company.com persistent ok (11ms)# Test the connection
ssh alice@work.podNext steps
How is this guide?