// 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:
| # | Step | What it does | Behind the scenes |
| 1 | Token | Paste OXID_API_TOKEN. With OXID_AUTO_TOKEN the hint shows docker compose logs | grep Generated. | GET /api/v1/stats verifies |
| 2 | Infrastructure | Checklist: Docker network, Traefik, self-wiring. One-click fix if anything is missing. | GET /api/v1/infra/status → POST /api/v1/infra/bootstrap (idempotent) |
| 3 | First project | Register 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 |
| 4 | Webhooks | Pick 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) |
| 5 | CLI | Copy-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