- Rust 96.1%
- Nix 3.9%
The service should work across reboots, the very simplistic config for the buckets/keys/permissions should be easy enough to extend for local development. Example: ``` $ nix run .#garage <starts vm and opens root-shell> [nixos login: root (automatic login) [root@nixos:~]# garage status ==== HEALTHY NODES ==== ID Hostname Address Tags Zone Capacity DataAvail Version fb73d8ca538099d0 nixos 10.0.2.15:3901 [] garage 2.0 GB 1.9 GB (92.8%) cargo:2.2.0 ``` second terminal, inside botanix devshell (for env vars): ``` $ aws s3 cp README.md s3://botanix/test upload: ./README.md to s3://botanix/test $ aws s3 ls s3://botanix/test 2026-04-16 17:32:11 3484 test ``` State of the vm is in the gitignored nixos.qcow2 in the repo root. look at me, I'm the OpenTofu now |
||
|---|---|---|
| nix | ||
| packaging | ||
| proto | ||
| src | ||
| tests | ||
| .env.example | ||
| .envrc | ||
| .gitignore | ||
| AUTHORS.md | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| flake.lock | ||
| flake.nix | ||
| LICENSE.md | ||
| Procfile | ||
| README.md | ||
| sources.nix | ||
Botanix
A distributed CI/CD system for Nix projects with webhook integration for Git forges.
Overview
Botanix is coordinator-worker system that builds Nix derivations in response to Git webhooks.
Quick Start
Prerequisites
- Rust (latest stable)
- Nix with flakes enabled
Create a test repository
Create a public test repository on a public forgejo instance. Since we use forge webhooks, Botanix still needs to clone something.
To have something to build for a quick-start, add a flake.nix like
{
description = "A very basic flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs = { self, nixpkgs }: let pkgs = nixpkgs.legacyPackages.x86_64-linux; in {
hydraJobs = {
hello = pkgs.hello.overrideAttrs (_: {
postPatch = ''
# noop
'';
});
};
};
}
Prepare environment
cp .env.example .env
Fill the values like this:
- leave
WORKER_TOKENempty (will be filled later) - set
FORGEJO_APItohttps://your-forgejo.tld/api/v1/ - create a token in Forgejo in "Settings > Applications", give it read/write access to public repos.
Start the coordinator (Server)
cargo run -- --mode server --port 8080
Start a Worker
First you need a WORKER_TOKEN. You can obtain one by registering with coordinator:
curl -s -X POST /worker/register \
-H 'Content-Type: application/json' \
-d '{"systems":[SYSTEM_OF_YOUR_WORKER]}'
Set WORKER_TOKEN to the token from this request.
Now you can start both components with hivemind.
Configure a Webhook
Add a webhook to your Git repository, for now available PROVIDER are { forgejo }
- URL:
http://your-server:8080/webhooks/PROVIDER - SECRET: Same as
WEBHOOK_SECRET - EVENTS: Push events, Pull Request events.
Send a webhook for your test repo
nix run .#forgejo-webhook '{
"repository":{"name":"your-test-repo","youruser/your-test-repo","clone_url":"https://forgejo.tld/youruser/your-test-repo","ssh_url":"..."},
"owner":"youruser",
"ref":"main",
"after":"'"$(git -C /path/to/your/testrepo log --format="%H" -n 1)"'"
}'
Configuration
Server
-
Required:
WEBHOOK_SECRET: HMAC secret for validating webhooksNIX_SYSTEMS: Comma separated systems this server supports (default:x86_64-linux).
-
Optional:
FORGEJO_API: Forgejo API base URL for status updates.FORGEJO_TOKEN: Personal Access Token for Forgejo API.PORT: Coordinator http Port, (default:8080)
Worker
-
Required:
COORDINATOR_URL: Coordinator server base URLWORKER_TOKEN: Authentication token.NIX_SYSTEMS: Comma separated systems this worker supports (default:x86_64-linux).
-
Optional:
CACHE_URL: Nix binary cache URL for substitutions.CACHE_NETRC_PATH: Path to .netrc file for authenticated cache access.CHECKOUT_DIR: Directory to store git checkouts (default: follows systemd hierarchy).
Checkout Directory configuration
The worker stores cloned git repositories in a checkout directory. By default, this directory is determined in the following order:
Priority order:
CHECKOUT_DIRenvironment variable (if set by the user).$RUNTIME_DIRECTORY/botanix/checkouts(ifRUNTIME_DIRECTORYis set, typically by systemd).$TMPDIR/botanix/checkouts(ifTMPDIRis set)./tmp/botanix/checkouts(default fallback).