Deploying the server
The server is one process that serves the API and the web UI on a single port. It needs somewhere to keep its data and, once you want it to be more than a laptop toy, a PostgreSQL database.
Docker Compose
Section titled “Docker Compose”From a checkout of the repository:
docker compose -f docker/docker-compose.yml up -dServer and web UI on port 3789, no external database, no config file. Data
persists in a horsie-data Docker volume.
Then open http://localhost:3789, sign in, and add a provider and a model — see Models & providers. A fresh server has none, and no session can run a turn without one.
Render
Section titled “Render”The button runs the published image from render.yaml with a managed Render
PostgreSQL database wired in as HORSIE_DATABASE_URL. No volume is needed —
both the settings store and the session journal live in that database.
To use a different PostgreSQL instance — Neon, Supabase, RDS, anything
speaking postgres:// — edit HORSIE_DATABASE_URL on the service after
deploy, or fork render.yaml and drop its databases: block. Any connection
string works as-is, including one with ?sslmode=require.
Fly.io
Section titled “Fly.io”Fly has no one-click button, so this is a handful of commands from a checkout:
fly launch --no-deploy # reads fly.toml, creates the appfly postgres createfly postgres attach --app <app-name> --variable-name HORSIE_DATABASE_URLfly deployfly launch assigns its own app name — the app value in fly.toml is a
placeholder it overwrites. --variable-name HORSIE_DATABASE_URL sets the
secret under the name horsie already reads, so nothing needs renaming
afterwards.
For an external database, skip the two fly postgres commands and set the
string directly:
fly secrets set HORSIE_DATABASE_URL=postgres://user:password@host/horsieWhich image tag
Section titled “Which image tag”All three paths above run ghcr.io/blossomstack/horsie:latest, which moves
with the default branch.
Every build also publishes an immutable sha-<short> tag, and a release
publishes <version> and v<version>. Pin to one of those if you want an
upgrade to be a deliberate step rather than a docker compose pull or a
redeploy.
Building it yourself
Section titled “Building it yourself”docker build -f docker/horsie.Dockerfile --target server -t horsie-server:latest .Or, with a recent Rust toolchain:
make build-server # ./target/release/horsie-servermake install-server # optional, into ~/.local/binRun the binary directly with:
horsie-server --addr 0.0.0.0:3789 --web clients/web/dist--web is what makes one process serve the UI as well as the API. Without it
the binary serves the API only.
PostgreSQL
Section titled “PostgreSQL”The default is SQLite in the server’s data directory, and it needs no
configuration at all. Point database.url at PostgreSQL when you would rather
the database were the thing that gets backed up than a container’s volume:
{ "database": { "url": "postgres://user:password@host/horsie" }}HORSIE_DATABASE_URL sets the same thing and takes precedence. Migrations run
at startup on either backend, and max_connections — 10 by default — sizes the
pool that settings reads and journal writes share.
Session and agent history lives in an actor journal: the journal_* tables in
the same database. There is nothing to configure and nothing separate to back
up.
What to back up
Section titled “What to back up”data_dir holds plugin artifacts and, with the default SQLite database,
the settings database and the journal under <data_dir>/server/. This is the
one to back up, and the one to mount a volume at in a container. A PostgreSQL
deployment keeps only plugin artifacts here.
state_dir is ephemeral. Losing it across a restart is fine.
Every field and its default is in the Configuration reference.
- Authentication & accounts — the generated first password, and the three auth modes.
- The local runtime and Cloud runtime vendors — sessions cannot run tools until one exists.