Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migrating from buildbot-nix

Nixbot is a rewrite of buildbot-nix, its spiritual ancestor, and no longer runs on top of Buildbot. The old master/worker pair is now a single asyncio service that does everything itself: forge webhooks, evaluation with nix-eval-jobs, builds through the local nix daemon, commit statuses, hercules-ci-style effects, and its own web UI with a JSON API, live logs over SSE, and Prometheus metrics.

This guide covers moving an existing buildbot-nix deployment to nixbot.

What you need to do when upgrading

NixOS module. Import nixosModules.nixbot; options live under services.nixbot. Your existing services.buildbot-nix.master.* config keeps working: options rename to services.nixbot.* automatically and print deprecation warnings. nixosModules.buildbot-master and nixosModules.buildbot-worker are aliases for the new module. Options that have no equivalent anymore (workers, oauth2-proxy mode, dbUrl, the Gitea webhook secret) fail the build with a hint on what to do instead.

Workers are gone. Delete workersFile, worker passwords and localWorkers. Builds go through the nix daemon and scale with ordinary nix remote builders.

Database. PostgreSQL only, with a fresh schema and plain SQL migrations. Build history does not carry over. By default the module provisions a local PostgreSQL over the unix socket; for a remote database set database.url or database.urlFile.

Authentication. httpbasicauth and the oauth2-proxy accessMode.fullyPrivate setup are gone. The built-in GitHub/Gitea/OIDC login covers the same need: private repositories are hidden from anyone not authorized to see them. GitHub token mode is also gone; use a GitHub App.

authBackend is removed. Enable forges explicitly (github.enable, gitea.enable, oidc.enable); every enabled forge with OAuth credentials configured offers a login, and several can be active at once.

admins entries must be provider-qualified: github:Mic92, not Mic92. Unqualified entries never match and only log a warning.

OAuth callback URLs change: update your GitHub App / Gitea application to https://<domain>/auth/<provider>/callback (buildbot used /auth/login), e.g. https://buildbot.example.com/auth/github/callback.

Commit statuses / check runs. GitHub now receives Check Runs instead of commit statuses; Gitea/GitLab keep commit statuses. Names default to the nixbot/ prefix (nixbot/nix-eval, nixbot/nix-build ...). Set services.nixbot.statusContextPrefix = "buildbot" to keep the buildbot-era names so existing branch protection rules keep working (GitHub matches check-run names and commit-status contexts in the same required-checks namespace); otherwise update the required status checks on every repository. Grant the GitHub App the Checks: read & write permission and subscribe it to the Check run / Check suite events.

Webhooks. The old GitHub endpoint (/change_hook/github) still works as an alias. /change_hook/gitea is gone: legacy Gitea hooks carry old buildbot secrets that cannot match the per-repository secrets this service generates, and the service re-registers Gitea hooks against /webhooks/gitea automatically. However, per-repository GitHub webhooks are no longer created: events arrive through the App-level webhook. Enable the webhook on your GitHub App (Active, URL https://<domain>/webhooks/github, secret matching webhookSecretFile, events push, pull_request, check_run, check_suite); see docs/GITHUB.md. Subscribing to the pull_request event requires the “Pull requests: Read-only” repository permission. Adding a permission must be accepted on every installation of the app (your user account and each organization) under Settings → GitHub Apps → Configure. The service logs a warning at startup if the app is misconfigured.

Gitea webhooks now register at https://<domain>/webhooks/gitea with an auto-generated per-repository secret stored in the database — gitea.webhookSecretFile is gone. Existing hooks are re-synced in place, leftover buildbot-era hooks pointing at this instance are removed. Hooks subscribe to push, pull_request and pull_request_sync.

Project enablement. Which repositories get built is now a per-project toggle in the web UI (admins only). topic is reduced to a one-shot import: on first startup with an empty database, repositories carrying the topic are enabled; afterwards it is ignored. userAllowlist/repoAllowlist remain a hard boundary at discovery time.

Per-repository config. The file is now called nixbot.toml; the legacy buildbot-nix.toml name keeps working with unchanged contents.

Post-build steps. interpolate placeholders still work. Properties: attr, out_path, drv_path, system, project, branch, revision, pr_number, default_branch. Two changes:

  • PR builds no longer run under a refs/pull/N/merge branch — use %(prop:pr_number)s instead of parsing the branch name.
  • %(secret:NAME)s reads systemd credentials of the nixbot unit, so move LoadCredential entries from systemd.services.buildbot-master to systemd.services.nixbot.

Buildbot customizations. Anything that reached into Buildbot itself — services.buildbot-master.extraConfig, the manhole, pythonPackages — has no equivalent.

API. The JSON API moves to /api/*, with an OpenAPI schema at /openapi.json.

What you get compared to buildbot-nix

  • Builds are keyed by the post-merge tree hash: identical trees across branches and PRs reuse results instead of rebuilding.
  • Crash recovery: after a restart, unfinished builds resume from their stored eval results without re-evaluating.
  • Evaluation runs in a bwrap sandbox with a kernel-enforced memory cap (delegated cgroup v2 subtree).
  • Per-user API tokens for scripted access.
  • GitLab support (services.nixbot.gitlab): token-based, with per-repository webhooks and commit statuses. See docs/GITLAB.md.
  • Effects show up on the build page with status, live log and failure excerpt.
  • Effects parity with hercules-ci-agent: secretsMap selection with per-secret conditions, GitToken references, the current-task state API (getStateFile/putStateFile), per-effect mounts (effects.mountables), virtual uid/gid, a disk-backed /build, and a private untrusted nix daemon per effect (effects.extraNixOptions). Effects without secretsMap keep receiving the whole secrets file.