Skip to content

Deploy agents onto remote Linux hosts

Editions: OSS, Cloud, Enterprise. Unless stated otherwise, everything on this page ships in OSS.

The console's SSH and GCP deployment actions run the same preloop agents install-runtime and live validation path as the CLI. Success requires a real registered agent, its selected model binding, runtime version, and validated control plugin. Installer output is not streamed into the API or audit log because upstream tools can include credentials in their diagnostics.

Enable these methods explicitly on the API process:

PRELOOP_AGENT_DEPLOYMENT_ENABLED=true
PRELOOP_URL=https://preloop.example.com
# Optional. Private networks are refused unless the operator lists them.
PRELOOP_DEPLOY_SSH_ALLOWED_CIDRS=192.168.10.0/24
# Optional GCP backend, using the API process's Application Default Credentials.
PRELOOP_DEPLOY_GCP_PROJECT=my-project
PRELOOP_DEPLOY_GCP_ZONE=us-central1-a
PRELOOP_DEPLOY_GCP_NETWORK=global/networks/default

SSH deployment requires an account owner or administrator, a password or unencrypted private key, and an independently verified SSH host public key such as ssh-ed25519 AAAA.... Obtain the host key from the host's console or another trusted administrator. A network scan alone does not authenticate it. The API never uses its own SSH agent, personal keys, or automatic trust of an unknown host. It resolves the destination once and pins that address. Loopback, link-local, multicast and metadata destinations are always refused.

Linux targets need Bash, curl, Python 3, flock, internet access to the runtime publishers, and any sudo access required by their official installers. Agent installation may take several minutes. Configure the reverse proxy read timeout to at least 930 seconds. The shipped Docker and Helm console nginx templates apply this budget to the exact deployment endpoint. For an NGINX ingress in front of the console, also set ingress.annotations keys nginx.ingress.kubernetes.io/proxy-read-timeout and nginx.ingress.kubernetes.io/proxy-send-timeout to "930" (or longer). The API bounds work to 700 seconds and reserves up to 200 seconds to clean up a failed GCP VM. Closing the browser may not cancel a request already accepted by the server. Check the agent registry and GCP resource before retrying; reuse the deployment's idempotency key after an uncertain HTTP result. The deterministic VM name prevents duplicate resources.

GCP creates Ubuntu 24.04 VMs without an attached service account. It obtains the new SSH host key from the authenticated Compute serial-output API, blocks project-wide SSH keys, and uses a transient deployment key. The provisioner's cloud credentials remain on the Preloop API server. Failed installations, validation failures, timeouts, and task cancellations remove newly created VMs; a cleanup failure identifies the resource in the API error for manual removal. A successful VM remains until the operator deletes it, and continues to incur normal GCP charges.

Use a dedicated provisioner identity with only:

  • compute.instances.create, get, delete, getSerialPortOutput, setMetadata, setLabels
  • compute.disks.create
  • compute.subnetworks.use, useExternalIp, compute.networks.use
  • compute.zoneOperations.get

Restrict its instance permissions to the selected zone and names beginning preloop-agent- using an IAM condition, and permit SSH from the Preloop server in the selected network's firewall. The Ubuntu public image supplies image-read access. The service identity needs no IAM administration, storage access, service-account impersonation, or key-management permission.

For an unreleased CLI candidate, the operator may set PRELOOP_DEPLOY_CLI_URL to an HTTPS Linux binary URL and PRELOOP_DEPLOY_CLI_SHA256 to its SHA256. Both are required together and the host verifies the downloaded binary before use. Otherwise deployment uses the normal published CLI installer. Candidate binaries must match the host architecture; production should use the published installer.

The deployment API is synchronous: POST /api/v1/agent-deployments accepts idempotency_key, runtime (hermes or openclaw), model_id, target (ssh or gcp), optional compute_size, optional desktop (default false), and SSH connection fields for SSH. GET /api/v1/agent-deployments/capabilities reports configured methods. SSH secrets are used only in request memory. Enrollment uses a hashed temporary API key with the initiating owner's permissions, a twenty-minute expiry, and revocation after success, failure, or cancellation. Official runtime and uv installers run before this key is exported; the CLI's --install-only phase requires no login and skips enrollment. Only the trusted target's subsequent --skip-install onboarding phase receives it. The target is trusted with these bootstrap permissions; this is not a narrower enrollment-only scope.

OpenClaw control uses a healthy existing Python environment or installs uv, aiohttp and PyYAML in ~/.preloop-agent-control/venv, without system pip. Installation refuses successful onboarding if the required control plugin or channel is not ready.

Account-scoped start/failure/completion audit events record identifiers and the observed runtime version.

For disposable environments, set PRELOOP_DEPLOY_GCP_MAX_RUN_SECONDS=14400. GCE will delete successful as well as failed test VMs after four hours. The optional value must be between 60 seconds and seven days; unset means that a successful VM has no automatic lifetime limit.

Desktop (optional)

Set desktop: true to install a headless desktop on the target after live validation. The deployment runs preloop agents install-runtime <runtime> --install-only --skip-install --desktop, so the upstream runtime installer is not executed again after live validation. On Debian and Ubuntu that installs Xvfb on display :99, a Chromium browser, and x11vnc. The VNC server listens on 127.0.0.1:5900 only (-localhost, no -listen 0.0.0.0). A random password is stored with x11vnc -storepasswd in ~/.preloop/desktop/vncpasswd (mode 0600) and is not written anywhere else. The password is a command argument for that short-lived x11vnc process, so a desktop install assumes a single-tenant host. VNC DES uses the first 8 characters of the password. Package installation refreshes the package lists (apt-get update) and then runs apt-get install as root, or sudo -n apt-get when the SSH user is not root. The GCP Ubuntu image grants the metadata SSH user passwordless sudo; a target without that privilege reports desktop: failed and does not change the validated runtime. The CLI records ~/.preloop/desktop.json and exports DISPLAY=:99 for the runtime. A systemd user unit preloop-desktop.service keeps the session up. For a non-root user the CLI runs sudo -n loginctl enable-linger first so the unit survives the end of the deployment SSH session. If a systemd user session is unavailable the CLI starts ~/.preloop/desktop/start.sh with nohup and says so. The install fails (and the deployment reports desktop: failed) when nothing accepts connections on 127.0.0.1:5900 within 15 seconds. The desktop step's output is kept on the host in ~/.local/state/preloop/desktop.log.

