Skip to content

Your machines

A runner is a machine you already have. Surfra does not rent it. The surfra command on that machine heartbeats, pulls the image, and runs one app at a time.

Local runners are free. If a runner serves an app, a later plan charges a flat $5 for that account when the runner was used in the last 30 days. That fee is not charged now.

Terminal window
surfra login

This opens the dashboard. Approve the code while you are signed in. The session is stored in ~/.surfra/config.json on that machine.

Terminal window
surfra runner register

The CLI guesses vCPU, RAM, and GPU, then opens a page where you can change those values and name the device. The name is the id you put in an app, for example desk-4090.

Registration binds the runner to the account that approved it. A runner runs apps for that account only. Register again while logged into a different account to move it. surfra runner whoami prints the id, the account, the hardware, and whether an app is using it.

surfra runner start does not install system packages. If Docker is missing, it prints the script to run:

Terminal window
surfra runner install cpu
surfra runner install gpu

The CPU script installs Docker. The GPU script installs Docker and the NVIDIA container toolkit, so the GPU is visible inside the container. Both scripts need sudo.

Terminal window
surfra runner start

While this is running, the machine heartbeats and accepts work. It pulls the image and starts it with surfra worker. Registry passwords are sealed to a key that stays in ~/.surfra/runner.json. The image is not encrypted. The parent process inside the container holds the replica token.

A runner can be listed on several apps. The first app to get a free runner keeps it until the container exits. Other apps that list it stay queued and try their next provider. That is app-to-app contention: the runner is still a backend, and it does not schedule a second app on top of the first.

autoscale={
"min_replicas": 0,
"max_replicas": 1,
"providers": [
{"provider": "runner", "runner": "desk-4090", "priority": 1},
{"provider": "runpod", "gpu": "rtx4090", "priority": 2},
],
}

Use the runner id from surfra runner whoami if you prefer the id over the name.