Deploy the binary (systemd)
Busbar is a single binary with no service dependencies, so the VM / bare-metal deployment is: drop the binary on the host, give it config and secrets, and run it under systemd.
One thing to check before you start: the published Linux builds are glibc, not musl. Debian, Ubuntu, RHEL, Amazon Linux and the rest are fine. Alpine and other musl distributions are not, and there is no musl artifact yet. The installer detects this and refuses rather than leaving you a binary that fails with a misleading not found; run the container image on those hosts instead.
1. Download and verify
Section titled “1. Download and verify”The one-line installer detects your platform and downloads the binary and the provider catalog:
curl -fsSL https://getbusbar.com/install.sh | sudo env BUSBAR_INSTALL_DIR=/usr/local/bin shBUSBAR_INSTALL_DIR has to be set for the shell that runs the script, which is the one on the right of the pipe. Writing it on the left (BUSBAR_INSTALL_DIR=... curl ... | sh) sets it for curl instead, where nothing reads it, and the install lands silently in your current directory. Drop the sudo env if you are already root, or omit the variable entirely to install into the working directory with no privileges at all.
Or fetch a specific release tarball manually and verify its build provenance before trusting it. Pick the asset name for your platform. Releases publish busbar-x86_64-unknown-linux-gnu.tar.gz and busbar-aarch64-unknown-linux-gnu.tar.gz for Linux, and busbar-x86_64-apple-darwin.tar.gz / busbar-aarch64-apple-darwin.tar.gz for macOS (Intel / Apple Silicon):
ver=1.5.3asset=busbar-x86_64-unknown-linux-gnu.tar.gz # swap for your platform, see abovebase="https://github.com/GetBusbar/busbar/releases/download/v${ver}"curl -fsSLO "${base}/${asset}"
# Verify the artifact was built by the busbar release workflow (Sigstore/OIDC).gh attestation verify "$asset" --repo GetBusbar/busbar
tar -xzf "$asset"sudo install -m 0755 busbar /usr/local/bin/busbarbusbar --version2. Place config and the provider catalog
Section titled “2. Place config and the provider catalog”sudo useradd --system --no-create-home --shell /usr/sbin/nologin busbarsudo mkdir -p /etc/busbar /var/lib/busbarsudo curl -fsSL https://getbusbar.com/providers.yaml -o /etc/busbar/providers.yamlsudo chown -R busbar:busbar /var/lib/busbarWrite /etc/busbar/config.yaml (see Configuration and Getting Started). A minimal one to start:
# /etc/busbar/config.yaml (dev/minimal, no client auth gate)providers: anthropic: api_key: { env: ANTHROPIC_KEY }models: claude-sonnet: provider: anthropicTo require caller auth instead (recommended before exposing Busbar beyond localhost), add an auth block with the keys verifier:
identity-providers: # DEFINE the provider once; `auth.chain` / `auth.admin_auth` reference it BY BARE NAME. admin-tokens: { module: admin-tokens, token: { env: BUSBAR_ADMIN_TOKEN } }
auth: chain: [keys] signing_key: { env: BUSBAR_SIGNING_KEY } admin_auth: [admin-tokens]auth.signing_key is a required, explicit secret reference. Busbar 1.5.3 does not auto-generate one; boot fails without it. Generate it once and keep the same value across every restart and every node in a fleet (rotating it revokes every key minted under the old one):
busbar --generate-signing-keyPut the printed 64-hex-char value in /etc/busbar/busbar.env as BUSBAR_SIGNING_KEY (below). Reference secrets in config.yaml as { env: VAR } / { file: /path }, never inline keys.
3. Secrets in an environment file
Section titled “3. Secrets in an environment file”Keep provider keys and tokens out of the config, in a root-owned, 0600 env file:
# /etc/busbar/busbar.env (chmod 600, owned by root)ANTHROPIC_KEY=sk-ant-...BUSBAR_ADMIN_TOKEN=a-long-random-string # guards the admin API (auth.admin_auth: admin-tokens)BUSBAR_SIGNING_KEY=<64-hex-char output of `busbar --generate-signing-key`> # only if auth.chain includes `keys`4. The systemd unit
Section titled “4. The systemd unit”/etc/systemd/system/busbar.service, runs as a non-root user with the rootfs read-only and privileges dropped:
[Unit]Description=Busbar LLM gatewayAfter=network-online.targetWants=network-online.target
[Service]Type=simpleUser=busbarGroup=busbarEnvironmentFile=/etc/busbar/busbar.envEnvironment=BUSBAR_CONFIG=/etc/busbar/config.yamlEnvironment=BUSBAR_PROVIDERS=/etc/busbar/providers.yaml# Fail fast: don't (re)start on an invalid config.ExecStartPre=/usr/local/bin/busbar --validateExecStart=/usr/local/bin/busbarRestart=alwaysRestartSec=2
# HardeningNoNewPrivileges=trueProtectSystem=strictProtectHome=truePrivateTmp=trueReadWritePaths=/var/lib/busbarCapabilityBoundingSet=AmbientCapabilities=
[Install]WantedBy=multi-user.targetsudo systemctl daemon-reloadsudo systemctl enable --now busbarcurl -s http://localhost:8080/healthz && echo okIf you enabled the keys auth verifier in step 2, mint a caller key; 127.0.0.1:8081 is directly reachable from the same host (unlike the Docker deploy, there’s no container network namespace in the way):
source /etc/busbar/busbar.envcurl -s -X POST http://127.0.0.1:8081/api/v1/admin/keys \ -H "Authorization: Bearer $BUSBAR_ADMIN_TOKEN" -H "Content-Type: application/json" \ -d '{"name":"quickstart"}'The response’s .token field is the signed key, shown once.
5. Admin plane, TLS, upgrades
Section titled “5. Admin plane, TLS, upgrades”-
Admin plane. The admin API (
/api/v1/admin) runs on its own listener, loopback (127.0.0.1:8081) by default, reachable from the host, not the network. To manage it remotely, setadmin_listento an exposed address and configureadmin_tls(mTLS); it refuses to boot otherwise. See Operations and the Admin API. -
Inbound TLS. Terminate TLS at Busbar (
tlsblock) or in front of it. See Operations → Inbound TLS. -
Upgrade / reload. Validate first, then restart (or reload live via the admin API):
Terminal window busbar --validate && sudo systemctl restart busbar
Multiple instances (HA)
Section titled “Multiple instances (HA)”Busbar is stateless, so for high availability run N hosts behind a load balancer
(nginx, HAProxy, or a cloud L4/L7 LB), each with the same config and each health-checked
on GET /healthz. Any host serves any request. If you rely on session affinity, enable
sticky sessions at the LB; and note that with a shared store, budgets accrue additively to
the shared store (fleet-correct), while only the requests/tokens rate windows are per-node.
For strict global rate limiting, run one instance per store or front the fleet with an upstream
rate limiter. See Running multiple instances (HA).