oxid/ docs

// install — 60 seconds

One command to a live preview

No .env to hand-wire. The shipped docker-compose.yml runs with OXID_AUTO_TOKEN=1 — secrets generate themselves, persist under /data, and the dashboard's onboarding wizard walks you through the rest in five clicks. Every other install path is still a single binary; Docker is just the fastest.

TL;DR  — copy, paste, open the wizard:
curl -fsSL https://raw.githubusercontent.com/sazardev/oxid/main/install.sh | sh -s -- --docker
# → open http://localhost:8080  → /ui/onboarding

Docker — zero-config (recommended)

Fastest path to a production-topology stack (daemon + Traefik, wake-on-request wired).

Option A — installer (one line)

curl -fsSL https://raw.githubusercontent.com/sazardev/oxid/main/install.sh | sh -s -- --docker
# verifies checksums of the released CLI, generates OXID_API_TOKEN + WEBHOOK_SECRET
# into ./oxid-stack/.env (0600, never rotated on re-run), pulls ghcr.io/sazardev/oxid,
# starts daemon + Traefik, waits for /health and verifies wiring.

# your stack lives in ./oxid-stack — upgrade with:
$ cd oxid-stack && docker compose pull && docker compose up -d

Re-running the installer is safe — existing secrets are reused, never rotated. Your token lives at ./oxid-stack/.env (grep ^OXID_API_TOKEN .env).

Option B — manual compose (no .env needed)

Thanks to OXID_AUTO_TOKEN=1 the shipped compose file works with nothing to fill in:

$ git clone https://github.com/sazardev/oxid && cd oxid
$ docker compose up -d
$ docker compose logs oxid-daemon | grep -A2 Generated  # your token, printed once
# → Dashboard: http://localhost:8080

Both tokens are generated (64 hex), persisted to /data/api-token and /data/webhook-secret (0600) inside the oxid-data volume, and never logged again. Retrieve later with docker compose cp oxid-daemon:/data/api-token - or simply re-read the volume. Explicit env values always win — pin your own by setting OXID_API_TOKEN / OXID_WEBHOOK_SECRET in a .env.

Option C — single docker run

docker run -d --name oxid-daemon \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v oxid-data:/data \
  -p 8080:8080 \
  -e OXID_API_TOKEN=$(openssl rand -hex 32) \
  -e OXID_WEBHOOK_SECRET=$(openssl rand -hex 32) \
  ghcr.io/sazardev/oxid:latest
# or: docker build -t oxid-daemon .  from source

See docker-compose.yml for the Traefik-wired reference stack. OXID_DOCKER_NETWORK=oxid-net is the supported production topology; without it, scale-to-zero is disabled by design and each env gets its own host port.

First 5 minutes — the wizard

First visit to http://localhost:8080 auto-redirects to /ui/onboarding. Five steps, everything also available later via CLI or API:

