Run it on a server

This page sets up the Orchestrator on a Linux server as a systemd service, with certificates from Let's Encrypt. The examples use workspaces.example.com. Replace it with your own domain. To run the Orchestrator in Docker instead, see Run it in Docker.

1. Prepare the server

  • DNS: point two records at the server's public IP address (an A record, plus AAAA for IPv6):
    • workspaces.example.com, for the Orchestrator itself;
    • *.workspaces.example.com, so that every workspace also has its own subdomain (acme.workspaces.example.com).
  • Firewall: open ports 80 and 443 to the internet. Let's Encrypt connects to them to check that you control the names, and nothing else on the server may use them.
  • Docker: install Docker Engine and make sure it starts at boot.

2. Install

Create a user for the service. It needs to be in the docker group to manage the workspaces' containers, and its home folder holds the Orchestrator's data. The data folder is opened to the curiosity group, so administrators in it can manage the data.

sudo useradd --system --user-group --create-home --home-dir /var/lib/curiosity-orchestrator --groups docker curiosity
sudo chmod 770 /var/lib/curiosity-orchestrator

Install the program into /opt/curiosity:

curl -fsSL https://raw.githubusercontent.com/curiosity-ai/orchestrator/refs/heads/main/install.sh \
  | sudo env ORC_INSTALL=/opt/curiosity ORC_NO_MODIFY_PATH=1 sh

3. Configure

Put the settings in /etc/curiosity-orchestrator.env, readable by root only, since it holds the console password:

sudo install -m 600 /dev/null /etc/curiosity-orchestrator.env
sudo nano /etc/curiosity-orchestrator.env
ORC_ADMIN_PASSWORD=choose-a-long-password
ORC_STORAGE=/var/lib/curiosity-orchestrator
ORC_PUBLIC_ADDRESS=https://workspaces.example.com

ORC_LETSENCRYPT=true
ORC_LETSENCRYPT_EMAIL=ops@example.com
ORC_LETSENCRYPT_ACCEPT_TERMS=true
# Start with Let's Encrypt's staging server; remove this line once everything works (step 5).
ORC_LETSENCRYPT_STAGING=true

ORC_LETSENCRYPT_ACCEPT_TERMS=true accepts the Let's Encrypt subscriber agreement. The e-mail address gets expiry warnings if renewal ever fails. Every other setting is in Settings. Put it in the same file.

4. Start the service

Create /etc/systemd/system/curiosity-orchestrator.service:

[Unit]
Description=Curiosity Orchestrator
Wants=network-online.target docker.service
After=network-online.target docker.service

[Service]
User=curiosity
Group=curiosity
SupplementaryGroups=docker
# New data files stay readable and writable by the curiosity group, like the folder itself.
UMask=0007
EnvironmentFile=/etc/curiosity-orchestrator.env
WorkingDirectory=/var/lib/curiosity-orchestrator
ExecStart=/opt/curiosity/orchestrator/curiosity-orchestrator
# Lets the service listen on ports 80 and 443 without running as root.
AmbientCapabilities=CAP_NET_BIND_SERVICE
Restart=on-failure
RestartSec=10
TimeoutStopSec=60

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now curiosity-orchestrator
journalctl -u curiosity-orchestrator -f

The Orchestrator now listens on 443 (HTTPS) and 80, which answers Let's Encrypt and redirects everything else to HTTPS. Within a minute the log should show the certificate for workspaces.example.com being issued.

5. Check it, then switch to real certificates

1

Sign in

Open https://workspaces.example.com/#/manage and sign in as admin. With the staging server your browser warns that the certificate is not trusted. That is expected: staging certificates are for testing only.

2

Check the certificates

Open Settings. Every host name should show as valid. A name that failed, for example because DNS does not point at the server yet or a port is closed, is retried every 15 minutes. The log says why it failed.

3

Create a workspace

Create a workspace and open it at https://acme.workspaces.example.com/. Its certificate is issued within about 10 minutes of creating it.

4

Switch to real certificates

Remove the ORC_LETSENCRYPT_STAGING=true line from /etc/curiosity-orchestrator.env, then delete the staging certificates, which would otherwise be kept until they are due for renewal:

sudo systemctl stop curiosity-orchestrator
sudo rm -rf /var/lib/curiosity-orchestrator/letsencrypt
sudo systemctl start curiosity-orchestrator

Starting with staging is how you find a DNS or firewall mistake without spending rate limit.

Upgrade

Run the install command again, then restart the service:

curl -fsSL https://raw.githubusercontent.com/curiosity-ai/orchestrator/refs/heads/main/install.sh \
  | sudo env ORC_INSTALL=/opt/curiosity ORC_NO_MODIFY_PATH=1 sh
sudo systemctl restart curiosity-orchestrator

Restarting the Orchestrator does not stop the workspaces, and certificates are kept in /var/lib/curiosity-orchestrator/letsencrypt, so nothing is re-issued.

Variations

  • Your own certificate instead of Let's Encrypt: leave out the ORC_LETSENCRYPT* lines and set ORC_CERT_FILE (a PFX, or a PEM with ORC_CERT_FILE_PRIVATE_KEY), plus ORC_PORT_HTTP=80 to redirect http:// to HTTPS. It must cover *.workspaces.example.com too for the workspaces' subdomains.
  • A separate host name for the console, recommended when workspace administrators are not fully trusted: add ORC_MANAGEMENT_HOST=manage.example.com and a DNS record for it. See Security.
  • No subdomains: with ORC_SUBDOMAIN_ROUTING=false, workspaces are served only at https://workspaces.example.com/<slug>/. No wildcard DNS record is needed, and only one certificate is issued.
  • Without systemd: the program needs permission to listen on ports below 1024. Either run it as root, or grant the permission with sudo setcap cap_net_bind_service=+ep /opt/curiosity/orchestrator/curiosity-orchestrator. An upgrade replaces that file, so run setcap again after each one.