ReaperCI Documentation

ReaperCI documentation

Outbound VPS connector

reaperci-connector is the managed-cloud transport for Linux Docker/Compose servers. It initiates every connection to the hub over HTTPS, so the VPS needs no public management port and the cloud does not need inbound SSH access. Direct SSH remains the default self-hosted transport.

Hub endpoint

Run the connector protocol on a dedicated TLS listener. The certificate must match the public connector hostname and should be issued by a normal public CA:

REAPERCI_CONNECTOR_ADDR=0.0.0.0:8443
REAPERCI_CONNECTOR_TLS_CERT_FILE=/run/secrets/connector-fullchain.pem
REAPERCI_CONNECTOR_TLS_KEY_FILE=/run/secrets/connector-key.pem

Publish that listener as HTTPS port 443 without terminating client TLS in a reverse proxy. The hub uses TLS 1.3 and optional client verification: enrollment is authorized by a short-lived one-time token, while heartbeat, long polling, results, and rotation require a client certificate signed by the encrypted per-instance connector authority.

Enroll a server

In Settings > Connectors, create an enrollment named for the VPS. Copy the token once, then on the server run:

install -d -m 0700 /var/lib/reaperci-connector
printf '%s' "$ONE_TIME_TOKEN" | reaperci-connector enroll \
  --url https://connect.example.com \
  --name edge-1 \
  --deploy-roots /srv,/opt \
  --token-stdin

If the public endpoint uses a private server CA, pass --server-ca-file /path/to/ca.pem. The enrollment token is never persisted. The generated client certificate, private key, and configuration are written with user-only permissions. A consumed or expired token cannot be replayed.

After the first heartbeat appears as online, open Servers, choose Outbound connector, and link the session to a server name. Environments and deployment targets reference that server name exactly as they do a direct SSH target.

Run the connector under a dedicated service account:

[Unit]
Description=ReaperCI outbound connector
After=network-online.target docker.service
Wants=network-online.target

[Service]
User=reaperci-connector
Group=docker
ExecStart=/usr/local/bin/reaperci-connector run --data-dir /var/lib/reaperci-connector
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/lib/reaperci-connector /etc/nginx/conf.d

[Install]
WantedBy=multi-user.target

Grant only the OS permissions needed for selected capabilities. Nginx apply and reload generally need additional narrowly scoped privilege; omit nginx capabilities when the service account cannot safely receive them. Compose commands are additionally confined to the absolute directories supplied with --deploy-roots (default /srv,/opt).

Add customer-provided build capacity

The same outbound connector can register a local BuildKit daemon as a BYO runner. No BuildKit port is exposed to the hub and the worker does not receive a general-purpose shell command:

printf '%s' "$ONE_TIME_TOKEN" | reaperci-connector enroll \
  --url https://connect.example.com \
  --name build-edge-1 \
  --buildkit-addr tcp://127.0.0.1:1234 \
  --build-platforms linux/amd64,linux/arm64 \
  --build-concurrency 2 \
  --runner-labels region=customer,tier=dedicated \
  --token-stdin

The heartbeat creates a Runner resource linked to that connector certificate. Admins can disable the runner or cap its concurrency in Settings > Connectors. The reported hardware capacity never raises an administrator cap. Use runner: byo in .reaperci.yml to require customer capacity, runner: hosted to require the hub worker, or runner: auto (the compatibility default) to allow either:

runner: byo
steps:
  - name: test
    image: node:22
    platform: linux/amd64
    run: pnpm test

Only workers reporting every explicitly requested step platform may claim the build. BYO assignments are recorded on the build as runnerKind: byo; managed usage metering can therefore exclude their minutes without trusting a client claim. The registry push host configured on the hub must be reachable from the worker.

When ReaperCI Cloud disables hosted compute for an expired trial or a non-serviceable subscription, an explicit runner: hosted build is rejected. An auto build stays queued for a compatible BYO worker but cannot be claimed by the hub's local worker. An explicit runner: byo build is unchanged. This keeps customer-provided capacity available without turning BYO minutes into hosted usage.

Source is streamed from the exact checked-out commit over the certificate- authenticated connector endpoint. Repository and environment secrets stay in the encrypted command queue, logs are masked on both the worker and hub, retained artifacts stream back through an assignment-scoped upload, and cache generations remain control-plane records while cache data stays on the worker. Cancellation is polled during BuildKit execution, and stale worker workspaces are removed after 24 hours if normal typed cleanup cannot be delivered.

Safety and recovery

There is no generic shell operation. Commands are validated twice—when queued by the hub and before dispatch in the connector—and are limited to:

  • host metrics and HTTP health checks;
  • Docker list/stats/events/log/restart/stop/pull and sanitized deployment drift inspection;
  • Docker Compose and single-container deploy/rollback;
  • allowlisted nginx config list/read/apply/rollback.
  • assignment-scoped BuildKit execution and workspace cleanup when the connector was explicitly enrolled with --buildkit-addr.

Command IDs are stable. Results are cached locally before delivery so reconnects can replay the result instead of repeating a completed operation. Certificates rotate automatically before expiry; the previous certificate has a ten-minute grace window so a lost rotation response does not strand the server. Revocation is enforced by the hub immediately. Heartbeats update last-known state, and long-poll requests reconnect with bounded exponential backoff.

For a successful deployment, the hub captures only the container name, image reference/digest, running and health state, or the equivalent Compose service summary. It does not store raw docker inspect output. ReaperCI periodically refreshes the newest successful deployment for each managed runtime. If a server is offline, the deployment is marked unavailable while its previous observation and lastKnownStateAt remain visible instead of being presented as current.

Environment values and registry credentials used by deployments are encrypted with the instance master key while queued in SQLite. They are decrypted only for delivery over the connector's authenticated TLS session, written to short-lived mode-0600 files or a temporary Docker client directory, and removed after the operation. Administrative command reads show a redacted payload.