SSH gives you command-line access to a container. It is optional: hoody-terminal opens a shell in the browser with no SSH configuration at all.
API endpoints summary
Section titled “API endpoints summary”SSH configuration is part of container creation and updates:
- POST /api/v1/projects/{id}/containers - Create container with
ssh_public_key - PATCH /api/v1/containers/{id} - Update
ssh_public_keyon existing container - GET /api/v1/containers/{id} - View current SSH configuration
Alternative access:
- Hoody Terminal - Web-based shell, no SSH needed
SSH vs hoody-terminal
Section titled “SSH vs hoody-terminal”SSH is not required to reach a container. The two paths differ in setup, session lifetime, and which local tools can attach.
| Feature | SSH | hoody-terminal |
|---|---|---|
| Setup | Generate keys, configure client | Visit the URL |
| Access | Desktop and mobile SSH clients | Any web browser |
| Session | Closes when you disconnect | Persists when you leave |
| Background processes | Need screen or tmux | Run directly, session maintained |
| Performance | SSH protocol | HTTP/2 (faster) |
| File transfer | SFTP, rsync, scp | hoody-files HTTP API |
| Use cases | VS Code Remote, SFTP, local tools | Quick access, mobile, no setup |
You can also run the Hoody CLI itself over SSH, with nothing installed and nothing running on your machine. See Run the Hoody CLI over SSH below.
SSH endpoints
Section titled “SSH endpoints”SSH uses the same address as every other service on your container. Only the protocol differs.
Container hostname
Section titled “Container hostname”ssh -i ~/.ssh/key root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.comThis is the address to use. It is the same {projectId}-{containerId}-{service}.{serverName}.containers.hoody.com shape you already use for terminal, files, and the rest, so once you can build one service URL you can build this one. The only difference from the others is that ssh carries no instance number: a container has exactly one SSH endpoint, so there is no -1 to append.
Shared endpoint
Section titled “Shared endpoint”ssh -i ~/.ssh/key root@ssh.{serverName}.containers.hoody.comOne hostname covers every container on a server. The proxy reads your public key from the SSH handshake and routes on that alone, so this name carries no project or container identifier and an observer at the endpoint cannot tell which container a connection is for.
It works, and it is useful when you want the connection string itself to reveal nothing. But it is not the address to build on: its shape is not guaranteed to stay as it is, while the container hostname follows the same rule as every other service and will not move. Prefer the container hostname in scripts, config files, and anything you hand to someone else.
Routing is by public key either way. Whichever name you use, you land in the container that owns the key.
Routing by public key
Section titled “Routing by public key”Your SSH Client ↓ssh -i ~/.ssh/key root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.com ↓Hoody SSH proxy (the hostname is not what selects the container) ├─ Reads public key from SSH handshake ├─ Matches key to container (one-to-one mapping) └─ Routes connection to that specific container ↓Container with matching public keyEach container needs its own public key.
Generate a key pair
Section titled “Generate a key pair”# Generate key (ed25519 recommended)ssh-keygen -t ed25519 -f ~/.ssh/hoody-container-1 -C "container-1" -N ""
# View public keycat ~/.ssh/hoody-container-1.pub# Copy the entire line starting with "ssh-ed25519..."PowerShell:
ssh-keygen -t ed25519 -f %USERPROFILE%\.ssh\hoody-container-1 -C "container-1" -N """"type %USERPROFILE%\.ssh\hoody-container-1.pubPuTTY: Use PuTTYgen → Generate → EdDSA/Ed25519 → Export OpenSSH key
Termux:
pkg install opensshssh-keygen -t ed25519 -f ~/.ssh/hoody-container-mobilecat ~/.ssh/hoody-container-mobile.pubJuiceSSH: Identities → + → Generate → Ed25519 → Export Public Key
Termius: Keychain → + → New Key → Ed25519 → Copy Public Key
Blink Shell: Settings → Keys → + → Ed25519 → Copy public key text
Adding SSH key to container
Section titled “Adding SSH key to container”Set ssh_public_key when you create the container:
# Create container with SSH keyhoody containers create --project $PROJECT_ID \ --server-id $SERVER_ID \ --name "dev-container" \ --dev-kit \ --ssh-public-key "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMx... container-1"const container = await client.api.containers.create(PROJECT_ID, { name: 'dev-container', server_id: SERVER_ID, hoody_kit: true, dev_kit: true, ssh_public_key: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMx... container-1',});curl -X POST "https://api.hoody.com/api/v1/projects/$PROJECT_ID/containers" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "dev-container", "server_id": "your_server_id", "hoody_kit": true, "dev_kit": true, "ssh_public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMx... container-1" }'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Creates the container with the SSH public key already set, using the same body as the HTTP tab. Swap in your own name, server ID, and key before opening it.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/projects/PROJECT_ID/containers&method=POST&bearer_token=TOKEN&json={"name":"dev-container","server_id":"your_server_id","hoody_kit":true,"dev_kit":true,"ssh_public_key":"ssh-ed25519%20AAAAC3NzaC1lZDI1NTE5AAAAIMx...%20container-1"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
To change it later, PATCH the container’s ssh_public_key. The key rotates immediately; the container does not need a stop and start.
Connect to a container
Section titled “Connect to a container”Through the container’s own hostname:
ssh -i ~/.ssh/hoody-container-1 root@{projectId}-{containerId}-ssh.us-west-1.containers.hoody.comOr through the shared endpoint, where the SSH proxy picks the container from your public key:
ssh -i ~/.ssh/hoody-container-1 root@ssh.us-west-1.containers.hoody.comClients by platform
Section titled “Clients by platform”# Container hostnamessh -i ~/.ssh/hoody-container-1 root@{projectId}-{containerId}-ssh.us-west-1.containers.hoody.com
# Or the shared endpointssh -i ~/.ssh/hoody-container-1 root@ssh.us-west-1.containers.hoody.com~/.ssh/config:
Host dev-container HostName {projectId}-{containerId}-ssh.us-west-1.containers.hoody.com User root IdentityFile ~/.ssh/hoody-container-1Then: ssh dev-container
Same as Linux. Use Terminal.app or iTerm2.
VS Code: Install Remote-SSH extension → Connect to root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.com
PowerShell:
ssh -i %USERPROFILE%\.ssh\hoody-container-1 root@{projectId}-{containerId}-ssh.us-west-1.containers.hoody.comPuTTY: Host: {projectId}-{containerId}-ssh.us-west-1.containers.hoody.com → Auth → Private key: .ppk file
JuiceSSH: Connections → + → Address: {projectId}-{containerId}-ssh.{serverName}.containers.hoody.com → Identity: (select your key)
Termius: Hosts → + → Hostname: {projectId}-{containerId}-ssh.{serverName}.containers.hoody.com → Key: (select your key)
SFTP support
Section titled “SFTP support”Every SSH connection also serves SFTP, with the same key and the same routing, so any SFTP client works.
FileZilla setup
Section titled “FileZilla setup”- File → Site Manager → New Site
- Protocol: SFTP - SSH File Transfer Protocol
- Host:
{projectId}-{containerId}-ssh.us-west-1.containers.hoody.com(replace with your ids and server) - Port: 22
- Logon Type: Key file
- User: root
- Key file: Browse to
~/.ssh/hoody-container-1(private key) - Connect
If FileZilla can’t find your key:
- Settings → Connection → SFTP → Add key file
- Press Cmd+Shift+G in file browser
- Type:
~/.ssh - Select your key → Open
Other SFTP clients:
- Cyberduck: New Connection → SFTP → Server:
ssh.$serverName.containers.hoody.com→ Private Key: (browse) - WinSCP: New Site → SFTP → Host:
ssh.$serverName.containers.hoody.com→ Advanced → Private key file - Command-line:
sftp -i ~/.ssh/hoody-container-1 root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.com
Run the Hoody CLI over SSH
Section titled “Run the Hoody CLI over SSH”You don’t need to install anything to drive Hoody from a terminal. SSH into the gateway with a token as the username and you land in the Hoody CLI, running remotely in a locked-down sandbox and already signed in:
ssh -tt hdy_xxxxxxxx@hoody.com- Your token (
hdy_…) is the SSH username. There is no password and no key. -ttrequests the interactive terminal the gateway needs; you can then run commands right away (hoody servers list,hoody containers create …).gateway.hoody.comworks too. Both names reach the same gateway.- The gateway is a free, best-effort service, so responsiveness can vary with distance and load.
This opens the Hoody CLI, not a shell inside one of your containers. The two are different destinations: hoody.com reaches the CLI gateway, while a container shell comes from ssh.$serverName.containers.hoody.com or the container’s own hostname, as described above.
Best practices
Section titled “Best practices”Generate one key per container
Section titled “Generate one key per container”# Correct: one key per containerssh-keygen -t ed25519 -f ~/.ssh/hoody-container-1ssh-keygen -t ed25519 -f ~/.ssh/hoody-container-2
# Wrong: reusing the same key breaks routingPrefer ed25519 keys
Section titled “Prefer ed25519 keys”# Modern, secure, fastssh-keygen -t ed25519 -f ~/.ssh/hoody-container-1
# Legacy (use only if ed25519 not supported)ssh-keygen -t rsa -b 4096 -f ~/.ssh/hoody-container-1Use ~/.ssh/config for several containers
Section titled “Use ~/.ssh/config for several containers”Host dev HostName {projectId}-{containerId}-ssh.us-west-1.containers.hoody.com User root IdentityFile ~/.ssh/hoody-container-1
Host prod HostName {projectId}-{containerId}-ssh.us-west-1.containers.hoody.com User root IdentityFile ~/.ssh/hoody-container-2Now connect with ssh dev or ssh prod. Same hostname, different keys: the SSH proxy routes by public key.
Protect private keys
Section titled “Protect private keys”# Ensure correct permissionschmod 600 ~/.ssh/hoody-container-*
# Never share private keys# Never commit to version control# Store securely (password manager, encrypted disk)Use hoody-terminal for quick access
Section titled “Use hoody-terminal for quick access”Don’t configure SSH just for occasional commands; save the setup for when you need SFTP, VS Code Remote, or rsync. Open the terminal URL instead:
https://{project}-{container}-terminal-1.{server}.containers.hoody.comUseful questions
Section titled “Useful questions”Can I SSH to a container without configuring keys?
Section titled “Can I SSH to a container without configuring keys?”No. The proxy authenticates by public key only. You don’t need SSH at all, though: the hoody-terminal web interface needs no key and no client.
What if two containers share a key?
Section titled “What if two containers share a key?”That state cannot be reached: the API rejects a key that already belongs to another container with a conflict error, so every key maps to exactly one container. Generate a fresh key per container.
Do I need SSH if I only use hoody-terminal?
Section titled “Do I need SSH if I only use hoody-terminal?”No. If you only reach containers through a browser, SSH keys are not required.
Can I SSH from one container to another?
Section titled “Can I SSH from one container to another?”Yes. From container A, ssh -i /path/to/key root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.com reaches container B, selected by the key you present.
Can the terminal SSH out to a non-Hoody server?
Section titled “Can the terminal SSH out to a non-Hoody server?”Yes. hoody-terminal acts as an HTTP-to-SSH bridge. Add ssh_host and ssh_user parameters to any terminal URL or execute request, and the container opens the SSH connection for you. You need no SSH client on your device. See the callout at the top of this page, or Terminals: SSH to Remote Servers for full details.
Does SSH work with containers in “block” network mode?
Section titled “Does SSH work with containers in “block” network mode?”Yes. SSH into a container is inbound traffic, and block mode stops outbound traffic only. The SSH bridge is the exception: the terminal connecting out to a remote server needs outbound access.
Can an in-container VPN lock me out of SSH?
Section titled “Can an in-container VPN lock me out of SSH?”No. The host brokers SSH through the container runtime, so there is no routing lookup and no in-container firewall between you and your shell. It keeps working with a tunnel up, kill switch and all, which makes SSH the recovery path when a VPN has taken your published URLs dark. See VPN Inside a Container.
What user do I connect as?
Section titled “What user do I connect as?”root by default. Containers run as the root user.
Can I disable SSH and only use hoody-terminal?
Section titled “Can I disable SSH and only use hoody-terminal?”Yes. SSH configuration is optional: create the container without ssh_public_key and only the hoody-terminal URL is available. Sending ssh_public_key: null to clear an already-assigned key is not applied by the API today, so retire a key by rotating it to one you control instead.
Does FileZilla support both SFTP and hoody-files?
Section titled “Does FileZilla support both SFTP and hoody-files?”No. FileZilla speaks SFTP over the SSH protocol only. For hoody-files, which is HTTP-based, use a browser.
Troubleshooting
Section titled “Troubleshooting”Connection refused
Section titled “Connection refused”Problem: ssh returns “Connection refused”
Solutions:
- Verify container is running:
GET /api/v1/containers/{id}→ check"status": "running" - Test SSH proxy connectivity:
telnet ssh.$serverName.containers.hoody.com 22 - Check key permissions:
chmod 600 ~/.ssh/hoody-container-1
SSH key not recognized
Section titled “SSH key not recognized”Problem: “Permission denied (publickey)”
Solutions:
- Verify correct key:
cat ~/.ssh/hoody-container-1.pubmatches container’sssh_public_key - Check key format: Must start with
ssh-ed25519,ssh-rsa, orecdsa-sha2-nistp* - Use verbose logging:
ssh -v -i ~/.ssh/hoody-container-1 root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.com
Two containers with the same key
Section titled “Two containers with the same key”Problem: SSH sometimes connects to wrong container
Cause: Duplicate public keys across containers
Solution: Generate unique keys for each container, update via API
FileZilla can’t find the key
Section titled “FileZilla can’t find the key”Problem: FileZilla says “No supported authentication methods available”
Solutions:
- Import key first: FileZilla → Edit → Settings → Connection → SFTP → Add key file
- Use Interactive logon: Logon Type: Interactive (FileZilla uses imported key automatically)
- On macOS: Use Cmd+Shift+G → Type
~/.sshwhen browsing for key
SSH key rotation
Section titled “SSH key rotation”Step 1: Generate a new key locally:
ssh-keygen -t ed25519 -f ~/.ssh/hoody-container-new -N ""Step 2: Update the key (works while the container is running):
# Update SSH public keyhoody containers update $CONTAINER_ID \ --ssh-public-key "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... (new key)"// Update SSH public keyawait client.api.containers.update(CONTAINER_ID, { ssh_public_key: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... (new key)' });# Update SSH public keycurl -X PATCH "https://api.hoody.com/api/v1/containers/$CONTAINER_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"ssh_public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... (new key)"}'One request, one link
cURL runs inside your container and can wrap any HTTP request into a single GET URL. The call stops being something you need a client for and becomes something you can paste into a browser, send in a chat, bookmark, schedule with cron, or drop into a no-code tool.
Nothing is installed on the machine that opens it. The link does carry whatever credentials the call needs, so treat it as you would treat those credentials.
Slashes, colons and braces pass through as they are. The one character you must
encode is an & inside a value, which happens when the wrapped URL
carries its own query string. Left raw it ends the value early, and the rest is
read as cURL's own parameters, so you get a 200 on a request you did
not make.
How the wrapping works Chaining calls into one link Turning a link into a shortcut
Swaps in the new key on the running container. Routing switches to it immediately, with no stop or restart needed.
https://PROJECT_ID-CONTAINER_ID-curl-1.SERVER.containers.hoody.com/api/v1/curl/request?url=https://api.hoody.com/api/v1/containers/CONTAINER_ID&method=PATCH&bearer_token=TOKEN&json={"ssh_public_key":"ssh-ed25519%20AAAAC3NzaC1lZDI1NTE5...%20(new%20key)"}&response=transparent The link carries a credential and executes with it, so it is as sensitive as the credential itself — and it passes through the cURL service's request log on the way, not just the target's. Share it only where you would share the secret, and prefer a delegated token with minimal permissions and an expiry: see API tokens.
Step 3: Test with the new key:
ssh -i ~/.ssh/hoody-container-new root@{projectId}-{containerId}-ssh.{serverName}.containers.hoody.comWhat’s next
Section titled “What’s next”Complete your networking setup:
- Firewall → - Control traffic at packet level
- Network Configuration → - Route through VPNs/proxies, change exit IP
- VPN Inside a Container → - Run your own tunnel, and keep your way back in
- IPv4 Management → - Dedicated IP addresses (coming soon)
Alternative access methods:
- Hoody Terminal → - Web-based shell, no SSH needed
- Hoody Files → - HTTP file access, no SFTP needed