Skip to content

sqi-worker Deployment Guide

This document covers installing and running sqi-worker on bare metal (Linux, macOS, Windows) and in Docker, including how to configure auto-start on boot in each environment.


Installation

Pre-built binaries

Download the latest release archive from the GitHub Releases page. One archive per platform contains both sqi-server and sqi-worker. Archives are named sqi_<OS>_<arch> — for example sqi_Linux_x86_64.tar.gz, sqi_Linux_arm64.tar.gz, sqi_Darwin_arm64.tar.gz, sqi_Windows_x86_64.zip.

# Linux (x86_64)
curl -Lo sqi.tar.gz https://github.com/uberware/sqi/releases/latest/download/sqi_Linux_x86_64.tar.gz
tar -xzf sqi.tar.gz sqi-worker
chmod +x sqi-worker
sudo mv sqi-worker /usr/local/bin/

Verify the download against the published checksums:

curl -Lo checksums.txt https://github.com/uberware/sqi/releases/latest/download/checksums.txt
sha256sum --check --ignore-missing checksums.txt

On macOS, extract the matching sqi_Darwin_<arch> archive. The binaries are signed with an Apple Developer ID and notarized, so Gatekeeper allows them to run without the quarantine workaround.

Build from source

Requirements: Go 1.26.3 or later (the go directive in go.mod pins the version).

git clone https://github.com/uberware/sqi.git
cd sqi
make build
# Binary is at ./bin/sqi-worker

Configuration

Before running the worker, create a configuration file. The recommended location for a system-wide installation is /etc/sqi/sqi-worker.yaml.

Copy the annotated example and edit it:

sudo mkdir -p /etc/sqi
sudo cp config/sqi-worker.example.yaml /etc/sqi/sqi-worker.yaml
sudo $EDITOR /etc/sqi/sqi-worker.yaml

Minimum settings (if mDNS auto-discovery is not available):

nats:
  url: "nats://sqi-server.example.com:4222"

discovery:
  enable_mdns: false

worker:
  data_dir: "/var/lib/sqi-worker"

A worker executes as many tasks concurrently as it has CPU cores; the server gates capacity by CPU-core fit, so there is no per-worker concurrency knob to set. On a multi-site farm you may also want to declare where this worker runs so the server can resolve storage paths and honour location affinity:

worker:
  compute_location: "onprem"      # matches a storage-location root name
  capability_tags: ["maya-2025", "arnold-7"]
  # queue_ids: ["gpu-renders"]    # restrict to specific queues (optional)

See Compute locations and Storage locations for how these names are used.

Validate before deploying:

sqi-worker start --dry-run --config /etc/sqi/sqi-worker.yaml

See docs/worker-configuration.md for every available option.


Linux — systemd

1. Create a dedicated user

sudo useradd --system --no-create-home --shell /bin/false sqiworker
sudo mkdir -p /var/lib/sqi-worker
sudo chown sqiworker:sqiworker /var/lib/sqi-worker

2. Install the systemd unit file

Ready-to-use unit files ship in the repository at deploy/systemd/ (sqi-worker.service and the sqi-worker@.service template). Copy one into place, or create /etc/systemd/system/sqi-worker.service from the following:

[Unit]
Description=sqi distributed task worker
Documentation=https://github.com/uberware/sqi
After=network-online.target
Wants=network-online.target
# Optional: wait for sqi-server if it is on the same host
# After=sqi-server.service

[Service]
Type=simple
User=sqiworker
Group=sqiworker
ExecStart=/usr/local/bin/sqi-worker start --config /etc/sqi/sqi-worker.yaml
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sqi-worker

# Worker ID and session working directories
StateDirectory=sqi-worker
StateDirectoryMode=0750

# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/sqi-worker

[Install]
WantedBy=multi-user.target

Note: If ProtectSystem=strict prevents the worker from executing render tools, replace it with ProtectSystem=full or remove it. Render workers often need read access to shared network mounts.

3. Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now sqi-worker

Check status:

sudo systemctl status sqi-worker
sudo journalctl -u sqi-worker -f

4. Upgrade

Replace the binary and restart:

sudo systemctl stop sqi-worker
sudo cp new-sqi-worker /usr/local/bin/sqi-worker
sudo systemctl start sqi-worker

macOS — launchd

1. Create a configuration file

mkdir -p ~/.sqi
cp config/sqi-worker.example.yaml ~/.sqi/sqi-worker.yaml
$EDITOR ~/.sqi/sqi-worker.yaml

Set worker.data_dir to ~/Library/Application Support/sqi-worker (or another persistent path).

2. Install the launchd plist

Create ~/Library/LaunchAgents/net.uberware.sqi-worker.plist for a per-user agent (runs when the user is logged in):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>net.uberware.sqi-worker</string>

  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/sqi-worker</string>
    <string>start</string>
    <string>--config</string>
    <string>/Users/YOURUSERNAME/.sqi/sqi-worker.yaml</string>
  </array>

  <key>RunAtLoad</key>
  <true/>

  <key>KeepAlive</key>
  <dict>
    <key>SuccessfulExit</key>
    <false/>
  </dict>

  <key>StandardOutPath</key>
  <string>/tmp/sqi-worker.log</string>

  <key>StandardErrorPath</key>
  <string>/tmp/sqi-worker.log</string>

  <key>EnvironmentVariables</key>
  <dict>
    <key>PATH</key>
    <string>/usr/local/bin:/usr/bin:/bin</string>
  </dict>
</dict>
</plist>

Replace YOURUSERNAME with your actual macOS username.

For a system-wide daemon (runs without a user logged in), place the plist at /Library/LaunchDaemons/net.uberware.sqi-worker.plist and run it as a dedicated service account. Update UserName and paths accordingly.

