Set Up A VPS Or VM For Breyta SSH
Use this guide to provision a Linux VM/VPS, prepare SSH auth and host verification, create a Breyta SSH connection, and smoke test the :ssh step before moving to long-running remote agents.
Quick Answer
If you want Breyta to run commands on your own server, you need:
- a Linux VM/VPS with a public hostname or IP
- inbound SSH access on port
22(or your chosen SSH port) - a Linux user that Breyta can log in as
- SSH auth, preferably a private key stored as a Breyta secret
- a
known_hostsentry stored as a Breyta secret - a flow/template that declares an SSH connection slot and the secret refs you want to use
For short commands, use a single sync :ssh step.
For long-running jobs, use :ssh kickoff plus :wait callback.
Choose The Right Execution Model
| Use case | Pattern | Why |
|---|---|---|
| Run a short remote command | Sync :ssh | The flow can wait for command exit naturally. |
| Start an agent, worker, scraper, or code job that can run for minutes or hours | :ssh kickoff + :wait callback | More reliable than keeping one SSH activity open the whole time. |
| Unsure which one you need | Start with sync only if the command is short and chatty | Silent or long-lived jobs should move to the async pattern early. |
Use Step SSH for schema details and Remote Agents (SSH) for the long-running pattern.
1. Provision A Linux Host
Breyta works best with a standard Linux VM/VPS. Ubuntu and Debian are the easiest starting point.
Minimum practical requirements:
| Requirement | Why it matters |
|---|---|
| Public DNS name or public IP | Production-like Breyta environments block private-IP SSH targets by default. |
| Inbound SSH access | Breyta needs to reach the host over SSH. |
| Outbound HTTPS access | Remote-agent flows need to POST results back to Breyta callbacks. |
| A stable Linux user | Your SSH connection config must specify a username. |
sh on the remote host | The SSH step runs commands through sh -lc. |
If you are using a cloud provider, reserve a static IP or DNS name when possible. It makes known_hosts and future rotation much easier.
What To Buy
For a first Breyta SSH setup, look for:
- a self-managed VPS or VM, not shared hosting
- Ubuntu 24.04 LTS or Debian 12
- a public IPv4 address
- a provider option to inject your SSH public key during provisioning
- snapshots or backups
- a region close to your users or data
A small starting machine is usually enough for smoke tests and simple automation:
1-2 vCPU2-4 GB RAM40+ GB SSD
Increase size when your remote workload actually needs it, for example code indexing, browser automation, or larger AI/tooling jobs.
Popular Starting Points
These are common places to start if you do not already have a preferred provider:
- Hetzner Cloud for straightforward self-managed cloud servers. Hetzner also publishes a direct create a server guide.
- Hostinger VPS Hosting if you want a more consumer-friendly VPS product with Linux templates and an account dashboard.
- Google Cloud Compute Engine if you already use Google Cloud or want deeper cloud-networking and IAM controls. Google also documents how to create and start a Compute Engine instance.
No matter which provider you choose, try to make the same choices during provisioning:
- pick a plain Linux image rather than an app-specific template unless you know you need it
- add your SSH public key at creation time if the provider supports it
- enable backups or snapshots
- keep the first machine simple
- confirm that the machine will have a public IP or public DNS name
2. Create A Linux User For Breyta Work
You can use an existing user like ubuntu, but a dedicated user is usually cleaner.
Example on the VM as an admin:
sudo adduser breyta
sudo usermod -aG sudo breyta
Then install your public key for that user:
sudo install -d -m 700 -o breyta -g breyta /home/breyta/.ssh
sudo tee /home/breyta/.ssh/authorized_keys >/dev/null <<'EOF'
ssh-ed25519 AAAA... your-key-comment
EOF
sudo chown breyta:breyta /home/breyta/.ssh/authorized_keys
sudo chmod 600 /home/breyta/.ssh/authorized_keys
If you do not have a key yet, create one locally:
ssh-keygen -t ed25519 -f ~/.ssh/breyta_vm -C "breyta-ssh"
3. Allow SSH Inbound Safely
Open your SSH port in the provider firewall or security group.
Prefer one of these:
- only allow your office/VPN egress IPs
- use a bastion or VPN
- use your cloud provider's SSH forwarding/IAP features
Avoid leaving port 22 open to 0.0.0.0/0 unless you have to.
If you use a non-default SSH port, use that same port consistently in the SSH check, ssh-keyscan, and the Breyta connection config below.
4. Verify SSH Before Involving Breyta
Confirm that normal SSH works from your own machine first:
ssh -i ~/.ssh/breyta_vm breyta@<HOST> 'uname -a'
Do not continue until this works. Breyta will fail with the same underlying network or auth problems.
Use the same private key file here that you plan to store in Breyta. Do not rely on whichever key your local SSH agent happens to pick automatically.
5. Capture The Host Key For known_hosts
The :ssh step expects host key verification unless you explicitly disable it for development.
Create a known_hosts file locally:
mkdir -p ./tmp
ssh-keyscan -H -p 22 <HOST> > ./tmp/ssh-known-hosts
Then verify that the fingerprint you captured matches the host you actually provisioned. A simple check is:
ssh-keygen -lf ./tmp/ssh-known-hosts
and on the VM:
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Proceed only when the fingerprints match, or when you have matched the fingerprint against the value shown by your VPS/cloud provider.
Recommended:
- use the final DNS name if you have one
- regenerate this file if you replace the VM or rotate its host keys
Dev-only escape hatch:
- set
"insecure-skip-host-key-verification": trueon the SSH connection config - do this only for temporary development environments
6. Prepare The VM Runtime
For simple sync SSH commands, the VM only needs the command-line tools your command uses.
For long-running remote-agent flows, prepare the VM like an actual worker machine:
- install the CLIs and runtimes the job needs
- make sure repository access and auth are already working if the job touches code
- make sure the VM can reach Breyta callback URLs over HTTPS
- use
nohup,tmux,systemd, or another background-process strategy
Example: if your Breyta flow starts a long-running code or agent job on the VM, the machine may also need:
gitcurl- the language runtime your task uses
- any provider CLI or SDK the task depends on
- repository auth if it clones private code
- outbound access to
https://flows.breyta.aiso it can send a callback when the work is done
7. Make Sure The Flow Or Template Exposes SSH Slots
The connection itself points at secret refs, but the secret values still need to be stored in the workspace through flow configuration.
That means your flow or copied template should declare:
- one SSH connection slot, often
:vps - one secret slot for the private key
- one secret slot for
known_hosts - optionally one secret slot for a key passphrase
Example:
{:requires [{:slot :vps
:type :ssh
:label "VPS (SSH)"}
{:slot :ssh-private-key
:type :secret
:secret-ref :ssh-private-key
:label "SSH private key"}
{:slot :ssh-known-hosts
:type :secret
:secret-ref :ssh-known-hosts
:label "SSH known_hosts"}
{:slot :ssh-key-passphrase
:type :secret
:secret-ref :ssh-key-passphrase
:label "SSH key passphrase"
:optional true}]}
This is a common pattern for SSH-backed templates because the connection can reference stable secret ids while the actual secret values stay out of the flow definition.
8. Store The SSH Secrets Through Flow Configuration
The command examples below assume your breyta CLI is already configured with default API, workspace, and token settings.
Use @/absolute/path or @./relative/path to read multiline file content into a secret value. Do not use @~/.ssh/..., because shells do not expand ~ reliably in that form.
Assuming your flow exposes the secret slots above:
breyta \
flows configure <FLOW_SLUG> \
--set ssh-private-key.secret=@$HOME/.ssh/breyta_vm \
--set ssh-known-hosts.secret=@./tmp/ssh-known-hosts
If the key has a passphrase:
breyta \
flows configure <FLOW_SLUG> \
--set ssh-key-passphrase.secret=@./tmp/ssh-key-passphrase.txt
Use Flow Configuration and Secrets for the full model.
9. Create The SSH Connection In Breyta
Create a workspace connection that points at the host and references the stored secrets:
breyta \
connections create \
--type ssh \
--backend ssh \
--name "My VPS (SSH)" \
--config '{
"host":"<HOST>",
"port":22,
"username":"breyta",
"auth":{
"type":"private-key",
"secret-ref":"ssh-private-key",
"passphrase-secret-ref":"ssh-key-passphrase"
},
"known-hosts":{"secret-ref":"ssh-known-hosts"}
}'
Copy the returned connection id.
Optional verification:
breyta \
connections test <CONNECTION_ID>
This confirms that Breyta sees the connection as configured and active. It is useful, but it is not a substitute for a real SSH-backed flow run.
10. Bind The Connection To The Flow
Bind the SSH connection to the flow's SSH slot and check configuration:
breyta \
flows configure <FLOW_SLUG> \
--set vps.conn=<CONNECTION_ID>
breyta \
flows configure check <FLOW_SLUG>
If the flow uses a different slot name than vps, bind that slot instead.
If this is a brand-new flow with no active version yet, also prepare the live target now so the first release can succeed:
breyta \
flows configure <FLOW_SLUG> \
--target live \
--version latest \
--set vps.conn=<CONNECTION_ID>
11. Smoke Test With A Safe Command
Do one short, boring command before you trust the VM for a real template flow:
echo hellouname -apwd
If your flow accepts a command input, the run may look like this:
breyta \
flows run <FLOW_SLUG> --wait \
--input '{"command":"uname -a"}'
If your copied template does not accept a freeform command input, run its normal manual trigger with the smallest safe input it supports.
If flows run returns no_active_version, the flow has not been released yet. Create the first release, then rerun the smoke test:
breyta flows release <FLOW_SLUG>
For flows with :requires slots, that first release will only succeed if the live target has already been configured for --version latest, as shown above.
12. Move Long-Running Jobs To The Callback Pattern
If the remote job can take several minutes or more, do not keep one sync SSH step open for the full run.
Use this pattern instead:
- Breyta uses
:sshto start the remote worker quickly. - The remote worker keeps running on the VM.
- Breyta pauses in a
:waitstep. - The remote worker POSTs JSON back to Breyta when it finishes.
Your VM must be able to reach a callback URL shaped like:
https://flows.breyta.ai/<workspace-id>/events/<wait-key>
Local check from the VM:
curl -I https://flows.breyta.ai
Use Remote Agents (SSH) for the step-level authoring pattern.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| SSH connect timeout | Firewall, wrong host, wrong port | Re-test plain ssh from your machine first. |
Permission denied (publickey) | Wrong user or wrong key | Verify the Linux username and authorized_keys. |
Missing SSH host key verification config | No known_hosts secret configured | Store known_hosts and reference it from the connection. |
Secret not found | The flow never stored the referenced secret | Configure the secret slot on the flow before creating the connection. |
SSH host resolves to private IP | Host is not publicly reachable for your Breyta environment | Use a public host or an environment configured for private IP SSH. |
| The SSH step exits but the remote job is still running | You modeled a long-lived process as sync SSH | Switch to :ssh kickoff + :wait callback. |