The listener is loopback-only. Deployment does not change GCP firewall rules and does not open port 5900 on any non-loopback interface. Other Linux distros are refused with desktop_unsupported_distro and do not change the runtime install. A desktop failure does not change the deployment result after runtime validation has already passed. The result's desktop field is installed, failed, or skipped.

Verify on the host:

ss -ltnp | grep 5900

The socket must be loopback only: 127.0.0.1:5900 (x11vnc 0.9.16 on Ubuntu 24.04 also binds [::1]:5900 despite -noipv6). DISPLAY=:99 chromium --headless=new --screenshot should write a screenshot, and preloop agents status <runtime> --json reports desktop.installed when ~/.preloop/desktop.json exists.

Live desktop view

Operators watch the display from the console, next to the agent's timeline, through a brokered live view. No inbound port is opened on the host: the host keeps one outbound connection to Preloop and relays the screen only while a view is open. The API must run with PRELOOP_DESKTOP_VIEWS_ENABLED=true (off by default).

Host side. With desktop: true the deployment's desktop step also creates a desktop credential bound to the deployed agent, stores it in ~/.preloop/desktop/token (mode 0600), and enables the systemd user unit preloop-desktop-tunnel.service, which runs preloop desktop serve. The endpoint registers the host (a stable id kept in ~/.preloop/desktop.json, hostname, display) and heartbeats. When a view opens it connects to the local x11vnc, does the VNC password handshake itself and hands Preloop a stream that needs no password: the VNC password never leaves the host and never reaches the browser. A failed tunnel step reports desktop: failed.

On a host that already runs an agent (Debian or Ubuntu), enable the same thing after the fact:

preloop desktop enable --agent <agent-id>

--agent creates the desktop credential with your login, so the desktop shows up on that agent's page and on its sessions. Without a login, pass an existing agent key with --agent-key or PRELOOP_DESKTOP_TOKEN. --dry-run prints the steps. Check the tunnel with systemctl --user status preloop-desktop-tunnel.

From the console. An active agent with no online desktop shows Live desktop: none under its summary to users with manage_agents. When the agent has a live Agent Control connection, Enable desktop sends desktop.install over that channel and the runtime plugin (Hermes, OpenClaw) runs preloop desktop enable --agent <id> on its host with the CLI login it already has; the Desktop tab appears once the tunnel registers. An unsupported OS or distro, or missing root or passwordless sudo, is shown with a link here and leaves the agent untouched. Without a control channel, or with a plugin that does not know the command, the console shows the one-line command to run on the host instead and sends nothing. Requests are audited as desktop.install.requested, desktop.install.succeeded and desktop.install.failed.

Sessions on the same host. The installer also exports PRELOOP_DESKTOP_ENDPOINT_ID (the endpoint id from ~/.preloop/desktop.json) next to DISPLAY=:99 in the runtime environment. CLI hooks, the Hermes plugin and the Browser Use reporter send it when they create or report on a runtime session (desktop_endpoint_id on POST /auth/runtime-sessions/token, or PUT /runtime-sessions/{id}/desktop-endpoint), so that session gets the Live desktop button even when it runs with a different credential than the tunnel. The value only matches desktops in the session's own account.

Console. An agent with an online desktop shows a Desktop tab, and its live sessions show a Live desktop button. A view is view only: keyboard, mouse and clipboard input are dropped by the API, not just by the browser. "Request control" (if you have the permission) forwards input for at most 10 minutes. You give a reason; when the account has another admin who can decide approvals, the request becomes a desktop_control approval that someone other than you must approve (the viewer shows "Waiting for another admin to approve control" and stays view only until then). With no one else to ask, your reason is the confirmation. A refused or expired request writes desktop.control.denied; an approved one writes desktop.control.granted with the approver. One viewer per desktop at a time; a second operator is told who holds it. Views last 15 minutes by default (PRELOOP_DESKTOP_VIEW_MAX_SECONDS caps them, default 60 minutes) and close after 120 seconds without viewer traffic. Opening, ending, and taking or releasing control are audit events. Nothing is recorded: no frames, no screenshots.

Native VNC client. From an operator machine:

preloop agents desktop <agent> --port 5901
vncviewer 127.0.0.1:5901

The CLI listens on 127.0.0.1 only, uses the same view and the same checks as the console, and ends the view when the VNC client disconnects or on Ctrl-C.

Permissions: opening a view (console or preloop agents desktop) needs control_managed_agent (the one that guards agent takeover) in the agent's account, checked when the view is created and again when the viewer connects. preloop agents desktop also lists agents to resolve a name, which needs view_agents. preloop desktop enable --agent creates an agent credential, which needs manage_agents; re-running it reuses the stored credential for the same agent (--rotate-credential mints a new one). API replicas join the two halves of a view over NATS, so no sticky routing is required.

Managed employee endpoints

For existing employee workstations, use the separate staged MDM recipes. They cover managed-file delivery and scoped inventory rather than remote runtime installation.