3. Load and start

launchctl load ~/Library/LaunchAgents/net.uberware.sqi-worker.plist
launchctl start net.uberware.sqi-worker

Check whether it is running:

launchctl list | grep sqi-worker

View logs:

tail -f /tmp/sqi-worker.log

4. Stop and unload

launchctl stop net.uberware.sqi-worker
launchctl unload ~/Library/LaunchAgents/net.uberware.sqi-worker.plist

Windows — Windows Service

1. Install the binary

Copy sqi-worker.exe to a permanent location, e.g.:

C:\Program Files\sqi\sqi-worker.exe

2. Create a configuration file

C:\ProgramData\sqi\sqi-worker.yaml

At minimum:

nats:
  url: "nats://sqi-server.example.com:4222"
discovery:
  enable_mdns: false
worker:
  data_dir: "C:\\ProgramData\\sqi\\worker"

3. Register as a Windows service

Open a PowerShell prompt as Administrator:

New-Service `
  -Name "sqi-worker" `
  -DisplayName "sqi Worker Agent" `
  -Description "sqi distributed task worker" `
  -BinaryPathName '"C:\Program Files\sqi\sqi-worker.exe" start --config "C:\ProgramData\sqi\sqi-worker.yaml"' `
  -StartupType Automatic

Start-Service sqi-worker

Check status:

Get-Service sqi-worker

View the Windows Event Log for output (the worker writes JSON to stderr, which Windows services route to the Event Log when StandardOutput is not redirected):

Get-EventLog -LogName Application -Source sqi-worker -Newest 50

4. Stop and remove the service

Stop-Service sqi-worker
Remove-Service sqi-worker

5. Auto-start on boot

The service is created with StartupType Automatic, so Windows starts it on every boot without additional configuration.

To delay start until after network services are ready:

Set-Service sqi-worker -StartupType AutomaticDelayedStart

Docker

See docs/worker-docker.md for the full Docker deployment guide including image details, required environment variables, volume mounts, and network requirements.

Quick start:

docker run -d \
  --name sqi-worker \
  -e SQI_WORKER_NATS_URL=nats://sqi-server:4222 \
  -e SQI_WORKER_DISCOVERY_ENABLE_MDNS=false \
  -e SQI_WORKER_DATA_DIR=/var/lib/sqi-worker \
  -v sqi-worker-data:/var/lib/sqi-worker \
  ghcr.io/uberware/sqi/sqi-worker:latest

Multiple workers on one host

A single worker already executes as many tasks concurrently as the host has CPU cores — the server gates capacity by CPU-core fit — so one worker per host is usually enough for throughput. Run separate worker processes only when you want distinct identities: independent heartbeats and registrations, different capability sets or compute locations, or separate queue assignments.

Each instance must have its own worker.data_dir (it holds the persistent worker.id UUID) and its own metrics.addr (the local health/metrics port), and should have a distinct worker.name. See Running multiple workers on one host for the full rationale.

On Linux the idiomatic approach is a systemd template unit, where the instance identifier (%i) carries the per-instance metrics port. Create /etc/systemd/system/sqi-worker@.service:

[Unit]
Description=sqi distributed task worker (instance %i)
Documentation=https://github.com/uberware/sqi
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=sqiworker
Group=sqiworker
ExecStart=/usr/local/bin/sqi-worker start --config /etc/sqi/sqi-worker.yaml
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=sqi-worker-%i

# Per-instance identity (%i is the metrics port, e.g. 9091)
Environment=SQI_WORKER_NAME=%H-worker-%i
Environment=SQI_WORKER_DATA_DIR=/var/lib/sqi-worker-%i
Environment=SQI_WORKER_METRICS_ADDR=127.0.0.1:%i

# Per-instance state directory: /var/lib/sqi-worker-<port>
StateDirectory=sqi-worker-%i
StateDirectoryMode=0750

# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/sqi-worker-%i

[Install]
WantedBy=multi-user.target

Enable one instance per port:

sudo systemctl daemon-reload
sudo systemctl enable --now sqi-worker@9091 sqi-worker@9092 sqi-worker@9093

The shared /etc/sqi/sqi-worker.yaml holds everything common (NATS URL, capability tags, queues); the template's Environment= lines override only the three settings that must differ per instance. Manage each with the usual systemctl status sqi-worker@9091 / journalctl -u sqi-worker@9091.

macOS / Windows: the same rule applies — give each launchd plist or Windows service a unique Label/service name, SQI_WORKER_DATA_DIR, and SQI_WORKER_METRICS_ADDR.

Docker: containers are already filesystem-isolated, so just run multiple containers with distinct --name values and separate data volumes — see docs/worker-docker.md.


Verifying the deployment

After starting the worker, confirm it registered with the server:

# Via REST API
curl -s http://sqi-server:8080/api/v1/workers | jq '.[].name'

# Via sqi-worker health probe (from the worker host)
curl -sf http://127.0.0.1:9091/healthz && echo healthy
curl -sf http://127.0.0.1:9091/readyz  && echo ready

The web UI at http://sqi-server:8080Workers shows registered workers with their capability tags and live task counts.


Rolling restarts

sqi-worker handles SIGTERM gracefully: it stops accepting new assignments and waits up to worker.shutdown_grace_period (default 30 s) for in-flight tasks to complete. systemd sends SIGTERM before SIGKILL, so the default TimeoutStopSec=90s in systemd is sufficient for most render workloads.

For long-running renders, increase shutdown_grace_period to match your longest expected task duration and set TimeoutStopSec in the unit file accordingly.


See also