Deploy

Last updated on August 14, 2026

Once .env is ready, re-run the deploy script. The license key is saved in .env, so you do not need to pass it again:

curl -fsSL https://tofupilot.sh/deploy | bash

The script validates your configuration, pulls the Docker images, and starts six containers.

ContainerRole
tofupilot-dashboardTofuPilot application
tofupilot-pgPostgreSQL
tofupilot-seaweedSeaweedFS file storage
tofupilot-proxyTraefik reverse proxy with automatic HTTPS
tofupilot-centrifugoRealtime WebSocket server (live status, telemetry, build logs)
tofupilot-deployerBuild worker for the deployments pipeline (Docker-in-Docker)

After each deploy or update, the script reports the running version and domain to Orbit so you can monitor instances. No other data leaves your server.

The tofupilot-deployer container needs standard (root) Docker because it runs a privileged build sandbox. Under rootless Docker it stays down and the build pipeline is unavailable; everything else, including realtime, works normally.

First login

Open your domain (e.g. https://tofupilot.yourcompany.com) and create your first account. That account becomes the organization admin, and each self-hosted instance supports one organization.

Upload runs

To upload test Runs, point your scripts to your instance URL. The SDK reads your API key from the TOFUPILOT_API_KEY environment variable, or you can pass it directly.

import openhtf as htffrom tofupilot.openhtf import uploaddef main():  test = htf.Test(      procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",  # procedure UUID from the dashboard      part_number="PCB1",  )  test.add_output_callbacks(upload(      url="https://tofupilot.yourcompany.com",      # verify="/etc/ssl/your-ca.crt",  # if the instance uses a private CA  ))  test.execute(lambda: "PCB1A001")if __name__ == '__main__':  main()
from datetime import datetime, timezone, timedeltafrom tofupilot.v2 import TofuPilotdef main():  client = TofuPilot(      api_key="your_api_key",      server_url="https://tofupilot.yourcompany.com/api"  )  started_at = datetime.now(timezone.utc)  ended_at = started_at + timedelta(seconds=30)  run = client.runs.create(      procedure_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",  # procedure UUID from the dashboard      serial_number="PCB1A001",      part_number="PCB1",      outcome="PASS",      started_at=started_at,      ended_at=ended_at,  )  print(f"Run created: {run.id}")if __name__ == '__main__':  main()

Custom CA certificates

When your instance uses a self-signed or internal CA cert, tell the Python SDK to trust it.

First, export the certificate from your server:

echo | openssl s_client -connect tofupilot.yourcompany.com:443 -showcerts 2>/dev/null | openssl x509 -outform PEM > ca-certificate.pem

Then pass it to the client.

from tofupilot.v2 import TofuPilotimport httpxhttp_client = httpx.Client(  verify="/path/to/ca-certificate.pem")client = TofuPilot(  api_key="your_api_key",  server_url="https://tofupilot.yourcompany.com/api",  client=http_client)

Rust SDK

The Rust SDK trusts the bundled Mozilla roots and the system certificate store, and accepts additional root certificates directly:

use tofupilot::{ClientConfig, TofuPilot};let config = ClientConfig::new("your_api_key")  .base_url("https://tofupilot.yourcompany.com")  .root_certificate_from_pem_file("/path/to/ca-certificate.pem")?;let client = TofuPilot::with_config(config);

add_root_certificate takes the PEM bytes, for a certificate that does not come from a file. It accepts a bundle, so a root and its intermediates can be passed together:

let config = ClientConfig::new("your_api_key")  .base_url("https://tofupilot.yourcompany.com")  .add_root_certificate(&pem_bytes)?;

CLI

Pass the certificate at login. It is saved with your credentials, so every later command and the station daemon reuse it without exporting anything into the service environment:

tofupilot login --url https://tofupilot.yourcompany.com \              --ca-cert /path/to/ca-certificate.pem

TOFUPILOT_CA_CERT=/path/to/ca-certificate.pem overrides the saved path for a single command. The certificate is added to the built-in roots rather than replacing them, so public hosts keep working; there is no option to disable certificate verification.

This covers every connection the CLI makes — login, deploy, pull, run uploads, the offline queue and the realtime link the station uses to stream live and show as online. No separate system-wide certificate install is needed. A CA already present in the machine's trust store also works for the realtime link, with or without this flag.

OpenHTF output callback

test.add_output_callbacks(upload(  url="https://tofupilot.yourcompany.com",  verify="/path/to/ca-certificate.pem",))

Other clients

The C++ and MATLAB SDKs do not expose a certificate option. Point the process at your CA with the standard OpenSSL environment variable instead:

export SSL_CERT_FILE=/path/to/ca-certificate.pem

How is this guide?

On this page