Skip to content

Get started with Docker Compose

The repository ships a Compose stack under deploy/compose/: a hardened single-node deployment (embedded SQLite, zero external dependencies) plus two overlays — a Postgres multi-tenant override and a disaster-recovery backup profile. This tutorial takes you from up -d to a configured control plane with backups.

The container wraps the same single binary as every other install; only the packaging differs. The commands below are the stack’s own documented entry points (deploy/compose/*.yml), and the first-run flow they drive is the same one verified end-to-end against the binary in the single-node tutorial.

1. Bring up the single-node stack (SQLite)

Section titled “1. Bring up the single-node stack (SQLite)”
  1. Start it:

    On hosts that mediate user namespace creation with AppArmor, complete “Session confinement on AppArmor hosts” in deploy/compose/README.md on the Docker daemon host before starting the stack.

    Terminal window
    docker compose -f deploy/compose/docker-compose.yml up --wait --wait-timeout 120

    What you get, by design:

    Property Value
    Ports 8443 (HTTPS: REST + web UI), 8444 (gRPC) — published on every host interface, because this is a server. Set OLIVARES_BIND=127.0.0.1 in .env to restrict them to the host
    Data named volume olivares-data → /var/lib/olivares (SQLite store, audit signing key, TLS material)
    Project folder at /project, read-write: the host folder whose absolute path is in OLIVARES_PROJECT_DIR, or an empty volume when it is unset. Sessions started in /project can change everything in it; see “Work on a host project folder” in deploy/compose/README.md
    Hardening runs as 65532:65532, read-only root, all capabilities dropped, no-new-privileges, tmpfs /tmp
    Healthcheck direct exec of the built-in olivares readyz command—no shell, curl, or wget. Only local /readyz HTTP 200 is healthy
  2. Ask the engine where it is, and read the one-time setup token from the logs:

    Terminal window
    # where the console answers, and whether setup is still pending. It runs the binary
    # directly and needs no shell.
    docker compose -f deploy/compose/docker-compose.yml exec olivares olivares first-boot
    # the one-time setup token, printed once at first boot
    docker compose -f deploy/compose/docker-compose.yml logs olivares \
    | sed -n '/FIRST-BOOT SETUP/,/========================/p'
  3. Create the first administrator, log in, and create your organization — exactly as in the single-node tutorial (the API is the same; the TLS certificate is self-signed on first boot, so curl -k for the bootstrap):

    Terminal window
    curl -ksf -X POST https://localhost:8443/v1/setup \
    -H 'Content-Type: application/json' \
    -d '{"token":"<olst_ token>","email":"you@example.com","password":"<strong-password>"}'

Sources are declared in one operator file named by OLIVARES_SOURCES_CONFIG (connect a source). For the container, mount the file and set the variable with a small override:

# deploy/compose/docker-compose.sources.yml (yours — not shipped)
services:
olivares:
environment:
OLIVARES_SOURCES_CONFIG: /etc/olivares/sources.json
volumes:
- ./sources.json:/etc/olivares/sources.json:ro
Terminal window
docker compose -f deploy/compose/docker-compose.yml \
-f deploy/compose/docker-compose.sources.yml up -d
docker compose -f deploy/compose/docker-compose.yml logs olivares | grep "ingest: wired source"

Remember the paths inside sources.json are container paths — a pgAudit log tail, for example, needs the Postgres log directory mounted read-only into the container as well.

3. Encrypted DR backups (the backup profile)

Section titled “3. Encrypted DR backups (the backup profile)”

The stack ships a one-shot backup service that produces an encrypted, ledger-continuity-safe bundle (olivares dr backup):

Keep the passphrase in a private file outside the checkout and the image, and keep a copy somewhere safe off this host: without it, no bundle can be restored. Give the backup container read-only access to it (the image runs as UID 65532):

Terminal window
sudo install -d -o 65532 -g 65532 -m 0700 /srv/olivares-dr
sudo install -o 65532 -g 65532 -m 0400 /path/to/private-passphrase /srv/olivares-dr/dr-pass
BACKUP_TS="$(date -u +%Y%m%dT%H%M%SZ)" \
docker compose -f deploy/compose/docker-compose.yml \
-f deploy/compose/docker-compose.backup.yml \
-f - --profile backup run --rm backup <<'YAML'
services:
backup:
volumes:
- /srv/olivares-dr/dr-pass:/run/secrets/dr-pass:ro
YAML

Wrap that command in host cron for a scheduled RPO, and mirror the olivares-backups volume offsite — a same-host backup is not disaster recovery, and the passphrase must travel separately from the bundles (3-2-1). The full procedure, including the restore drill (olivares dr verify), is in backup & restore.

4. Optional: the Postgres multi-tenant override

Section titled “4. Optional: the Postgres multi-tenant override”

For the multi-tenant topology, layer the Postgres override. It brings up a Postgres 16, provisions the least-privilege olivares_app role (no superuser, no BYPASSRLS — the engine refuses to start against a privileged role, because row-level security is the tenant backstop), and points the engine at it:

The override also provisions olivares_admin, a separate NOSUPERUSER BYPASSRLS role with read-only access to the engine tables. The engine uses it through --admin-dsn for cross-tenant reads, including first setup.

Set three distinct passwords first, using only A-Z a-z 0-9 . _ ~ - for this Compose demo, then bring the stack up:

Terminal window
cp deploy/compose/.env.example deploy/compose/.env
# set three distinct passwords in deploy/compose/.env:
# POSTGRES_SUPERUSER_PASSWORD, OLIVARES_DB_PASSWORD, OLIVARES_ADMIN_PASSWORD
docker compose -f deploy/compose/docker-compose.yml \
-f deploy/compose/docker-compose.postgres.yml up -d
Terminal window
curl -ks https://localhost:8443/readyz
# {"leader":true,"setup_required":false,"status":"ok","store":"up"}
curl -ks https://localhost:8443/metrics | grep olivares_store_up