Skip to content

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.

The one-line installer detects your platform and downloads the binary and the provider catalog:

Terminal window
curl -fsSL https://getbusbar.com/install.sh | sudo env BUSBAR_INSTALL_DIR=/usr/local/bin sh

BUSBAR_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):

Terminal window
ver=1.5.3
asset=busbar-x86_64-unknown-linux-gnu.tar.gz # swap for your platform, see above
base="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/busbar
busbar --version
Terminal window
sudo useradd --system --no-create-home --shell /usr/sbin/nologin busbar
sudo mkdir -p /etc/busbar /var/lib/busbar
sudo curl -fsSL https://getbusbar.com/providers.yaml -o /etc/busbar/providers.yaml
sudo chown -R busbar:busbar /var/lib/busbar

Write /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: anthropic

To 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):

Terminal window
busbar --generate-signing-key

Put 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.

Keep provider keys and tokens out of the config, in a root-owned, 0600 env file:

Terminal window
# /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`

/etc/systemd/system/busbar.service, runs as a non-root user with the rootfs read-only and privileges dropped:

[Unit]
Description=Busbar LLM gateway
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=busbar
Group=busbar
EnvironmentFile=/etc/busbar/busbar.env
Environment=BUSBAR_CONFIG=/etc/busbar/config.yaml
Environment=BUSBAR_PROVIDERS=/etc/busbar/providers.yaml
# Fail fast: don't (re)start on an invalid config.
ExecStartPre=/usr/local/bin/busbar --validate
ExecStart=/usr/local/bin/busbar
Restart=always
RestartSec=2
# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/busbar
CapabilityBoundingSet=
AmbientCapabilities=
[Install]
WantedBy=multi-user.target
Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now busbar
curl -s http://localhost:8080/healthz && echo ok

If 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):

Terminal window
source /etc/busbar/busbar.env
curl -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.

  • 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, set admin_listen to an exposed address and configure admin_tls (mTLS); it refuses to boot otherwise. See Operations and the Admin API.

  • Inbound TLS. Terminate TLS at Busbar (tls block) 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

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).