Bare Metal / VM Deployment

What you'll accomplish: Install the Hoziron binaries on a server or VM, run them as a dedicated system user under systemd, provision and install a carrier licence, and verify the deployment.

Overview

For direct installs without containers, hoziron-server runs as a systemd service under a dedicated non-root user. TLS can terminate in-process ([surfaces.<name>.tls], native rustls — see TLS and networking § TLS termination) or, if you'd rather not manage certs on this host, at a reverse proxy (Caddy, nginx) in front of it, the same way the Docker Compose guide does with its optional caddy service.

1. Install the binaries

Follow Install via Binary to download and install hoziron-cli (CLI) and hoziron-server (unified server) to /usr/local/bin/. For a dedicated service host, install as root and run the service as its own user instead of your own account:

sudo cp hoziron-cli hoziron-server /usr/local/bin/

2. Create a system user and data directory

sudo useradd --system --home /opt/hoziron --shell /usr/sbin/nologin hoziron
sudo mkdir -p /opt/hoziron
sudo chown hoziron:hoziron /opt/hoziron

Initialize the config directory as that user:

sudo -u hoziron env HOZIRON_HOME=/opt/hoziron hoziron-cli init --quick

This creates config.toml, .env, and a data/ directory under /opt/hoziron (mirroring what hoziron-cli init does under ~/.hoziron in the getting-started guide).

3. Carrier licence

hoziron-server will not boot any surface without a signed carrier licence at $HOZIRON_HOME/licence.json. This is enforced in HozironPlatform::init with no bypass — not an env var, not a test flag, not a config toggle (see ADR-034, ADR-034a, ADR-034b, ADR-055).

Licence trust is bound to the carrier's contract (carrier_id), not to the machine that runs the server — the same licence.json is portable to any host running that carrier's instance.

Get the licence. Send your carrier ID and name to Hoziron. Hoziron issues a signed licence.json and sends it back to you — there is no self-service issuance step.

Install it on the target host:

sudo -u hoziron cp licence.json /opt/hoziron/licence.json
sudo -u hoziron chmod 0600 /opt/hoziron/licence.json

Keep the file owner-readable only (0600) — it embeds an encrypted provider API key if your licence provisions a Hoziron-managed cloud entitlement.

Verify it once the server is running:

hoziron-cli licence status

This reports the carrier, expiry, workflow budgets, and (if present) the cloud token budget and SoR targets.

4. Configure config.toml

At minimum, set the API surface listen address, an auth mode (the daemon refuses to boot with auth.mode = "disabled" on a non-loopback bind), and at least one provider:

[surfaces.api]
enabled = true
listen = "0.0.0.0:4200"
base_path = "/"

[auth]
mode = "local"   # or "oidc" — see Authentication setup

[[provider]]
id = "anthropic"
driver = "anthropic"
api_key_env = "ANTHROPIC_API_KEY"

  [[provider.model]]
  id = "claude-sonnet-4-20250514"

[routing_gateway]
cloud_preference = [{ provider = "anthropic", model = "claude-sonnet-4-20250514" }]

To terminate TLS in-process instead of at a reverse proxy, add [surfaces.api.tls] with enabled = true, cert_path, and key_path — this is real native TLS termination (axum-server + rustls, with automatic cert/key hot-reload), not a placeholder. See TLS and networking for the full schema and the mount/main-route rules if you're also running mcp/dashboard on the same listener.

5. Create a systemd service

sudo tee /etc/systemd/system/hoziron.service << 'EOF'
[Unit]
Description=Hoziron AI Agent Platform
After=network.target

[Service]
Type=simple
User=hoziron
Group=hoziron
Environment=HOZIRON_HOME=/opt/hoziron
Environment=HOZIRON_LOG=info
ExecStart=/usr/local/bin/hoziron-server --config /opt/hoziron/config.toml
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now hoziron

To run additional surfaces on the same host, either add --surfaces api,registry to ExecStart or set enabled = true under each [surfaces.*] table in config.toml — the CLI flag takes precedence over the config file when both are present.

6. Verify

sudo systemctl status hoziron
curl http://localhost:4200/__server/health

/__server/health is the unified, surface-agnostic health check the daemon mounts on every listener. The API surface also serves its own richer /health (agent/provider/memory status) — but only where the API surface is bound.

From the CLI, point a context at the running server and check licence status:

hoziron-cli context add local --url http://127.0.0.1:4200
hoziron-cli context use local
hoziron-cli licence status
hoziron-cli health

hoziron-cli licence status reports the carrier, expiry (with days remaining), workflow budgets, and — if the licence has one — the cloud token budget and SoR targets. Watch for the expiry warning: the validator flags licences with 7 or fewer days remaining.

Capacity planning

These are starting-point guidelines, not measured benchmarks — validate against your own agent count and provider latency:

WorkloadCPUMemoryStorage
Minimal (1–5 agents)2 cores2 GB1 GB
Standard (10–50 agents)4 cores4 GB10 GB
Production (50–200 agents)8 cores8 GB50 GB
Enterprise (200+ agents)16+ cores16+ GB100+ GB

Graceful shutdown

hoziron-server listens for SIGTERM, Ctrl+C, and an internal shutdown-notify signal (fired via the API). On any of them it stops accepting new connections on every listener and waits up to 30 seconds for in-flight requests to drain before forcing exit. systemd sends SIGTERM on systemctl stop — the default 90s unit timeout comfortably covers this.

Next steps


Related: