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

NixOS Module

The kennel NixOS module configures the kennel service, Caddy, systemd integration, and resource provisioning on a NixOS host.

{ kennel, ... }:
{
  imports = [ kennel.nixosModules.default ];

  services.kennel = {
    enable = true;
    package = kennel.packages.x86_64-linux.kennel;
    devenvPackage = kennel.packages.x86_64-linux.devenv;
    webhookSecretFile = config.age.secrets.kennel-webhook.path;
    environmentFile = config.age.secrets.kennel.path;

    domains = {
      ephemeral = "scottylabs.net";
      cloudflare.zones."scottylabs.org" = "<zone-id>";
    };

    resources.postgres = {
      enable = true;
      socketDir = "/run/postgresql";
    };

    secrets = {
      enable = true;
      vaultEndpoint = "https://secrets2.scottylabs.org";
    };
  };
}

Options

services.kennel.enable

Enable the kennel deployment platform.

Type: bool, default: false

services.kennel.package

The kennel package to use.

Type: package

services.kennel.devenvPackage

The devenv package. The build worker uses devenv build to evaluate project kennel configs from their devenv.nix.

Type: package

services.kennel.environmentFile

Path to an environment file containing secrets like VAULT_TOKEN, CACHIX_AUTH_TOKEN, and GARAGE_ADMIN_TOKEN. Loaded by systemd before the service starts.

Type: nullOr path, default: null

services.kennel.api.host / services.kennel.api.port

API server bind address and port.

Type: str / port, defaults: "0.0.0.0" / 3000

services.kennel.webhookSecretFile

Path to a file containing the HMAC secret used to verify all incoming webhooks. This is a single secret shared across all projects, provisioned by governance.

Type: path

services.kennel.domains.ephemeral

Base domain for auto-generated deployment URLs. A wildcard DNS record should point *.{domain} to the kennel server.

Type: str, default: "scottylabs.net"

services.kennel.domains.cloudflare.zones

Map of domain names to Cloudflare zone IDs. When this is non-empty, publicIp is set, and the CLOUDFLARE_API_TOKEN env var is provided (typically via the environmentFile secret), kennel automatically manages A records for any custom domain whose suffix matches one of the configured zones. The most specific zone wins for nested domains.

The token must have Zone:DNS:Edit permission on the zones listed.

Records are upserted on deploy and on each reconciliation pass (so they self-heal if pruned externally). Records are deleted only when kennel tears the deployment down, which happens in three cases:

  1. The branch backing the deployment is deleted from the source repo (push event with deleted=true on that ref).
  2. The deployment is associated with a pull request and that pull request is closed.
  3. The deployment is on a dev or preview branch and exceeds DEPLOYMENT_EXPIRY_DAYS since its last update during a reconciliation pass.

Production deployments are not subject to expiry, so a record for a production custom domain stays in place until the project’s main branch is deleted or the deployment row is removed manually.

Type: attrsOf str, default: {}

services.kennel.domains.cloudflare.publicIp

Public IPv4 used as the content of the A records that kennel creates for custom domains. Required to enable DNS automation.

Type: nullOr str, default: null

services.kennel.domain

Public domain for the kennel API and webhook endpoint. The module configures a Caddy virtualhost with automatic TLS for this domain, reverse-proxying to the API server.

Type: str, default: "kennel.scottylabs.org"

services.kennel.caddy.adminUrl

Caddy admin API URL.

Type: str, default: "http://localhost:2019"

services.kennel.builder.maxConcurrentBuilds

Maximum number of concurrent nix builds.

Type: int, default: 2

services.kennel.builder.workDir

Build working directory.

Type: path, default: "/var/lib/kennel/builds"

services.kennel.builder.cachix.enable / services.kennel.builder.cachix.cacheName

Enable pushing build artifacts to a Cachix binary cache.

services.kennel.resources.postgres

Enable PostgreSQL resource provisioning. Kennel creates a database per deployment using the specified socket directory for peer authentication.

  • enable (bool, default: false)
  • socketDir (path, default: "/run/postgresql")

services.kennel.resources.valkey

Enable Valkey resource provisioning. Kennel allocates a DB number per deployment from the shared instance.

  • enable (bool, default: false)
  • socketPath (path, default: "/run/valkey/valkey.sock")

services.kennel.resources.garage

Enable Garage S3 resource provisioning. Kennel creates a bucket and API key per deployment. Requires GARAGE_ADMIN_TOKEN in the environment file.

  • enable (bool, default: false)
  • adminEndpoint (str, default: "http://localhost:3903")
  • s3Endpoint (str, default: "http://localhost:3900")

services.kennel.secrets

Enable secretspec/OpenBao secret resolution at deploy time.

  • enable (bool, default: false)
  • vaultEndpoint (str, default: "https://secrets2.scottylabs.org")

services.kennel.forgejo

Forgejo API access for posting deployment status comments on pull requests. Required.

  • apiUrl (str, default: "https://codeberg.org/api/v1") – Forgejo API base URL
  • apiTokenFile (path, required) – path to a file containing an API token with the write:issue scope. Kennel uses this to post and update a sticky comment on each PR listing its deployment URLs, and to mark the comment torn down when the PR is closed.

services.kennel.keycloak

Keycloak admin access for OIDC client reconciliation. When url is set, kennel manages a confidential client per project (named after the project slug) plus a {slug}-staging client, keeping their valid_redirect_uris in sync with each service’s oidc.redirectPaths declared in scottylabs.kennel.services.<name>.oidc. PR-preview URLs are added on PR open and removed on PR close.

  • url (nullOr str, default: null) – Keycloak server URL. Setting this enables reconciliation
  • realm (str, default: "scottylabs") – realm to manage clients in
  • adminClientId (nullOr str, required when url is set) – client_id of the service-account client kennel authenticates as (typically "kennel", provisioned in tofu with the realm-management/manage-clients role)
  • adminClientSecretFile (nullOr path, required when url is set) – path to a file containing the admin client secret. Typically populated by bao-agent from secret/data/infra/kennel-keycloak-admin

What the module configures

  • A systemd service for kennel with Delegate=yes for cgroup v2 access
  • A polkit rule allowing the kennel user to create transient systemd units via D-Bus
  • A kennel.slice for all managed deployment units
  • A Caddy virtualhost for the kennel domain with automatic TLS, plus the admin API for dynamic route management
  • tmpfiles rules for /var/lib/kennel subdirectories
  • Firewall rules for ports 80 and 443
  • Cachix binary cache substituter