Skip to main content
You can run Orbi in Docker with the community-maintained wrapper when the host cannot provide a systemd user session. The Docker deployment under 3rd/docker/ in the repository is a community-maintained, unofficial wrapper: one Linux container that runs the unmodified official deployment — systemd as PID 1 with a real user manager, the official systemd/[email protected] + [email protected] user units, the editable CLI install and the idempotent orbi setup. It is not a second deployment model: on a Linux host with a systemd user session, always prefer the official path (see Getting started) — pick one of the two, never both for the same task pool.

When to use it

  • The host has no user systemd session (a NAS, a container platform, a restricted cloud box) but can run Docker.
  • You want the runner isolated from the host user session.

Quick start

Pull the published image, then start the runner with one docker run. Both registries carry the same tags: latest and the release number without the v prefix (e.g. 0.5.17).
One docker run reaches a real delivery: the GitHub token, the task pool and the model provider all come from environment variables, and the entrypoint turns them into the deploy-home env file, the generated orbi.toml and the generated pi-providers.json on first start (existing files are never overwritten). Substitute the three quoted values — token, repo, key; the DeepSeek endpoint is an example, any OpenAI-compatible endpoint works through the same four variables (ORBI_PI_PROVIDER, ORBI_PI_MODEL, ORBI_PI_BASE_URL, ORBI_PI_API_KEY). If you pulled from Docker Hub, use the docker.io/orbibuild/orbi:latest image reference instead.
One docker run, no compose (the runner is a single service). --cgroupns=host plus the read-write /sys/fs/cgroup bind is what lets systemd run as PID 1 inside the container — without them /init.scope creation fails under the default private cgroup namespace (verified; no --privileged needed). All state (the orbi checkout, the config, the runtime state, the delivery checkout and its worktrees) lives in the two volumes, so a container rebuild keeps everything and the idempotent setup re-converges on the next start.

How configuration reaches the runner

The full contract — the generated config, daily operations, and the failure/repair table — lives in 3rd/docker/README.md.

First delivery

With the runner up, deliver a real task:
  1. In the task pool repo, open an Issue stating ONE runtime outcome in one of the two shapes: a fix (when X, should Y, actually Z) or a change (when X, the user should be able to Y; today they cannot because Z).
  2. Add the ai-ready label (the first start created the twelve platform labels).
  3. Follow the tick log:
Within two ticks (the timer fires every 5 minutes) the Issue walks ai-ready → ai-in-progress → ai-pr-opened and a PR opens; it merges (ai-merged) after the independent review round. No docker exec configuration step is needed anywhere in this path — one that is required is a docs or entrypoint defect.

Orbi quick guide

The delivery vocabulary in one place; each link goes to the full reference instead of duplicating it.
  • The task — an Issue stating ONE runtime outcome in one of the two shapes: a fix (when X, should Y, actually Z) or a change (when X, the user should be able to Y; today they cannot because Z), plus a short acceptance list: the user journey as precondition → command/configuration → the success the user sees → the failure path with its repair.
  • The labels — ai-ready is the execution switch: the runner only picks up labelled Issues (pickup order: p0 first, then bug, then plain). A claimed Issue walks ai-in-progress → ai-pr-opened → ai-merged (terminal; the PR body’s Fixes #N closes the Issue) or ai-fix-needed (the next review round fixes the finding); ai-blocked (terminal) needs a human decision. ai-epic marks a coordination-only Issue the runner never claims, and ai-release marks a release task (below). The runner manages the delivery states; ai-ready, ai-epic, ai-release, p0 and bug are yours to add and remove.
  • Watching — the run keeps exactly one progress comment on the Issue, updated in place at every milestone, and the tick log streams through the journalctl command above.
  • What to expect — within two ticks the branch and PR exist (Fixes #N in the body), an independent review session runs (at most 5 rounds), and the merge lands exactly the reviewed head — GitHub then closes the Issue.
  • Releases — a release is its own Issue labelled ai-ready + ai-release whose body declares a ## Release block: version, base_branch, and a scope (a list of #N items) or scope_from_milestone. The Runner’s release state machine freezes the base, waits for green CI, bumps the version, tags, publishes the GitHub Release and closes the matching Milestone — no PR; any failure stops at ai-blocked for a human decision.
Full reference: Workflow (labels, review loop, release state machine) and Operations (observability, journal).

Operations

In-container commands run as -u orbi (the user the runner runs as); systemctl --user / journalctl --user need the user session bus, which docker exec does not carry automatically — hence -e XDG_RUNTIME_DIR=/run/user/1000. The orbi CLI installs into the orbi user’s ~/.local/bin, which is not on the docker exec PATH — hence the bash -c wrapper (docker exec sets HOME for the -u user). Upgrading: docker pull the newer tag and recreate the container with the same volumes and variables — state survives in the volumes and the entrypoint plus orbi setup re-converge.

Build from source

When you need to change the wrapper itself (the entrypoint, the setup oneshot, the Dockerfile), build the image from a repository checkout instead of pulling:
Then run the same docker run as in the Quick start with orbi-docker as the image name. The image contains no credentials — configuration is injected per the table above at docker run time.

Advanced: several providers

The Quick start configures ONE provider from environment variables. To serve several (e.g. deepseek plus groq):
  1. Pass the extra keys at docker run time via ORBI_ENV_<NAME> (repeatable): -e ORBI_ENV_GROQ_API_KEY="gsk-xxx" lands in /orbi/.orbi/env as GROQ_API_KEY.
  2. Edit /orbi/.orbi/pi-providers.json — the entrypoint generated it with one provider and never overwrites an existing file — and add the other providers, each apiKey referencing its env variable ("$GROQ_API_KEY").
  3. Point pi_provider / pi_model in /orbi/orbi.toml at the provider and model to use, then restart the container: docker restart orbi (both files are yours — the entrypoint never overwrites an existing config).
If the first start had no provider variables at all, no pi-providers.json was generated; orbi setup scaffolds a starter file to fill in instead. Verify the endpoint before relying on it:
A real API reply means endpoint and key work; 401 or a timeout is a key or network problem.

Boundaries

  • Thin wrapper only: src/orbi/ core code is untouched, and a blocker hit inside the container is filed as its own core Issue.
  • Linux only; no compose, no multi-container orchestration, no automatic image updates; not part of the PyPI release or the getting-started main path.
  • Community-maintained: official Issues prioritize the core systemd path.