Deployment

Arona ships as a single self-contained Rust binary built from the _cli crate. The release workflow publishes it as arona-server-<tag>-linux-x86_64 (.github/workflows/release.yml), and the same binary plays both roles:

The server requires PostgreSQL. The schema is created and migrated automatically on startup, so deployment is mostly "get the binary, point it at a database, run it". Start with the quickstart for an end-to-end walkthrough, then come back here for the production layout.

Requirements

Install

From a release asset

Download the asset for the tag you want and make it executable:

bash
1
2
3
4
export VERSION=v0.1.25   # or any released tag
curl -fsSL "https://github.com/celestia-island/arona/releases/download/${VERSION}/arona-server-${VERSION}-linux-x86_64" \
    -o /usr/local/bin/arona-server
chmod +x /usr/local/bin/arona-server

From the install script

The repository ships scripts/install.sh: it resolves the latest release tag from the GitHub API (or honors the ARONA_VERSION override), downloads the matching asset, installs it as arona-server in ~/.local/bin by default (override the directory with ARONA_BIN_DIR), and prints the next steps:

bash
1
2
3
curl -fsSL https://raw.githubusercontent.com/celestia-island/arona/master/scripts/install.sh | bash
# or pin a version and a directory:
ARONA_VERSION=v0.1.25 ARONA_BIN_DIR=/usr/local/bin ./scripts/install.sh

From source

bash
1
2
cargo build --release -p _cli
install -m 0755 target/release/_cli /usr/local/bin/arona-server

cargo build --release -p _cli is exactly what the release workflow and the Dockerfile build.

Configuration

All configuration is via environment variables; the configuration reference documents every variable, and .env.example at the repository root is a working starting point. The minimum set:

VariableRequired?Purpose
DATABASE_URLyesPostgreSQL connection string; the server exits if unset.
JWT_SECRETyesToken-signing secret; the server refuses to run with the built-in development secret unless MOCK_MODE=1.
ARONA_ADMIN_TOKENstrongly recommendedShared bearer token for /api/admin/* routes; without it those routes always return 401.

Optional: ARONA_HOST (default 0.0.0.0), ARONA_PORT (default 8420), ARONA_REGISTRATION_OPEN (truthy 1/true/yes/on — opens signup; the first registered user becomes admin), ARONA_DATA_DIR (model cache root), ARONA_MEMORY_URL / ARONA_MEMORY_TOKEN / ARONA_MEMORY_WRITEBACK (memory gateway), ARONA_API_RATE_LIMIT_RPM (per-key rate limit) and RUST_LOG (tracing filter).

Database migrations

serve connects to the database and applies all pending schema migrations on startup (init_databaseMigrator::up), so there is no separate deploy step. To apply migrations explicitly — for example to verify them before the first start — run:

bash
1
arona-server migrate

The database user needs CREATE privilege on the target schema, because the startup migration creates tables. There is no data migration beyond the schema: the database is the state, so back it up before upgrades (see Upgrade and backup).

Bare metal with systemd

Example unit file (/etc/systemd/system/arona.service). All secret values below are placeholders — replace CHANGE_ME before use:

ini
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[Unit]
Description=Arona API server
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=simple
User=arona
Environment=DATABASE_URL=postgres://arona:CHANGE_ME@127.0.0.1:5432/arona
Environment=JWT_SECRET=CHANGE_ME
Environment=ARONA_ADMIN_TOKEN=CHANGE_ME
Environment=ARONA_HOST=0.0.0.0
Environment=ARONA_PORT=8420
Environment=RUST_LOG=info
# Optional:
# Environment=ARONA_REGISTRATION_OPEN=1
# Environment=ARONA_MEMORY_URL=ws://127.0.0.1:8424/ws
# Environment=ARONA_MEMORY_TOKEN=CHANGE_ME
# Environment=ARONA_MEMORY_WRITEBACK=1
# Environment=ARONA_DATA_DIR=/var/lib/arona
ExecStart=/usr/local/bin/arona-server serve
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Then enable and start it:

bash
1
2
3
sudo systemctl daemon-reload
sudo systemctl enable --now arona
curl -fsS http://127.0.0.1:8420/readyz   # expect {"status":"ok",...}

Docker Compose

The repository root ships a docker-compose.yml with two services:

bash
1
2
3
4
5
6
cp .env.example .env
# Edit .env, e.g.:
#   DATABASE_URL=postgres://arona:CHANGE_ME@postgres:5432/arona
#   JWT_SECRET=CHANGE_ME
#   ARONA_ADMIN_TOKEN=CHANGE_ME
docker compose up -d

The bundled postgres service is reachable at host postgres inside the Compose network. The Dockerfile builds _cli and _agent in a rust:1.91-slim-bookworm builder, installs ca-certificates, curl and python3 in a debian:bookworm-slim runtime, copies both binaries in, exposes ports 8420 (server) and 5790 (the bundled _agent's local API), and runs arona-server serve as the entrypoint.

Supervised deployment with malkuth (workspace convention)

In this workspace arona runs under malkuth supervision as arona-malkuth.service. The pattern applies to any service deployed here:

Operational consequences:

Health probes

The server exposes two unauthenticated health families (both also covered in the operations guide):

Point load balancers, supervisors and container healthchecks at /readyz; use /api/health when you need uptime and network detail.

Upgrade and backup