SSH execution backends
By default a runner runs each claimed task in a container on its own local
Docker host (SUPACLOUD_RUNNER_EXECUTION_BACKEND=docker). ADR 0046 Phase 3 adds
two SSH execution backends so a runner can instead drive a remote host. Pick
a backend with SUPACLOUD_RUNNER_EXECUTION_BACKEND:
| Value | What it does | Isolation | Status |
|---|---|---|---|
docker |
Local Docker host (the default). | Full container caps. | GA |
ssh-docker |
Remote Docker daemon over SSH. | Full — identical container caps on the remote host. | Experimental (see caveats) |
raw-ssh-exec |
Agent as a raw remote process over SSH. | None — no container, no resource caps. | Experimental, double-gated |
Recommended: ssh-docker (remote Docker, isolation preserved)
Section titled “Recommended: ssh-docker (remote Docker, isolation preserved)”A DockerManager pointed at a remote daemon makes this backend run each task
through the same code path as a local run — so cap_drop=ALL,
no-new-privileges, a read-only rootfs, tmpfs mounts and the memory/CPU/pids
limits are all preserved on the remote host.
-
Install an SSH client on the runner host. bollard’s SSH transport shells out to the system
sshbinary (docker system dial-stdio). The SupaCloud server image shipsopenssh-client; a bespoke runner image may not — install it (apt-get install -y openssh-client) or the backend cannot connect. -
Configure host-key verification in your system ssh client (mandatory). For
ssh-docker, SupaCloud cannot enforce the host key itself — bollard’s SSH transport shells out to the systemsshbinary and exposes only a key-file path, not-ooptions. You must configure that client to pin the host, then acknowledge the delegation. Add the remote host’s key to aknown_hostsfile and point the runner’s ssh at it with strict checking, e.g. in the runner user’s~/.ssh/config:Host remote-host.exampleStrictHostKeyChecking yesUserKnownHostsFile /etc/supacloud/known_hostsThen set the backend env, including the explicit delegation acknowledgment:
SUPACLOUD_RUNNER_EXECUTION_BACKEND=ssh-dockerSUPACLOUD_SSH_HOST=ssh://docker@remote-host.example:22SUPACLOUD_SSH_KEY_PATH=/etc/supacloud/id_ed25519SUPACLOUD_SSH_KNOWN_HOSTS=/etc/supacloud/known_hostsSUPACLOUD_SSH_DOCKER_HOSTKEY_DELEGATED=trueThe runner refuses to start without both
SUPACLOUD_SSH_KNOWN_HOSTS(existing, non-empty) andSUPACLOUD_SSH_DOCKER_HOSTKEY_DELEGATED=true.
Known gaps (ssh-docker)
Section titled “Known gaps (ssh-docker)”- Live event relay. The agent bridge resolves only on the remote host’s
agent network, so live UI progress needs an
ssh -Lforward that v1 does not set up. The run still reportsdone/failedcorrectly — only the live stream is absent. - Live round-trip is not in CI. There is no remote Docker daemon (or system
ssh) in the test image, so the end-to-end remote run is validated only by an
env-gated test (
SUPACLOUD_TEST_SSH_DOCKER_HOST).
raw-ssh-exec (unsandboxed, double-gated)
Section titled “raw-ssh-exec (unsandboxed, double-gated)”This backend runs the agent as a bare remote process — no container, no
resource caps, no event relay. It exists only to satisfy the ADR 0046 decision
to ship both SSH variants. Prefer ssh-docker unless you specifically need it.
It is double-gated — both opt-ins are required and it fails closed otherwise:
- GATE 1 (runner boot):
SUPACLOUD_RUNNER_ALLOW_UNSANDBOXED=true. Selectingraw-ssh-execwithout it is a hard boot error. - GATE 2 (dispatch): the runner must advertise
capabilities.backends: ["ssh-exec"]at registration and the task must resolve to thessh-execbackend. A default (docker) task never lands on an ssh-exec-only runner, and an ssh-exec task never lands on a docker-only runner.
Because the work payload carries no agent entrypoint (it normally lives in the container image), you must supply a curated launch command:
SUPACLOUD_RUNNER_EXECUTION_BACKEND=raw-ssh-execSUPACLOUD_RUNNER_ALLOW_UNSANDBOXED=trueSUPACLOUD_SSH_HOST=ssh://agent@remote-host.example:22SUPACLOUD_SSH_KEY_PATH=/etc/supacloud/id_ed25519SUPACLOUD_SSH_KNOWN_HOSTS=/etc/supacloud/known_hostsSUPACLOUD_SSH_EXEC_AGENT_CMD="run-agent --image {image} --type {agent_type}"{image} and {agent_type} are substituted shell-quoted, and the task’s
environment is exported through a quoted prologue — every value is treated as
untrusted and escaped, so a hostile image name or env value cannot inject a
command.