Guides

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_hosts entry 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 casePatternWhy
Run a short remote commandSync :sshThe 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 callbackMore reliable than keeping one SSH activity open the whole time.
Unsure which one you needStart with sync only if the command is short and chattySilent 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:

RequirementWhy it matters
Public DNS name or public IPProduction-like Breyta environments block private-IP SSH targets by default.
Inbound SSH accessBreyta needs to reach the host over SSH.
Outbound HTTPS accessRemote-agent flows need to POST results back to Breyta callbacks.
A stable Linux userYour SSH connection config must specify a username.
sh on the remote hostThe 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 vCPU
  • 2-4 GB RAM
  • 40+ 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:

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": true on 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:

  • git
  • curl
  • 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.ai so 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 hello
  • uname -a
  • pwd

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:

  1. Breyta uses :ssh to start the remote worker quickly.
  2. The remote worker keeps running on the VM.
  3. Breyta pauses in a :wait step.
  4. 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

SymptomLikely causeFix
SSH connect timeoutFirewall, wrong host, wrong portRe-test plain ssh from your machine first.
Permission denied (publickey)Wrong user or wrong keyVerify the Linux username and authorized_keys.
Missing SSH host key verification configNo known_hosts secret configuredStore known_hosts and reference it from the connection.
Secret not foundThe flow never stored the referenced secretConfigure the secret slot on the flow before creating the connection.
SSH host resolves to private IPHost is not publicly reachable for your Breyta environmentUse a public host or an environment configured for private IP SSH.
The SSH step exits but the remote job is still runningYou modeled a long-lived process as sync SSHSwitch to :ssh kickoff + :wait callback.

Related

As of Mar 13, 2026