#StepWhat it doesBehind the scenes
1TokenPaste OXID_API_TOKEN. With OXID_AUTO_TOKEN the hint shows docker compose logs | grep Generated.GET /api/v1/stats verifies
2InfrastructureChecklist: Docker network, Traefik, self-wiring. One-click fix if anything is missing.GET /api/v1/infra/statusPOST /api/v1/infra/bootstrap (idempotent)
3First projectRegister by Git URL (https://… or scp-style git@host:org/repo.git; private repos take an encrypted PAT) or a path under mounted ./repos. Kicks off first deploy of main with live polling.POST /api/v1/projects {"repo_url":…}POST /api/v1/projects/{id}/deploy
4WebhooksPick provider, copy URL + auto-generated secret into your Git host. Every push deploys its branch from then on.GET /api/v1/setup/webhook-secret (master only)
5CLICopy-paste oxid context add prod --api … --token … and a curl snippet for programmatic registration.

Finish → /ui/environments with your first branch running. Re-run anytime from Setup in the top bar or /ui/onboarding; dismissing sets localStorage oxid_onboarded.

Direct-publish mode (no OXID_DOCKER_NETWORK) shows "nothing to bootstrap" at step 2 — a valid topology where each env just gets its own host port. No DNS needed.

CLI alternative — no browser needed

The same flow from the terminal, against any daemon (local or remote). The daemon clones the repo itself when you pass a URL — no shared filesystem.

# point the CLI at the daemon (once):
oxid context add prod --api http://localhost:8080 --token $OXID_API_TOKEN
oxid doctor  # reachability + token check

# register + deploy in one:
oxid up main --repo https://github.com/you/app.git
# private repo:
oxid up main --repo https://github.com/you/private.git --git-token ghp_xxx

# infra (same as the wizard's step 2):
oxid infra status
oxid infra setup  # idempotent

Every command speaks --json with distinct exit codes for CI. See CLI reference.

Other ways to run

Native server — systemd

curl -fsSL https://raw.githubusercontent.com/sazardev/oxid/main/install.sh | sh -s -- --server
# installs to /usr/local/bin, writes /etc/oxid/oxidd.env (0600)
# + /etc/systemd/system/oxidd.service, starts, waits for /health, bootstraps Traefik.
$ journalctl -u oxidd -f          # logs
$ sudo grep ^OXID_API_TOKEN /etc/oxid/oxidd.env | cut -d= -f2  # your token

Binaries only

curl -fsSL https://raw.githubusercontent.com/sazardev/oxid/main/install.sh | sh
# → /usr/local/bin/oxid and /usr/local/bin/oxidd (or ~/.local/bin without root)
# then run oxidd under your own supervisor with the env vars from Daemon docs.

Nix / NixOS

nix run github:sazardev/oxid#oxid -- ps
nix run github:sazardev/oxid#oxidd
# or: nix develop  # dev shell with full toolchain

From source

git clone https://github.com/sazardev/oxid && cd oxid
cargo build --workspace  # toolchain pinned in rust-toolchain.toml
cargo run -p oxid-daemon  # or: cargo run -p oxid-cli -- ps

See CONTRIBUTING.md for the fmt → clippy -D warnings → test gate order and hook setup.

Requirements

  • Docker on the daemon host (build/run/pause) — not needed for cargo test (pure oxid-core tests are instant).
  • A long random OXID_API_TOKEN when binding beyond 127.0.0.1 (the daemon refuses to start otherwise; override with OXID_ALLOW_OPEN_API=1).
  • For Traefik subdomains: a wildcard DNS/hosts entry pointing at the host (e.g. *.local.dev → 127.0.0.1).

Upgrading

# installer-managed docker stack:
cd oxid-stack && docker compose pull && docker compose up -d

# binaries (re-run is safe — secrets never rotated):
curl -fsSL https://raw.githubusercontent.com/sazardev/oxid/main/install.sh | sh -s -- --docker
curl -fsSL https://raw.githubusercontent.com/sazardev/oxid/main/install.sh | sh -s -- --server

Migrations in crates/oxid-daemon/migrations/*.sql run automatically at startup via sqlx. Pre-built binaries for every tagged release cover Linux (musl/gnu x86_64/aarch64), macOS and Windows — see Releases.

Next steps

  • Point webhooks at http://DAEMON:8080/api/v1/webhooks/{github,gitlab,gitea,gogs} with the same OXID_WEBHOOK_SECRET — every push deploys, branch deletion destroys.
  • Scope team access: oxid token create alice --project 1 (project-scoped tokens 404 outside their projects) — or via the dashboard's Admin page.
  • Configure shared Postgres/Redis: add OXID_POSTGRES_URL / OXID_REDIS_URL to the daemon env so projects declaring those dependencies get per-branch logical databases.
  • Backups: OXID_BACKUP_INTERVAL_SECS or a Litestream sidecar (see docker-compose.yml); restore with oxid restore.

Deep dive: Daemon env vars · CLI reference · HTTP API · Web dashboard · PRODUCTION.md