SSH into a Box
Open an SSH session to a Box with cubicle ssh, or write an OpenSSH alias with cubicle ssh-config for ssh, file copies and VS Code or Cursor Remote-SSH, using ed25519 keys, Box grants and pinned host keys.
SSH gives you an interactive shell in a Box as your own guest account, over Cubicle's private
network. The cubicle CLI connects the network, writes an OpenSSH alias for the Box and pins its
host key, so plain ssh, scp, rsync and editor remote extensions work with it. SSH needs an
interactive login: service accounts and API keys cannot use SSH, so automation runs commands with
exec instead.
Quick start
Run these three commands once the CLI is installed and signed in:
-
Install the tunnel, the network helper that SSH needs. It asks for confirmation and runs
sudo.cubicle tunnel install -
Register your SSH public key. Without an argument the CLI reads
~/.ssh/id_ed25519.pub.cubicle keys add # added SHA256:… -
Connect. The Box must be running and you must hold a grant on it (the Box's creator gets one automatically).
cubicle ssh acme/job-1
The rest of this page explains each step and the options.
Before you begin
- The
cubicleCLI on macOS or Linux, signed in withcubicle login. See Install the CLI and Sign in and API keys. - The tunnel, which has its own requirements on Linux and macOS. See platform requirements.
- An ed25519 key pair. If
~/.ssh/id_ed25519does not exist, create it withssh-keygen -t ed25519. - Active membership in the Box's organization and an explicit grant on the Box. The
ownerandadminroles alone are not enough; see Grant access to a Box. - A running Box. Refer to it by
ORG/NAME(acme/job-1) or by its ID. The CLI does not accept a bare name.
How it works
cubicle ssh and the alias connect straight to the Box's private address on port 22, through the
WireGuard tunnel to Gateway. Nothing is exposed on the public internet. See
Networking for the full path and what else the tunnel carries.
SSH works only when two sides are ready:
- The Box: running at its current generation, on a healthy Node, with its host key published.
ssh.readyon the Box reports this part. - You: an active member of the Box's organization with a grant on it and at least one active
ed25519 key, and the Box's Node has applied your guest account and keys at the Box's current
generation. This part shows up as the
not readyreason thatcubicle ssh-configprints (see Troubleshooting).
Add an SSH key
Cubicle accepts only ssh-ed25519 public keys. RSA, ECDSA and security-key types such as
sk-ssh-ed25519@openssh.com are refused with
invalid_key: only ssh-ed25519 public keys are accepted. Keys belong to your account, not to one
Box: adding or revoking a key updates your guest account on every Box you are granted.
-
Add the key. Pass a path to use a key other than
~/.ssh/id_ed25519.pub.cubicle keys add ~/.ssh/id_ed25519.pub # added SHA256:…In the Dashboard, open Account, paste the public key into My SSH keys and click Add key.
-
Check that it is active.
cubicle keys list # 5d2c8f41-9a7e-4b3c-8d1f-2e6a0b9c7d5e SHA256:… dev@laptop
Remove a key with cubicle keys rm KEY_ID. Removing a key stops new SSH logins with it. It does
not cut your network access to the Box.
Grant access to a Box
The user who creates a Box gets a grant on it automatically; anyone else, owners and admins
included, needs one from an owner or admin of the Box's organization.
Grant access to a Box in the members guide shows how to
find the user ID, add the grant and check that it has reached the Box.
cubicle ssh and cubicle ssh-config
Both commands write the same OpenSSH configuration. They differ in what else they do.
| Command | Connects the tunnel | Writes the alias | Runs ssh |
|---|---|---|---|
cubicle ssh acme/job-1 | Yes | Yes | Yes |
cubicle ssh-config acme/job-1 | No | Yes | No |
cubicle ssh exits with ssh's exit code. Pass arguments for ssh after --, for example a
remote command:
cubicle ssh acme/job-1 -- uname -acubicle ssh-config prints the alias to use. If you are not ready yet, it adds the reason:
cubicle ssh-config acme/job-1
# configured: ssh cubicle-acme-job-1An alias written by ssh-config needs the tunnel to be connected. Run cubicle connect first, or
run cubicle ssh once; the connection stays up after ssh exits. See
Reach a Box over the private network.
Both commands take the same options, --alias, --identity and --accept-new-host-key, before
any --; see cubicle ssh-config. Pass --identity a full
path, because the path is written into the alias as given.
What the CLI writes
The CLI writes to ~/.ssh (CB_CLI_SSH_DIR changes the directory):
| File | Contents |
|---|---|
~/.ssh/cubicle_config | One Host block per alias, between # BEGIN cubicle <alias> and # END cubicle <alias>. |
~/.ssh/cubicle_known_hosts | One pinned host key per Box, stored under the name jevbox-<box ID>. |
~/.ssh/config | An Include line for cubicle_config, added once at the top so it applies to every Host. |
Running the command again replaces the alias's block. The block for acme/job-1 looks like this:
# BEGIN cubicle cubicle-acme-job-1
Host cubicle-acme-job-1
HostName job-1.acme.cubicle.internal
User jv_6b9e0d2c41af
IdentityFile /home/you/.ssh/id_ed25519
IdentitiesOnly yes
HostKeyAlias jevbox-<box ID>
UserKnownHostsFile /home/you/.ssh/cubicle_known_hosts
StrictHostKeyChecking yes
ForwardAgent no
ForwardX11 no
ServerAliveInterval 5
ServerAliveCountMax 2
# END cubicle cubicle-acme-job-1HostName is the Box's private DNS name, <box>.<org>.cubicle.internal. The CLI has no command
to remove an alias; delete its block from ~/.ssh/cubicle_config by hand.
jv_ in guest account names and jevbox- in pinned host key names are legacy prefixes.
Use the alias with other tools
Every tool that reads your OpenSSH configuration can use the alias while the tunnel is connected:
cubicle connect
ssh cubicle-acme-job-1To copy files with scp, sftp or rsync, see
Copy files over SSH.
VS Code and Cursor. Run cubicle connect first. Then, in the Remote-SSH extension, run
Remote-SSH: Connect to Host… and enter the alias, cubicle-acme-job-1. The editor uses the
same ~/.ssh/config, so the pinned host key and identity apply.
Forwarding a Box-local port. OpenSSH local forwarding to the Box's own localhost works,
which is what editor remote extensions rely on:
ssh -N -L 8080:localhost:8080 cubicle-acme-job-1Forwarding to other hosts, remote forwarding (-R), agent forwarding and X11 forwarding are
disabled in the Box. To reach a service that listens on the Box's network interface, use the
private network directly.
The guest account and sudo
- You log in as your own guest account,
jv_followed by the first 12 hex characters of your user ID (for examplejv_6b9e0d2c41af), which also runs your exec commands.cubicle grants listshows it, andcubicle ssh-configwrites it as the alias'sUser. - The account has passwordless
sudo, so your SSH sessions have root inside the Box. The desktop console signs in as a different account.
Guest accounts covers its home directory and shell, and what happens to it while you have no active SSH key.
Host keys
Cubicle generates each Box's SSH host key when it provisions the Box, and the CLI pins the key the
API publishes in ~/.ssh/cubicle_known_hosts with StrictHostKeyChecking yes, so ssh never
asks you to trust a key and never silently accepts a new one. Pins are per Box ID, so a new Box
that reuses a deleted Box's name gets its own pin without a warning, while a changed key for the
same Box stops the connection with one of the two host key errors under
Troubleshooting; see
SSH and host-key pinning.
Keepalives and disconnects
The alias sets ServerAliveInterval 5 and ServerAliveCountMax 2. When the Box stops answering,
ssh gives up within about 15 seconds with Timeout, server … not responding. instead of
hanging.
When access ends
Your session ends, or new sessions are refused, when any of these happens:
| Event | Effect |
|---|---|
| The Box hibernates, by request or by itself | The VM stops, so open connections end. See automatic hibernation. |
| The Box is stopped or deleted | The VM stops, so open connections end. |
| The Box is started again while running | SSH traffic cannot reach the Box until its Node has applied the new generation, and open connections may not survive the gap. |
| Your grant or membership is revoked | Network access ends within the 30 s lease, including open sessions, and your guest account loses its keys. |
| Your device is revoked, or you log out | The tunnel closes, so open sessions drop. |
| Your CLI login expires (after 30 days) | The tunnel can no longer refresh and closes. Run cubicle login, then cubicle connect. |
| You remove an SSH key | New logins with that key fail. Network access is unchanged. |
Verify
Run a command and check the account it runs as:
cubicle ssh acme/job-1 -- whoami
# jv_6b9e0d2c41afcubicle boxes list --org acme shows ssh=ready for a Box that is SSH-ready.
Troubleshooting
Troubleshooting covers errors shared with other pages. These are the SSH errors and their fixes.
box has no trusted host key or guest account yet (<reason>); retry when it is running
The Box is not running at its current generation, or you have no guest account on it yet. The
reason in parentheses says which. Start the Box with cubicle boxes start acme/job-1 --wait, or
ask for a grant if the reason is grant_inactive.
configured: ssh cubicle-acme-job-1 (not ready: <reason>)
The alias was written, but SSH will fail until the reason clears. Reasons about the Box itself,
which cubicle boxes list also shows as ssh=<reason>, are explained with what to do in
When a Box is ready. These five concern you rather
than the Box, so only cubicle ssh-config and the Access tab of the Box's details in the
Dashboard show them:
| Reason | Dashboard wording | Fix |
|---|---|---|
user_disabled | Your account is disabled | Ask a Cubicle operator. |
membership_inactive | You are not an active member of its organization | Ask an owner or admin to add you. |
grant_inactive | You have no active grant on this Box | Ask an owner or admin for a grant. |
no_active_key | You have no active SSH key (add one under Account) | cubicle keys add |
authorization_pending | Guest accounts and keys are still being applied | The Node is still applying your account and keys. Retry in a few seconds. |
Permission denied (publickey).
The Box refused your key. Run cubicle ssh-config acme/job-1 and read the not ready reason.
If there is none, check that --identity points at the private key whose public half you added
with cubicle keys add.
ssh: Could not resolve hostname …
The tunnel is not connected, the Box is not routed, you have not run cubicle ssh-config for the
Box, or your network blocks the tunnel's outbound UDP traffic to Gateway. Run cubicle connect
and check that the Box appears in cubicle status. Stopped Boxes, and Boxes you have no grant on,
do not resolve. A Box that became ready after you connected is added on the next refresh, within
about 10 seconds.
the host key for … changed
The full message is
the host key for jevbox-<box ID> changed. Verify the box was restored or replaced, then rerun with --accept-new-host-key,
where jevbox-<box ID> is the name of the Box's pinned host key. The API now publishes a
different host key for this Box than the one the CLI pinned in ~/.ssh/cubicle_known_hosts.
Confirm the change is expected, then accept the new key:
cubicle ssh-config acme/job-1 --accept-new-host-keyWARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
OpenSSH refused the connection because the key the Box's SSH server presented differs from the pinned one. Do not turn off host key checking. Compare the pin with the key the API publishes:
cubicle ssh-config acme/job-1If it does not report a changed host key, your pin matches the key the API publishes, so the host
key files under /etc/ssh inside the Box no longer match the key Cubicle generated, for example
because they were regenerated in the Box.
Errors fixed on other pages
the Cubicle tunnel is not installed; run `cubicle tunnel install`, or the helper is not responding: see the private network guide.api_key_not_allowed: API keys cannot use this route:CB_CLI_API_KEYis set, and SSH needs an interactive login. Unset it and runcubicle login; see API keys.
Related
- Networking: the private network, Gateway and what a Box can reach.
- Reach a Box over the private network:
cubicle connect, DNS names and services on other ports. - Move files into and out of a Box:
scp,sftpandrsyncwith the alias. - Manage organization members: grants for people and service accounts.
- Networking and SSH commands: every option of
ssh,ssh-configand the tunnel commands. - Run commands: exec for automation and service accounts.
- Remote development environments: a Box per developer for VS Code or Cursor, a team template and the daily lifecycle.
Move files into and out of a Box
Copy files to and from a Cubicle Box with scp, sftp or rsync through the cubicle ssh-config alias, write and read files from code with exec and base64 within the 64 KiB request body and 1 MiB output limits, and move large files through presigned storage URLs that the Box downloads or uploads.
Reach a Box over the private network
Connect your computer to Cubicle's private WireGuard network with cubicle connect, reach granted Boxes on any port by private DNS names such as job-1.acme.cubicle.internal, and manage tunnel sessions, devices and platform requirements on macOS and Linux.

