Create an SSH key and connect to your Spark
Every GPUwerk instance is reachable over SSH, and there is no password to fall back on: the tenant image sets PasswordAuthentication no, so a key pair is the only way in. This takes about two minutes if you have never made one.
1, Generate the key
A key pair is two files. The private key stays on your laptop forever and is the actual secret. The public key, the one ending in .pub, is safe to paste anywhere, and it is what you hand to us.
Use ed25519. It is the modern default, the keys are short, and OpenSSH has supported it since version 6.5 in 2014, so nothing you will connect to is too old for it.
ssh-keygen -t ed25519 -C "you@company.com"
# PowerShell, OpenSSH ships with Windows
ssh-keygen -t ed25519 -C "you@company.com"
Press Enter to accept the default path (~/.ssh/id_ed25519, or C:\Users\you\.ssh\id_ed25519 on Windows). When it asks for a passphrase, set one. A private key with no passphrase is a plaintext credential sitting in your home directory, and the next step means you only type it once per session anyway.
Already have a key? Run ls ~/.ssh/*.pub first. If you see id_ed25519.pub or id_rsa.pub, reuse it. There is no benefit to a fresh key per provider, and a second key is a second thing to lose.
2, Load it into the agent
The ssh-agent holds your decrypted private key in memory, so the passphrase is asked once instead of on every connection.
# --apple-use-keychain stores the # passphrase across reboots ssh-add --apple-use-keychain ~/.ssh/id_ed25519
eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519
# once, as Administrator
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
3, Give the public key to your instance
Print the public key and copy the whole line, from ssh-ed25519 through the trailing comment:
# macOS, straight to the clipboard pbcopy < ~/.ssh/id_ed25519.pub # Linux cat ~/.ssh/id_ed25519.pub # Windows PowerShell Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | Set-Clipboard
It is one line, and it looks like this:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK7f...4mQ2 you@company.com
Paste it into the SSH key field when you deploy an instance in the console. We accept ssh-ed25519, ssh-rsa and ecdsa- keys. The key is written to /root/.ssh/authorized_keys when the container starts, and it is stored with the instance, so a rebuild keeps the same key and you do not paste it twice.
Paste the public key, never the private one. If the text you copied starts with -----BEGIN OPENSSH PRIVATE KEY-----, you have the wrong file. The right one is the same name with .pub on the end.
4, Connect
Instances do not each get their own SSH hostname. They share one entry point, ssh.gpuwerk.com, and your instance is identified by its port. The console shows the port on the instance page; numbering starts at 22001 and counts up across the fleet.
ssh -p 22001 root@ssh.gpuwerk.com
The first connection asks you to confirm a host key fingerprint. Say yes once and it is recorded in ~/.ssh/known_hosts. Host keys are kept in the instance workspace, so stopping and starting an instance does not invalidate them.
You land as root in /workspace. That directory is the part that survives a stop: it is archived when the instance stops and restored when it starts again. Anything you install outside /workspace lives on the container filesystem and is gone after a rebuild, so keep your models, checkouts and datasets under /workspace.
5, Make it one word to type
Nobody wants to remember a port number. Add this to ~/.ssh/config, creating the file if it is missing (on Windows it is C:\Users\you\.ssh\config):
Host spark
HostName ssh.gpuwerk.com
Port 22001
User root
IdentityFile ~/.ssh/id_ed25519
# keeps an idle session from being dropped
ServerAliveInterval 60
Now ssh spark is the whole command, and scp bigfile.jsonl spark:/workspace/ works the same way. The Remote-SSH extensions in VS Code and Cursor read this file too, so the host appears in their picker with no extra configuration.
6, Reach a web UI over the tunnel
When you start something with a browser interface on the instance, Open WebUI, a Jupyter server, a vLLM endpoint you would rather not publish, do not open it to the internet. Forward the port over the SSH connection you already have:
# -N means "no shell, just the tunnel"
ssh -N -L 8080:localhost:8080 spark
Leave that running, and http://localhost:8080 in your browser is the instance's port 8080: encrypted, authenticated by your key, invisible to everyone else. Chain more -L flags for more ports.
Instances also get a public HTTPS endpoint at https://<name>.gpuwerk.com, routed to port 8888 in the container. Use that for what you intend to share, and the SSH tunnel for what you do not.
When it does not work
Three errors cover almost everything.
Permission denied (publickey). The server did not accept your key. Run ssh -v -p 22001 root@ssh.gpuwerk.com and read the Offering public key lines. If your key is never offered, the agent does not have it: check ssh-add -l and add it. If it is offered and still refused, the key on the instance is not the one you are holding, and redeploying with the right key is faster than debugging it.
Bad permissions, or UNPROTECTED PRIVATE KEY FILE. OpenSSH refuses to use a private key that other users on your machine can read. Fix it with chmod 700 ~/.ssh && chmod 600 ~/.ssh/id_ed25519. This is the usual result of moving a key between machines on a USB stick or a cloud drive.
REMOTE HOST IDENTIFICATION HAS CHANGED. A different machine is answering on that port, which is what you would expect after an instance is terminated and its port is recycled to someone else. Clear the stale entry with ssh-keygen -R "[ssh.gpuwerk.com]:22001" and reconnect. Do not do that reflexively on a host you have not just changed, since the same warning is what a genuine interception looks like.