From d4b647542c0af136976d0e2250274fb894295915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 16 Jul 2026 19:05:02 +0200 Subject: [PATCH 01/16] feat: add `authentik` service module --- doc/authentik.md | 76 +++++ nix/services/authentik.nix | 8 + nix/services/authentik/options.nix | 265 ++++++++++++++++++ nix/services/authentik/service.nix | 237 ++++++++++++++++ .../authentik/test-blueprints/example.yaml | 11 + nix/services/authentik_test.nix | 58 ++++ nix/services/default.nix | 81 +++--- test/flake.lock | 238 +++++++++++++++- test/flake.nix | 185 ++++++------ test/nix/pkgs.nix | 47 ++-- 10 files changed, 1058 insertions(+), 148 deletions(-) create mode 100644 doc/authentik.md create mode 100644 nix/services/authentik.nix create mode 100644 nix/services/authentik/options.nix create mode 100644 nix/services/authentik/service.nix create mode 100644 nix/services/authentik/test-blueprints/example.yaml create mode 100644 nix/services/authentik_test.nix diff --git a/doc/authentik.md b/doc/authentik.md new file mode 100644 index 00000000..2c430763 --- /dev/null +++ b/doc/authentik.md @@ -0,0 +1,76 @@ +# Authentik + +[Authentik](https://goauthentik.io/) is an open-source identity provider offering single sign-on (SSO), user federation and support for OpenID Connect, OAuth 2.0, SAML and more. + +> [!NOTE] +> +> This module runs the `migrate`, `worker` and `server` components for local development and testing, not production. Secrets set via `settings`/`secretKey/`environmentFile` end up in the Nix store. + +Authentik is not in nixpkgs. You must provide the packages yourself via [`components`](#components) from the [`authentik-nix`](https://github.com/nix-community/authentik-nix) flake input. +It also needs a PostgreSQL and a Redis instance. + +{#start} + +## Getting Started + +```nix +perSystem = {config, inputs', ...}: +let + ak = config.process-compose."authentik".services.authentik.ak; +in +{ + process-compose."authentik" = { + # Configure the sidecar postgres and a redis processes. + services.postgres = ak.services.postgres; + services.redis = ak.services.redis; + + # Configure authentik. + services.authentik."authentik" = { + enable = true; + + compponents = inputs.authentik-nix.packages.${system}; + secretKey = "dev-secret"; + + settings = { + listen.http = "0.0.0.0:9000"; + + postgresql = { + host = "127.0.0.1"; + port = 5433; + user = "authentik"; + name = "authentik"; + password = "authentik"; + }; + + redis.host = "127.0.0.1"; + redis.port = 6378; + }; + }; + }; +``` + +Authentik becomes available at [http://localhost:9000](http://localhost:9000). +The bootstrap admin user is `akadmin` with the e-mail set by [`initialAdminEmail`](#admin-email) (`admin@example.com` by default) and the password set by [`initialAdminPassword`](#admin-password) (`admin` by default). + +{#tips} + +## Tips & Tricks + +{#blueprints} + +### Import Blueprints + +[Blueprints](https://docs.goauthentik.io/docs/customize/blueprints/) declare Authentik objects (groups, applications, flows, ...) as YAML. A blueprint listed under `settings.blueprints` with `import = true` (the default) and a `path` set is copied into the blueprints directory on start up and auto-applied by the worker. +The `path` may be relative to the `process-compose` working directory or a Nix store path. + +```nix +{ + services.authentik."ak" = { + enable = true; + + settings.blueprints.my-app = { + path = ./blueprints/my-app.yaml; + }; + }; +} +``` diff --git a/nix/services/authentik.nix b/nix/services/authentik.nix new file mode 100644 index 00000000..167ec313 --- /dev/null +++ b/nix/services/authentik.nix @@ -0,0 +1,8 @@ +{ ... +}: +{ + imports = [ + ./authentik/service.nix + ./authentik/options.nix + ]; +} diff --git a/nix/services/authentik/options.nix b/nix/services/authentik/options.nix new file mode 100644 index 00000000..a0fd0cb3 --- /dev/null +++ b/nix/services/authentik/options.nix @@ -0,0 +1,265 @@ +{ lib +, ... +}: + +let + inherit (lib) + mkOption + types + ; + + # A relative user-provided path, or a Nix store path (same pattern as keycloak realms). + blueprintPath = types.nullOr ( + types.either + (types.pathWith { + inStore = false; + absolute = false; + }) + (types.pathWith { inStore = true; }) + ); +in +{ + options = { + # Authentik is not in nixpkgs, so the package set has to be provided by the user + # from their own `authentik-nix` flake input. This keeps services-flake input-free. + components = mkOption { + type = types.attrsOf types.package; + example = lib.literalExpression "inputs.authentik-nix.packages.\${system}"; + description = '' + The Authentik component packages, typically + `inputs.authentik-nix.packages.''${system}`. + + Must provide at least the following attributes: + - `gopkgs` – provides `bin/server` + - `rust` – provides `bin/authentik` (the `worker` subcommand) + - `migrate` – provides `bin/migrate.py` + - `staticWorkdirDeps` – the working-directory dependencies + (`authentik/`, `blueprints/`, `templates/`, static assets) + - `manage` – the management CLI (optional, for blueprint tooling) + ''; + }; + + secretKey = mkOption { + type = types.nullOr types.str; + example = "insecure-dev-secret"; + description = '' + The Authentik secret key, exported as `AUTHENTIK_SECRET_KEY`. + + This is written into the process environment and therefore ends up in the + Nix store; only use it for local development. For anything else set + {option}`environmentFile` instead and put `AUTHENTIK_SECRET_KEY` there. + ''; + }; + + initialAdminEmail = mkOption { + type = types.str; + default = "admin@example.com"; + description = '' + E-mail for the bootstrap `akadmin` user, exported as + `AUTHENTIK_BOOTSTRAP_EMAIL`. + ''; + }; + + initialAdminPassword = mkOption { + type = types.str; + default = "admin"; + description = '' + Initial password for the bootstrap `akadmin` user, exported as + `AUTHENTIK_BOOTSTRAP_PASSWORD`. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr ( + types.pathWith { + inStore = false; + absolute = false; + } + ); + default = null; + example = "authentik.env"; + description = '' + Path to an environment file with additional + `AUTHENTIK_*` variables, e.g. `AUTHENTIK_SECRET_KEY` and + `AUTHENTIK_POSTGRESQL__PASSWORD`. + Values here override {option}`settings`. + ''; + }; + + services = { + postgres = mkOption { + type = types.attrsOf types.raw; + readOnly = true; + description = '' + The config to easily define the needed postgres process. + ''; + }; + + redis = mkOption { + type = types.attrsOf types.raw; + readOnly = true; + description = '' + The config to easily define the needed redis process. + ''; + }; + }; + + settings = mkOption { + type = types.submodule { + options = { + logLevel = mkOption { + type = types.str; + default = "info"; + example = "debug"; + description = "Authentik log level (`log_level`)."; + }; + + listen = { + http = mkOption { + type = types.str; + default = "0.0.0.0:9000"; + description = "Address the HTTP server listens on (`listen.listen_http`)."; + }; + + https = mkOption { + type = types.str; + default = "0.0.0.0:9443"; + description = "Address the HTTPS server listens on (`listen.listen_https`)."; + }; + }; + + email = { + host = mkOption { + type = types.str; + default = "localhost"; + description = "Email SMTP server host name."; + }; + + port = mkOption { + type = types.str; + default = "localhost"; + description = "Email SMTP server port."; + }; + }; + + postgres = { + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "PostgreSQL host (`postgresql.host`)."; + }; + + port = mkOption { + type = types.port; + default = 5432; + description = "PostgreSQL port (`postgresql.port`)."; + }; + + name = mkOption { + type = types.str; + default = "authentik"; + description = "PostgreSQL database name (`postgresql.name`)."; + }; + + user = mkOption { + type = types.str; + default = "authentik"; + description = "PostgreSQL user (`postgresql.user`)."; + }; + + password = mkOption { + type = types.str; + default = "authentik"; + description = '' + PostgreSQL password (`postgresql.password`). Written to the config + file in the Nix store; for non-dev use, override it via + {option}`environmentFile` (`AUTHENTIK_POSTGRESQL__PASSWORD`). + ''; + }; + }; + + redis = { + host = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Redis host (`redis.host`)."; + }; + + port = mkOption { + type = types.port; + default = 6379; + description = "Redis port (`redis.port`)."; + }; + }; + + blueprints = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule { + options = { + path = mkOption { + type = blueprintPath; + default = null; + example = "./blueprints/my-blueprint.yaml"; + description = '' + Path (relative to the `process-compose` working dir, or a Nix store + path) of a blueprint YAML file to make available for import. + ''; + }; + + import = mkOption { + type = types.bool; + default = true; + description = "Whether to make this blueprint available for import."; + }; + }; + } + ); + + example = lib.literalExpression '' + { + my-app = { + path = ./blueprints/my-app.yaml; + }; + } + ''; + + description = '' + Blueprints to import on start up. + Enabled blueprints are copied into the blueprints directoryr and + auto-applied by the Authentik worker. + ''; + }; + }; + }; + + default = { }; + + example = lib.literalExpression '' + { + log_level = "debug"; + listen.listen_http = "0.0.0.0:9002"; + postgresql.host = "127.0.0.1"; + email = { + host = "localhost"; + port = 25; + }; + } + ''; + + description = '' + Authentik configuration, rendered to a `local.yml` file that Authentik loads + from its working directory. Corresponds to the keys documented at + . + + Authentik config is hierarchical YAML, so allow arbitrary nested keys. + It is not well documented. + See the default: + + The defaults point PostgreSQL/Redis at the companion + `services.postgres."authentik-db-pg"` and + `services.redis."authentik-db-redis"` instances used in the example and test. + ''; + }; + }; +} diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix new file mode 100644 index 00000000..723b0fcc --- /dev/null +++ b/nix/services/authentik/service.nix @@ -0,0 +1,237 @@ +{ config +, lib +, pkgs +, name +, ... +}: +let + # The authentik components. + gopkgs = requireComponent "gopkgs"; + rust = requireComponent "rust"; + rawMigrate = requireComponent "migrate"; + pythonEnv = requireComponent "pythonEnv"; + staticWorkdirDeps = requireComponent "staticWorkdirDeps"; + + requireComponent = + attr: + if config.components ? ${attr} then + config.components.${attr} + else + throw '' + services.authentik.${name}.authentikComponents is missing the `${attr}` attribute. + (see the module docs). + ''; + + # NOTE: Workaround -> authentik-nix's `migrate` ships `bin/migrate.py`, a shell wrapper that execs an inner + # `bin/.migrate.py-wrapped` whose shebang is `#!/usr/bin/env python`. + # `/usr/bin/env` does not exist in the Nix build sandbox that + # `nix build .#checks…` (`just test`) runs the process-compose stack in, + # so the migration crashes with "bad interpreter". + migrate = pkgs.runCommand "authentik-migrate-sandbox-safe" { } '' + cp -R --no-preserve=mode,ownership ${rawMigrate} $out + substituteInPlace $out/bin/.migrate.py-wrapped \ + --replace-fail '#!/usr/bin/env python' '#!${pythonEnv}/bin/python' + substituteInPlace $out/bin/migrate.py \ + --replace-fail '${rawMigrate}/bin/.migrate.py-wrapped' "$out/bin/.migrate.py-wrapped" + chmod +x "$out/bin/.migrate.py-wrapped" "$out/bin/migrate.py" + ''; + + # HTTP port for the readiness probe, parsed from `listen.listen_http` (e.g. "0.0.0.0:9000"). + httpPort = lib.last (lib.splitString ":" config.settings.listen.http); + + # NOTE: We would rather set a config.yaml, + # but documentation is pretty bad and not sufficient for certain stuff + # Therefore we set env variables as described. + connEnv = { + AUTHENTIK_POSTGRESQL__HOST = config.settings.postgres.host; + AUTHENTIK_POSTGRESQL__PORT = toString config.settings.postgres.port; + AUTHENTIK_POSTGRESQL__NAME = config.settings.postgres.name; + AUTHENTIK_POSTGRESQL__USER = config.settings.postgres.user; + AUTHENTIK_POSTGRESQL__PASSWORD = config.settings.postgres.password; + + AUTHENTIK_REDIS__HOST = config.settings.redis.host; + AUTHENTIK_REDIS__PORT = toString config.settings.redis.port; + + AUTHENTIK_LISTEN__HTTP = config.settings.listen.http; + AUTHENTIK_LISTEN__HTTPS = config.settings.listen.https; + AUTHENTIK_LOG_LEVEL = config.settings.logLevel; + }; + + baseEnv = connEnv // { + AUTHENTIK_SECRET_KEY = config.secretKey; + AUTHENTIK_BOOTSTRAP_PASSWORD = config.initialAdminPassword; + AUTHENTIK_BOOTSTRAP_EMAIL = config.initialAdminEmail; + }; + + # Copy the enabled blueprints into the Authentik blueprints dir. + # Copied (not symlinked) for the same reason as the built-in + # blueprints: authentik rejects blueprints whose resolved path escapes `blueprints_dir`. + blueprintImport = lib.mapAttrsToList + ( + bp: e: + # Bash + '' + f=$(realpath "${e.path}") + echo "Copying blueprint '${bp}' from '$f' into './blueprints'." + if [ ! -f "$f" ]; then + echo "Blueprint file '$f' does not exist!" >&2 + exit 1 + fi + cp -L "$f" "./blueprints/" + chmod u+w "./blueprints/$(basename "$f")" + unset f + '' + ) + (lib.filterAttrs (_: v: v.import && v.path != null) config.settings.blueprints); + + loadEnvFile = lib.optionalString (config.environmentFile != null) '' + echo "Load extra authentic environment file '${config.environmentFile}'." + set -a + # shellcheck disable=SC1091 + . "${config.environmentFile}" + set +a + ''; + + runtimeEnv = '' + dataDir="$(realpath ${config.dataDir})" + echo "Authentik data dir: '$dataDir'." + + mkdir -p "$dataDir/media" "$dataDir/certs" "$dataDir/prometheus" + + export AUTHENTIK_STORAGE__FILE__PATH="$dataDir" + export AUTHENTIK_STORAGE__MEDIA__FILE__PATH="$dataDir/media" + export AUTHENTIK_CERT_DISCOVERY_DIR="$dataDir/certs" + export AUTHENTIK_BLUEPRINTS_DIR="$dataDir/blueprints" + export PROMETHEUS_MULTIPROC_DIR="$dataDir/prometheus" + export AUTHENTIK_TEMPLATE_DIR="$dataDir/templates"; + + cd "$dataDir" + ''; + + setup = '' + set -euo pipefail + ${runtimeEnv} + ${loadEnvFile} + + echo "Working dir: $(pwd)" + + # Bring in Authentik's working-directory dependencies (authentik/, templates/, static + # assets, ...) but manage `blueprints/` ourselves so we can add user blueprints. + for dep in ${staticWorkdirDeps}/*; do + base=$(basename "$dep") + [ "$base" = "blueprints" ] && continue + + echo "Symlinking '$dep' -> '$base'" + ln -sfn "$dep" "./$base" + done + + # Combined blueprints dir: Authentik's built-in blueprints + user-provided ones. + # These must be COPIED (not symlinked): A symlink into the nix store resolves + # outside the dir and is rejected, which crashes the server's bootstrap worker. + rm -rf ./blueprints + mkdir -p ./blueprints + if [ -d "${staticWorkdirDeps}/blueprints" ]; then + cp -rL "${staticWorkdirDeps}/blueprints/." ./blueprints/ + fi + chmod -R u+w ./blueprints + ${builtins.concatStringsSep "\n" blueprintImport} + ''; + + authentik-migrate = + pkgs.writeShellScriptBin "authentik-migrate" + # Bash + '' + ${setup} + echo "Starting authentik migrate ..." + exec ${migrate}/bin/migrate.py + ''; + + authentik-worker = + pkgs.writeShellScriptBin "authentik-worker" + # Bash + '' + ${runtimeEnv} + ${loadEnvFile} + echo "Starting authentik worker ..." + exec ${rust}/bin/authentik worker + ''; + + authentik-server = + pkgs.writeShellScriptBin "authentik-server" + # Bash + '' + ${runtimeEnv} + ${loadEnvFile} + echo "Starting authentik server ..." + exec ${gopkgs}/bin/server + ''; + + authentik-health = + pkgs.writeShellScriptBin "authentik-health" + # Bash + '' + ${pkgs.curl}/bin/curl -fsS "http://localhost:${httpPort}/-/health/ready/" + ''; +in +{ + services = { + postgres = { + "${name}-pg-db" = { + enable = true; + port = config.settings.postgres.port; + initialScript.before = '' + CREATE USER \"${config.settings.postgres.user}\" WITH PASSWORD '${config.settings.postgres.password}' CREATEDB; + CREATE DATABASE \"${config.settings.postgres.name}\" OWNER \"${config.settings.postgres.user}\" + ''; + }; + }; + + redis = { + "${name}-redis-db" = { + enable = true; + port = config.settings.redis.port; + }; + }; + }; + + outputs = { + settings.processes = { + "${name}-migrate" = { + description = "Authentik database migrations: a prerequisite that creates the schema."; + environment = baseEnv; + command = "${lib.getExe authentik-migrate}"; + + depends_on = { + "${name}-pg-db".condition = "process_healthy"; + "${name}-redis-db".condition = "process_healthy"; + }; + }; + + "${name}-worker" = { + description = "Authentik background worker: processes tasks and applies blueprints."; + environment = baseEnv; + command = "${lib.getExe authentik-worker}"; + depends_on."${name}-migrate".condition = "process_completed_successfully"; + }; + + "${name}" = { + description = "Authentik HTTP/API server."; + environment = baseEnv; + command = "${lib.getExe authentik-server}"; + + depends_on = { + "${name}-migrate".condition = "process_completed_successfully"; + "${name}-worker".condition = "process_started"; + }; + + readiness_probe = { + exec.command = "${lib.getExe authentik-health}"; + initial_delay_seconds = 20; + period_seconds = 5; + timeout_seconds = 4; + failure_threshold = 30; + }; + }; + }; + }; +} diff --git a/nix/services/authentik/test-blueprints/example.yaml b/nix/services/authentik/test-blueprints/example.yaml new file mode 100644 index 00000000..e2f834ce --- /dev/null +++ b/nix/services/authentik/test-blueprints/example.yaml @@ -0,0 +1,11 @@ +# Minimal Authentik blueprint used by authentik_test.nix (analogous to keycloak's +# test-realms/*.json). Creates a single group so the import can be observed. +version: 1 +metadata: + name: services-flake example blueprint +entries: + - model: authentik_core.group + identifiers: + name: services-flake-example + attrs: + name: services-flake-example diff --git a/nix/services/authentik_test.nix b/nix/services/authentik_test.nix new file mode 100644 index 00000000..b73b8005 --- /dev/null +++ b/nix/services/authentik_test.nix @@ -0,0 +1,58 @@ +{ config +, pkgs +, ... +}: +let + name = "ak"; + httpPort = 9002; + ak = config.services.authentik.${name}; +in +{ + services.postgres = ak.services.postgres; + services.redis = ak.services.redis; + + services.authentik.${name} = { + enable = true; + + components = pkgs.authentik-nix; + + secretKey = "test"; + + # These match the companion instances above; shown explicitly for clarity even + # though they are also the module defaults. + settings = { + logLevel = "info"; + + listen.http = "0.0.0.0:${toString httpPort}"; + + postgres = { + port = 5433; + user = "authentik"; + name = "authentik"; + password = "authentik"; + }; + + redis.port = 6378; + + blueprints.example = { + path = ./authentik/test-blueprints/example.yaml; + }; + }; + + }; + + # Verify the server is actually serving once it reports healthy. + settings.processes.test = { + command = pkgs.writeShellApplication { + name = "${name}-test"; + runtimeInputs = [ pkgs.curl ]; + text = '' + echo "Checking authentik health endpoints..." + curl -fsS "http://localhost:${toString httpPort}/-/health/live/" + curl -fsS "http://localhost:${toString httpPort}/-/health/ready/" + echo "authentik is up." + ''; + }; + depends_on.${name}.condition = "process_healthy"; + }; +} diff --git a/nix/services/default.nix b/nix/services/default.nix index 58c82bc9..f53e44c0 100644 --- a/nix/services/default.nix +++ b/nix/services/default.nix @@ -2,44 +2,47 @@ let inherit (import ../lib.nix) multiService; in { - imports = (builtins.map multiService [ - ./apache-kafka.nix - ./azurite.nix - ./clickhouse - ./dynamodb-local.nix - ./elasticmq.nix - ./elasticsearch.nix - ./mongodb.nix - ./mysql - ./nginx - ./ollama.nix - ./postgres - ./open-webui.nix - ./plantuml.nix - ./redis-cluster.nix - ./redis.nix - ./seaweedfs.nix - ./zookeeper.nix - ./grafana.nix - ./memcached.nix - ./minio.nix - ./nats-server.nix - ./prometheus.nix - ./pgadmin.nix - ./cassandra.nix - ./pyroscope.nix - ./tempo.nix - ./weaviate.nix - ./searxng.nix - ./tika.nix - ./loki.nix - ./phpfpm.nix - ./pubsub-emulator.nix - ./qdrant.nix - ./chromadb.nix - ./neo4j.nix - ]) ++ [ - ./devshell.nix - ]; + imports = + (map multiService [ + ./apache-kafka.nix + ./azurite.nix + ./clickhouse + ./dynamodb-local.nix + ./elasticmq.nix + ./elasticsearch.nix + ./mongodb.nix + ./mysql + ./nginx + ./ollama.nix + ./postgres + ./open-webui.nix + ./plantuml.nix + ./redis-cluster.nix + ./redis.nix + ./seaweedfs.nix + ./zookeeper.nix + ./grafana.nix + ./memcached.nix + ./minio.nix + ./nats-server.nix + ./prometheus.nix + ./pgadmin.nix + ./cassandra.nix + ./pyroscope.nix + ./tempo.nix + ./weaviate.nix + ./searxng.nix + ./tika.nix + ./loki.nix + ./phpfpm.nix + ./pubsub-emulator.nix + ./qdrant.nix + ./chromadb.nix + ./neo4j.nix + ./authentik.nix + ]) + ++ [ + ./devshell.nix + ]; } diff --git a/test/flake.lock b/test/flake.lock index 122c7f3b..07a0ea70 100644 --- a/test/flake.lock +++ b/test/flake.lock @@ -1,9 +1,86 @@ { "nodes": { + "authentik-nix": { + "inputs": { + "authentik-src": "authentik-src", + "flake-compat": "flake-compat", + "flake-parts": "flake-parts", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "pyproject-build-systems": "pyproject-build-systems", + "pyproject-nix": "pyproject-nix", + "systems": "systems", + "uv2nix": "uv2nix" + }, + "locked": { + "lastModified": 1784059115, + "narHash": "sha256-HDox7X6IKv0tgURi1DoWX9NYjY/ngTOfMvlOJsEl0oI=", + "owner": "nix-community", + "repo": "authentik-nix", + "rev": "1a0767799b4be2fc6d0dcf8b77d86f5838eafbc6", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "authentik-nix", + "type": "github" + } + }, + "authentik-src": { + "flake": false, + "locked": { + "lastModified": 1783473460, + "narHash": "sha256-pGOd9+Una59JUgOcPC3PoqOqY08GkJtY+jgtk13rJ1Y=", + "owner": "goauthentik", + "repo": "authentik", + "rev": "c2942671a5b98dfa596de7bf247accb48a5c71ee", + "type": "github" + }, + "original": { + "owner": "goauthentik", + "ref": "version/2026.5.4", + "repo": "authentik", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, + "locked": { + "lastModified": 1782949081, + "narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib_2" + }, "locked": { "lastModified": 1778716662, "narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=", @@ -18,23 +95,59 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": [ + "authentik-nix", + "systems" + ] + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1778869304, - "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", - "owner": "nixos", + "lastModified": 1783776592, + "narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", + "rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3", "type": "github" }, "original": { - "owner": "nixos", - "ref": "nixpkgs-unstable", + "owner": "NixOS", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-lib": { + "locked": { + "lastModified": 1782614948, + "narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs-lib_2": { "locked": { "lastModified": 1777168982, "narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=", @@ -49,6 +162,22 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1778869304, + "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, "process-compose-flake": { "locked": { "lastModified": 1767863885, @@ -64,13 +193,64 @@ "type": "github" } }, + "pyproject-build-systems": { + "inputs": { + "nixpkgs": [ + "authentik-nix", + "nixpkgs" + ], + "pyproject-nix": [ + "authentik-nix", + "pyproject-nix" + ], + "uv2nix": [ + "authentik-nix", + "uv2nix" + ] + }, + "locked": { + "lastModified": 1782093830, + "narHash": "sha256-6gmEVe69+KlRkZD4PEEV5xAlB9CB0Y9TiuEgQjDrKTQ=", + "owner": "pyproject-nix", + "repo": "build-system-pkgs", + "rev": "430680a19bc85a3bda55f12e4cc1a1aadcf2e478", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "build-system-pkgs", + "type": "github" + } + }, + "pyproject-nix": { + "inputs": { + "nixpkgs": [ + "authentik-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1782905613, + "narHash": "sha256-SvXJcAemihifkTn4BGvyE5K1FJX9bl4U8DQ5pqKvD0s=", + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "rev": "7af23cfe91064865ecf2e835da28b45b3c6f49fd", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "type": "github" + } + }, "root": { "inputs": { - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs", + "authentik-nix": "authentik-nix", + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_2", "process-compose-flake": "process-compose-flake", "services-flake": "services-flake", - "systems": "systems" + "systems": "systems_2" } }, "services-flake": { @@ -89,6 +269,21 @@ } }, "systems": { + "locked": { + "lastModified": 1689347949, + "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=", + "owner": "nix-systems", + "repo": "default-linux", + "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default-linux", + "type": "github" + } + }, + "systems_2": { "locked": { "lastModified": 1681028828, "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", @@ -102,6 +297,31 @@ "repo": "default", "type": "github" } + }, + "uv2nix": { + "inputs": { + "nixpkgs": [ + "authentik-nix", + "nixpkgs" + ], + "pyproject-nix": [ + "authentik-nix", + "pyproject-nix" + ] + }, + "locked": { + "lastModified": 1783511944, + "narHash": "sha256-Z/Ss9rWw9QYcRK+Qqkmty7PB1pIik5XGbrtit+ad2qs=", + "owner": "pyproject-nix", + "repo": "uv2nix", + "rev": "83995ef5e4ece3c9c704aa645bbff439e15a0ac3", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "uv2nix", + "type": "github" + } } }, "root": "root", diff --git a/test/flake.nix b/test/flake.nix index bd104f51..91c18b9b 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -5,98 +5,115 @@ systems.url = "github:nix-systems/default"; process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; + authentik-nix.url = "github:nix-community/authentik-nix"; }; - outputs = inputs: + outputs = + inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } { systems = import inputs.systems; imports = [ inputs.process-compose-flake.flakeModule ./nix/pkgs.nix ]; - perSystem = { self', inputs', pkgs, system, lib, ... }: { - process-compose = - let - mkPackageFor = mod: - let - # Derive name from filename - name = lib.pipe mod [ - builtins.baseNameOf - (builtins.match "(.*)_test.nix") - builtins.head - ]; - in - lib.nameValuePair name { - imports = [ - inputs.services-flake.processComposeModules.default - mod - ]; - cli = { - options = { - # HTTP server disabled by default but we need it here for tests - no-server = false; - use-uds = true; - unix-socket = "pc-${name}.sock"; + perSystem = + { self' + , inputs' + , pkgs + , system + , lib + , ... + }: + { + process-compose = + let + mkPackageFor = + mod: + let + # Derive name from filename + name = lib.pipe mod [ + builtins.baseNameOf + (builtins.match "(.*)_test.nix") + builtins.head + ]; + in + lib.nameValuePair name { + imports = [ + inputs.services-flake.processComposeModules.default + mod + ]; + cli = { + options = { + # HTTP server disabled by default but we need it here for tests + no-server = false; + use-uds = true; + unix-socket = "pc-${name}.sock"; + }; }; }; - }; - in - builtins.listToAttrs (builtins.map mkPackageFor ([ - "${inputs.services-flake}/nix/services/apache-kafka-kraft_test.nix" - "${inputs.services-flake}/nix/services/azurite_test.nix" - "${inputs.services-flake}/nix/services/chromadb_test.nix" - "${inputs.services-flake}/nix/services/clickhouse/clickhouse_test.nix" - "${inputs.services-flake}/nix/services/dynamodb-local_test.nix" - "${inputs.services-flake}/nix/services/elasticmq_test.nix" - "${inputs.services-flake}/nix/services/grafana_test.nix" - "${inputs.services-flake}/nix/services/memcached_test.nix" - "${inputs.services-flake}/nix/services/mysql/mysql_test.nix" - "${inputs.services-flake}/nix/services/nats-server_test.nix" - "${inputs.services-flake}/nix/services/nginx/nginx_test.nix" - "${inputs.services-flake}/nix/services/ollama_test.nix" - "${inputs.services-flake}/nix/services/pgadmin_test.nix" - "${inputs.services-flake}/nix/services/plantuml_test.nix" - "${inputs.services-flake}/nix/services/postgres/postgres_test.nix" - "${inputs.services-flake}/nix/services/prometheus_test.nix" - "${inputs.services-flake}/nix/services/pubsub-emulator_test.nix" - "${inputs.services-flake}/nix/services/qdrant_test.nix" - "${inputs.services-flake}/nix/services/neo4j_test.nix" - "${inputs.services-flake}/nix/services/redis_test.nix" - "${inputs.services-flake}/nix/services/redis-cluster_test.nix" - "${inputs.services-flake}/nix/services/searxng_test.nix" - "${inputs.services-flake}/nix/services/pyroscope_test.nix" - "${inputs.services-flake}/nix/services/tempo_test.nix" - "${inputs.services-flake}/nix/services/loki_test.nix" - "${inputs.services-flake}/nix/services/tika_test.nix" - "${inputs.services-flake}/nix/services/weaviate_test.nix" - "${inputs.services-flake}/nix/services/zookeeper_test.nix" - ] ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ - # `phpfpm` test fails on aarch64-darwin: - # [phpfpm1 ] [28-Jul-2025 13:05:47.512506] DEBUG: pid 90757, fpm_stdio_save_original_stderr(), line 81: saving original STDERR fd: dup() - # [phpfpm1 ] [28-Jul-2025 13:05:47.512606] ERROR: pid 90757, fpm_stdio_open_error_log(), line 386: failed to open error_log (/proc/self/fd/2): No such file or directory (2) - # [phpfpm1 ] [28-Jul-2025 13:05:47.512647] ERROR: pid 90757, fpm_conf_init_main(), line 1882: failed to post process the configuration - # [phpfpm1 ] [28-Jul-2025 13:05:47.512661] ERROR: pid 90757, fpm_init(), line 72: FPM initialization failed - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to open error_log (/proc/self/fd/2): No such file or directory (2) - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to post process the configuration - # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: FPM initialization failed - "${inputs.services-flake}/nix/services/phpfpm_test.nix" - # Fails on macOS with: `error: chmod '"/nix/store/rcx3n94ygmd61rrv2p22sykhk0yx49n4-elasticsearch-7.17.16/modules/x-pack-ml/platform/darwin-aarch64/controller.app"': Operation not permitted` - # Related: https://github.com/NixOS/nix/issues/6765 - "${inputs.services-flake}/nix/services/elasticsearch_test.nix" - # error: Refusing to evaluate package 'postgresql-test-hook' in /nix/store/cqzw8bdv3bjjrvhln6nhc5hk2y0sxqs8-source/pkgs/by-name/po/postgresqlTestHook/package.nix:8 because it is not available on the requested hostPlatform: - # hostPlatform.system = "aarch64-darwin" - # package.meta.platforms = [ ] - # package.meta.badPlatforms = [ - # "x86_64-darwin" - # "aarch64-darwin" - # ] - "${inputs.services-flake}/nix/services/open-webui_test.nix" - "${inputs.services-flake}/nix/services/seaweedfs_test.nix" # Darwin build fixed in https://github.com/NixOS/nixpkgs/pull/534897 - ] - # Tests on non-linux host only - ++ lib.optionals (!pkgs.stdenv.hostPlatform.isLinux) [ - # Fails on Linux due to Nix's build sandbox constraints, see https://github.com/NixOS/nixpkgs/issues/377016#issuecomment-2614610914 - "${inputs.services-flake}/nix/services/mongodb_test.nix" - ])); - }; + in + builtins.listToAttrs ( + builtins.map mkPackageFor ( + [ + "${inputs.services-flake}/nix/services/apache-kafka-kraft_test.nix" + "${inputs.services-flake}/nix/services/azurite_test.nix" + "${inputs.services-flake}/nix/services/chromadb_test.nix" + "${inputs.services-flake}/nix/services/clickhouse/clickhouse_test.nix" + "${inputs.services-flake}/nix/services/dynamodb-local_test.nix" + "${inputs.services-flake}/nix/services/elasticmq_test.nix" + "${inputs.services-flake}/nix/services/grafana_test.nix" + "${inputs.services-flake}/nix/services/memcached_test.nix" + "${inputs.services-flake}/nix/services/mysql/mysql_test.nix" + "${inputs.services-flake}/nix/services/nats-server_test.nix" + "${inputs.services-flake}/nix/services/nginx/nginx_test.nix" + "${inputs.services-flake}/nix/services/ollama_test.nix" + "${inputs.services-flake}/nix/services/pgadmin_test.nix" + "${inputs.services-flake}/nix/services/plantuml_test.nix" + "${inputs.services-flake}/nix/services/postgres/postgres_test.nix" + "${inputs.services-flake}/nix/services/prometheus_test.nix" + "${inputs.services-flake}/nix/services/pubsub-emulator_test.nix" + "${inputs.services-flake}/nix/services/qdrant_test.nix" + "${inputs.services-flake}/nix/services/neo4j_test.nix" + "${inputs.services-flake}/nix/services/redis_test.nix" + "${inputs.services-flake}/nix/services/redis-cluster_test.nix" + "${inputs.services-flake}/nix/services/searxng_test.nix" + "${inputs.services-flake}/nix/services/pyroscope_test.nix" + "${inputs.services-flake}/nix/services/tempo_test.nix" + "${inputs.services-flake}/nix/services/loki_test.nix" + "${inputs.services-flake}/nix/services/tika_test.nix" + "${inputs.services-flake}/nix/services/weaviate_test.nix" + "${inputs.services-flake}/nix/services/zookeeper_test.nix" + "${inputs.services-flake}/nix/services/authentik_test.nix" + ] + ++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [ + # `phpfpm` test fails on aarch64-darwin: + # [phpfpm1 ] [28-Jul-2025 13:05:47.512506] DEBUG: pid 90757, fpm_stdio_save_original_stderr(), line 81: saving original STDERR fd: dup() + # [phpfpm1 ] [28-Jul-2025 13:05:47.512606] ERROR: pid 90757, fpm_stdio_open_error_log(), line 386: failed to open error_log (/proc/self/fd/2): No such file or directory (2) + # [phpfpm1 ] [28-Jul-2025 13:05:47.512647] ERROR: pid 90757, fpm_conf_init_main(), line 1882: failed to post process the configuration + # [phpfpm1 ] [28-Jul-2025 13:05:47.512661] ERROR: pid 90757, fpm_init(), line 72: FPM initialization failed + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to open error_log (/proc/self/fd/2): No such file or directory (2) + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: failed to post process the configuration + # [phpfpm2 ] [28-Jul-2025 13:05:47] ERROR: FPM initialization failed + "${inputs.services-flake}/nix/services/phpfpm_test.nix" + # Fails on macOS with: `error: chmod '"/nix/store/rcx3n94ygmd61rrv2p22sykhk0yx49n4-elasticsearch-7.17.16/modules/x-pack-ml/platform/darwin-aarch64/controller.app"': Operation not permitted` + # Related: https://github.com/NixOS/nix/issues/6765 + "${inputs.services-flake}/nix/services/elasticsearch_test.nix" + # error: Refusing to evaluate package 'postgresql-test-hook' in /nix/store/cqzw8bdv3bjjrvhln6nhc5hk2y0sxqs8-source/pkgs/by-name/po/postgresqlTestHook/package.nix:8 because it is not available on the requested hostPlatform: + # hostPlatform.system = "aarch64-darwin" + # package.meta.platforms = [ ] + # package.meta.badPlatforms = [ + # "x86_64-darwin" + # "aarch64-darwin" + # ] + "${inputs.services-flake}/nix/services/open-webui_test.nix" + "${inputs.services-flake}/nix/services/seaweedfs_test.nix" # Darwin build fixed in https://github.com/NixOS/nixpkgs/pull/534897 + ] + # Tests on non-linux host only + ++ lib.optionals (!pkgs.stdenv.hostPlatform.isLinux) [ + # Fails on Linux due to Nix's build sandbox constraints, see https://github.com/NixOS/nixpkgs/issues/377016#issuecomment-2614610914 + "${inputs.services-flake}/nix/services/mongodb_test.nix" + ] + ) + ); + }; }; } diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index 163a2661..8741b4b1 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -1,25 +1,40 @@ { inputs, ... }: { - perSystem = { self', inputs', pkgs, system, lib, ... }: { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; + perSystem = + { self' + , inputs' + , pkgs + , system + , lib + , ... + }: + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; - # Required for elastic search - config.allowUnfree = true; + # Required for elastic search + config.allowUnfree = true; - overlays = [ - (self: super: lib.optionalAttrs super.stdenv.isDarwin { + overlays = [ + ( + self: super: + lib.optionalAttrs super.stdenv.isDarwin + { - # Disable tests, because they are failing on darwin: - # https://github.com/NixOS/nixpkgs/issues/281214 - pgadmin4 = super.pgadmin4.overrideAttrs (_: { - doInstallCheck = - false; - }); + # Disable tests, because they are failing on darwin: + # https://github.com/NixOS/nixpkgs/issues/281214 + pgadmin4 = super.pgadmin4.overrideAttrs (_: { + doInstallCheck = false; + }); - }) - ]; + } + // { + # Add authentik packages cause its not in nixpkgs. + authentik-nix = inputs'.authentik-nix.packages; + } + ) + ]; + }; }; - }; } From 492e28b9f034e99409a7da44b5e7ee35ca4ad10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Fri, 17 Jul 2026 15:55:31 +0200 Subject: [PATCH 02/16] chore: obsolete comments --- nix/services/authentik_test.nix | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/nix/services/authentik_test.nix b/nix/services/authentik_test.nix index b73b8005..2d007e2f 100644 --- a/nix/services/authentik_test.nix +++ b/nix/services/authentik_test.nix @@ -1,6 +1,7 @@ -{ config -, pkgs -, ... +{ + config, + pkgs, + ... }: let name = "ak"; @@ -18,8 +19,6 @@ in secretKey = "test"; - # These match the companion instances above; shown explicitly for clarity even - # though they are also the module defaults. settings = { logLevel = "info"; @@ -41,7 +40,6 @@ in }; - # Verify the server is actually serving once it reports healthy. settings.processes.test = { command = pkgs.writeShellApplication { name = "${name}-test"; From 621b40673c2837622be4c79845dbc1b813c5c622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Fri, 17 Jul 2026 18:30:05 +0200 Subject: [PATCH 03/16] fix: authentic to released version --- test/flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/flake.nix b/test/flake.nix index 91c18b9b..4f5f7a94 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -5,7 +5,7 @@ systems.url = "github:nix-systems/default"; process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; - authentik-nix.url = "github:nix-community/authentik-nix"; + authentik-nix.url = "github:nix-community/authentik-nix/version/2026.5.4"; }; outputs = inputs: From e903c9e34df2e9025f3aad39dd6b98b3ce8d0d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Sun, 19 Jul 2026 20:25:02 +0200 Subject: [PATCH 04/16] fix: improve startup and orchestration --- nix/services/authentik/options.nix | 252 +++++++------------ nix/services/authentik/service.nix | 386 +++++++++++++++++------------ nix/services/authentik_test.nix | 40 ++- test/flake.nix | 2 +- test/nix/pkgs.nix | 2 +- 5 files changed, 337 insertions(+), 345 deletions(-) diff --git a/nix/services/authentik/options.nix b/nix/services/authentik/options.nix index a0fd0cb3..69555b74 100644 --- a/nix/services/authentik/options.nix +++ b/nix/services/authentik/options.nix @@ -1,4 +1,6 @@ { lib +, pkgs +, name , ... }: @@ -8,34 +10,29 @@ let types ; - # A relative user-provided path, or a Nix store path (same pattern as keycloak realms). - blueprintPath = types.nullOr ( - types.either - (types.pathWith { - inStore = false; - absolute = false; - }) - (types.pathWith { inStore = true; }) - ); + settingsFormat = pkgs.formats.yaml { }; + + hostAndPort = name: port: { + host = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Host of the ${name}."; + }; + port = mkOption { + type = types.number; + default = port; + description = "Port of the ${name}."; + }; + }; in { options = { - # Authentik is not in nixpkgs, so the package set has to be provided by the user - # from their own `authentik-nix` flake input. This keeps services-flake input-free. components = mkOption { - type = types.attrsOf types.package; - example = lib.literalExpression "inputs.authentik-nix.packages.\${system}"; + type = types.raw; + example = lib.literalExpression "inputs.authentik-nix.legacyPackages.\${system}.authentikComponents"; description = '' - The Authentik component packages, typically - `inputs.authentik-nix.packages.''${system}`. - - Must provide at least the following attributes: - - `gopkgs` – provides `bin/server` - - `rust` – provides `bin/authentik` (the `worker` subcommand) - - `migrate` – provides `bin/migrate.py` - - `staticWorkdirDeps` – the working-directory dependencies - (`authentik/`, `blueprints/`, `templates/`, static assets) - - `manage` – the management CLI (optional, for blueprint tooling) + The Authentik component set, typically + `inputs.authentik-nix.legacyPackages.''${system}.authentikComponents`. ''; }; @@ -94,172 +91,97 @@ in The config to easily define the needed postgres process. ''; }; - - redis = mkOption { - type = types.attrsOf types.raw; - readOnly = true; - description = '' - The config to easily define the needed redis process. - ''; - }; }; - settings = mkOption { - type = types.submodule { - options = { - logLevel = mkOption { - type = types.str; - default = "info"; - example = "debug"; - description = "Authentik log level (`log_level`)."; - }; - - listen = { - http = mkOption { - type = types.str; - default = "0.0.0.0:9000"; - description = "Address the HTTP server listens on (`listen.listen_http`)."; - }; - - https = mkOption { - type = types.str; - default = "0.0.0.0:9443"; - description = "Address the HTTPS server listens on (`listen.listen_https`)."; - }; - }; + logLevel = mkOption { + type = types.str; + default = "info"; + example = "debug"; + description = "Authentik log level."; + }; - email = { - host = mkOption { - type = types.str; - default = "localhost"; - description = "Email SMTP server host name."; - }; + server = { + http = hostAndPort "server endpoint" 9000; + https = hostAndPort "server endpoint" 9443; + metrics = hostAndPort "server metrics endpoint." 9300; + }; - port = mkOption { - type = types.str; - default = "localhost"; - description = "Email SMTP server port."; - }; - }; + worker = { + http = hostAndPort "worker endpoint" 9001; + metrics = hostAndPort "worker metrics endpoint." 9302; + }; - postgres = { - host = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "PostgreSQL host (`postgresql.host`)."; - }; + email = hostAndPort "${name}'s email connection" 25; - port = mkOption { - type = types.port; - default = 5432; - description = "PostgreSQL port (`postgresql.port`)."; - }; + postgres = (hostAndPort "${name}'s postgres DB" 5432) // { + name = mkOption { + type = types.str; + default = "authentik"; + description = "PostgreSQL database name (`postgresql.name`)."; + }; - name = mkOption { - type = types.str; - default = "authentik"; - description = "PostgreSQL database name (`postgresql.name`)."; - }; + user = mkOption { + type = types.str; + default = "authentik"; + description = "PostgreSQL user (`postgresql.user`)."; + }; - user = mkOption { - type = types.str; - default = "authentik"; - description = "PostgreSQL user (`postgresql.user`)."; - }; + password = mkOption { + type = types.str; + default = "authentik"; + description = '' + PostgreSQL password (`postgresql.password`). Written to the config + file in the Nix store; for non-dev use, override it via + {option}`environmentFile` (`AUTHENTIK_POSTGRESQL__PASSWORD`). + ''; + }; + }; - password = mkOption { - type = types.str; - default = "authentik"; + blueprints = mkOption { + default = { }; + type = types.attrsOf ( + types.submodule { + options = { + path = mkOption { + type = types.pathWith { + inStore = true; + }; + default = null; description = '' - PostgreSQL password (`postgresql.password`). Written to the config - file in the Nix store; for non-dev use, override it via - {option}`environmentFile` (`AUTHENTIK_POSTGRESQL__PASSWORD`). + Path of a blueprint YAML file to make available for import. ''; }; - }; - - redis = { - host = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "Redis host (`redis.host`)."; - }; - port = mkOption { - type = types.port; - default = 6379; - description = "Redis port (`redis.port`)."; + import = mkOption { + type = types.bool; + default = true; + description = "Whether to make this blueprint available for import."; }; }; - - blueprints = mkOption { - default = { }; - type = types.attrsOf ( - types.submodule { - options = { - path = mkOption { - type = blueprintPath; - default = null; - example = "./blueprints/my-blueprint.yaml"; - description = '' - Path (relative to the `process-compose` working dir, or a Nix store - path) of a blueprint YAML file to make available for import. - ''; - }; - - import = mkOption { - type = types.bool; - default = true; - description = "Whether to make this blueprint available for import."; - }; - }; - } - ); - - example = lib.literalExpression '' - { - my-app = { - path = ./blueprints/my-app.yaml; - }; - } - ''; - - description = '' - Blueprints to import on start up. - Enabled blueprints are copied into the blueprints directoryr and - auto-applied by the Authentik worker. - ''; - }; - }; - }; - - default = { }; + } + ); example = lib.literalExpression '' { - log_level = "debug"; - listen.listen_http = "0.0.0.0:9002"; - postgresql.host = "127.0.0.1"; - email = { - host = "localhost"; - port = 25; + my-app = { + path = ./blueprints/my-app.yaml; }; } ''; description = '' - Authentik configuration, rendered to a `local.yml` file that Authentik loads - from its working directory. Corresponds to the keys documented at - . - - Authentik config is hierarchical YAML, so allow arbitrary nested keys. - It is not well documented. - See the default: - - The defaults point PostgreSQL/Redis at the companion - `services.postgres."authentik-db-pg"` and - `services.redis."authentik-db-redis"` instances used in the example and test. + Blueprints to import on start up. + Enabled blueprints are copied into the blueprints directoryr and + auto-applied by the Authentik worker. ''; }; + + settings = mkOption { + description = "YAML option for authentic which are merged with '/authentic/lib/default.yml'."; + type = types.submodule { + freeformType = settingsFormat.type; + options = { }; + }; + }; }; } diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index 723b0fcc..63871c37 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -5,63 +5,118 @@ , ... }: let - # The authentik components. - gopkgs = requireComponent "gopkgs"; - rust = requireComponent "rust"; - rawMigrate = requireComponent "migrate"; - pythonEnv = requireComponent "pythonEnv"; - staticWorkdirDeps = requireComponent "staticWorkdirDeps"; - - requireComponent = - attr: - if config.components ? ${attr} then - config.components.${attr} - else - throw '' - services.authentik.${name}.authentikComponents is missing the `${attr}` attribute. - (see the module docs). - ''; + cfg = config; + dataDir = cfg.dataDir; + + settingsFormat = pkgs.formats.yaml { }; # NOTE: Workaround -> authentik-nix's `migrate` ships `bin/migrate.py`, a shell wrapper that execs an inner # `bin/.migrate.py-wrapped` whose shebang is `#!/usr/bin/env python`. # `/usr/bin/env` does not exist in the Nix build sandbox that # `nix build .#checks…` (`just test`) runs the process-compose stack in, # so the migration crashes with "bad interpreter". - migrate = pkgs.runCommand "authentik-migrate-sandbox-safe" { } '' - cp -R --no-preserve=mode,ownership ${rawMigrate} $out - substituteInPlace $out/bin/.migrate.py-wrapped \ - --replace-fail '#!/usr/bin/env python' '#!${pythonEnv}/bin/python' - substituteInPlace $out/bin/migrate.py \ - --replace-fail '${rawMigrate}/bin/.migrate.py-wrapped' "$out/bin/.migrate.py-wrapped" - chmod +x "$out/bin/.migrate.py-wrapped" "$out/bin/migrate.py" - ''; - - # HTTP port for the readiness probe, parsed from `listen.listen_http` (e.g. "0.0.0.0:9000"). - httpPort = lib.last (lib.splitString ":" config.settings.listen.http); - - # NOTE: We would rather set a config.yaml, - # but documentation is pretty bad and not sufficient for certain stuff - # Therefore we set env variables as described. - connEnv = { - AUTHENTIK_POSTGRESQL__HOST = config.settings.postgres.host; - AUTHENTIK_POSTGRESQL__PORT = toString config.settings.postgres.port; - AUTHENTIK_POSTGRESQL__NAME = config.settings.postgres.name; - AUTHENTIK_POSTGRESQL__USER = config.settings.postgres.user; - AUTHENTIK_POSTGRESQL__PASSWORD = config.settings.postgres.password; - - AUTHENTIK_REDIS__HOST = config.settings.redis.host; - AUTHENTIK_REDIS__PORT = toString config.settings.redis.port; - - AUTHENTIK_LISTEN__HTTP = config.settings.listen.http; - AUTHENTIK_LISTEN__HTTPS = config.settings.listen.https; - AUTHENTIK_LOG_LEVEL = config.settings.logLevel; - }; + modMigrate = + prev: pythonEnv: + pkgs.runCommand "authentik-migrate-sandbox-safe" { } + # Bash + '' + cp -R --no-preserve=mode,ownership ${prev} $out - baseEnv = connEnv // { - AUTHENTIK_SECRET_KEY = config.secretKey; - AUTHENTIK_BOOTSTRAP_PASSWORD = config.initialAdminPassword; - AUTHENTIK_BOOTSTRAP_EMAIL = config.initialAdminEmail; - }; + substituteInPlace $out/bin/.migrate.py-wrapped \ + --replace-fail '#!/usr/bin/env python' '#!${pythonEnv}/bin/python' + substituteInPlace $out/bin/migrate.py \ + --replace-fail '${prev}/bin/.migrate.py-wrapped' "$out/bin/.migrate.py-wrapped" + chmod +x "$out/bin/.migrate.py-wrapped" "$out/bin/migrate.py" + ''; + + settingsFile = settingsFormat.generate "authentik.yml" cfg.settings; + + modStaticWorkdirDeps = + prev: authentik-src: + prev.overrideAttrs (oA: { + buildCommand = + oA.buildCommand + + + # Bash + '' + rm -v $out/authentik + cp -r --no-preserve=mode,ownership ${authentik-src}/authentik $out/authentik + src="authentik/lib/default.yml" + echo "Merging settings file into '$src'." + ${lib.getExe pkgs.yq-go} eval-all '. as $item ireduce ({}; . *+ $item)' \ + "${authentik-src}/$src" "${settingsFile}" > "$out/$src" + + # Set blueprints_dir, template_dir. + ${lib.getExe pkgs.yq-go} -i ".blueprints_dir = \"$out/blueprints\"" "$out/$src" + ${lib.getExe pkgs.yq-go} -i ".templates_dir = \"$out/templates\"" "$out/$src" + + cat "$src" | grep -v "/blueprints" || { + echo "Blueprints directory must not anymore point to /blueprints." + exit 1 + } + chmod -R -w $out/authentik + + echo "Placing blueprints." + rm -v $out/blueprints + cp -vr ${authentik-src}/blueprints $out/blueprints + cd "$out" + ${lib.concatStringsSep "\n" blueprintImport} + ''; + }); + + finalComponents = cfg.components.overrideScope ( + final: prev: + let + prevComps = prev.authentikComponents; + in + { + authentikComponents = prevComps // { + migrate = modMigrate prevComps.migrate prevComps.pythonEnv; + staticWorkdirDeps = modStaticWorkdirDeps prevComps.staticWorkdirDeps prev.authentik-src; + }; + } + ); + + # The authentik components. + inherit (finalComponents.authentikComponents) gopkgs; + inherit (finalComponents.authentikComponents) rust; + inherit (finalComponents.authentikComponents) migrate; + inherit (finalComponents.authentikComponents) pythonEnv; + inherit (finalComponents.authentikComponents) staticWorkdirDeps; + + connEnv = + assert allPortsUnique; + { + AUTHENTIK_SECRET_KEY = cfg.secretKey; + AUTHENTIK_BOOTSTRAP_PASSWORD = cfg.initialAdminPassword; + AUTHENTIK_BOOTSTRAP_EMAIL = cfg.initialAdminEmail; + }; + + allPortsUnique = + let + allPorts = [ + cfg.server.http.port + cfg.server.https.port + cfg.server.metrics.port + + cfg.worker.http.port + cfg.worker.metrics.port + ]; + in + lib.assertMsg (lib.allUnique allPorts) "Some ports in `authentik.server`, `authentik.worker` are identical: ${toString allPorts}."; + + listenEnv = + type: + let + c = cfg.${type}; + in + { + AUTHENTIK_LISTEN__HTTP = "${c.http.host}:${toString c.http.port}"; + AUTHENTIK_LISTEN__METRICS = "${c.metrics.host}:${toString c.metrics.port}"; + } + // lib.optionalAttrs (c ? https) { + AUTHENTIK_LISTEN__HTTPS = "${c.https.host}:${toString c.https.port}"; + }; # Copy the enabled blueprints into the Authentik blueprints dir. # Copied (not symlinked) for the same reason as the built-in @@ -71,71 +126,76 @@ let bp: e: # Bash '' - f=$(realpath "${e.path}") - echo "Copying blueprint '${bp}' from '$f' into './blueprints'." - if [ ! -f "$f" ]; then - echo "Blueprint file '$f' does not exist!" >&2 - exit 1 - fi - cp -L "$f" "./blueprints/" - chmod u+w "./blueprints/$(basename "$f")" - unset f + filename="${bp}.yaml" + f="${e.path}" + echo "Copying blueprint '${bp}' from '$f' into './additional/${bp}'." + mkdir -p ./additional + cp -L "$f" "./additional/${bp}" '' ) - (lib.filterAttrs (_: v: v.import && v.path != null) config.settings.blueprints); - - loadEnvFile = lib.optionalString (config.environmentFile != null) '' - echo "Load extra authentic environment file '${config.environmentFile}'." - set -a - # shellcheck disable=SC1091 - . "${config.environmentFile}" - set +a - ''; - - runtimeEnv = '' - dataDir="$(realpath ${config.dataDir})" - echo "Authentik data dir: '$dataDir'." - - mkdir -p "$dataDir/media" "$dataDir/certs" "$dataDir/prometheus" - - export AUTHENTIK_STORAGE__FILE__PATH="$dataDir" - export AUTHENTIK_STORAGE__MEDIA__FILE__PATH="$dataDir/media" - export AUTHENTIK_CERT_DISCOVERY_DIR="$dataDir/certs" - export AUTHENTIK_BLUEPRINTS_DIR="$dataDir/blueprints" - export PROMETHEUS_MULTIPROC_DIR="$dataDir/prometheus" - export AUTHENTIK_TEMPLATE_DIR="$dataDir/templates"; - - cd "$dataDir" - ''; - - setup = '' - set -euo pipefail - ${runtimeEnv} - ${loadEnvFile} - - echo "Working dir: $(pwd)" - - # Bring in Authentik's working-directory dependencies (authentik/, templates/, static - # assets, ...) but manage `blueprints/` ourselves so we can add user blueprints. - for dep in ${staticWorkdirDeps}/*; do - base=$(basename "$dep") - [ "$base" = "blueprints" ] && continue - - echo "Symlinking '$dep' -> '$base'" - ln -sfn "$dep" "./$base" - done - - # Combined blueprints dir: Authentik's built-in blueprints + user-provided ones. - # These must be COPIED (not symlinked): A symlink into the nix store resolves - # outside the dir and is rejected, which crashes the server's bootstrap worker. - rm -rf ./blueprints - mkdir -p ./blueprints - if [ -d "${staticWorkdirDeps}/blueprints" ]; then - cp -rL "${staticWorkdirDeps}/blueprints/." ./blueprints/ - fi - chmod -R u+w ./blueprints - ${builtins.concatStringsSep "\n" blueprintImport} - ''; + (lib.filterAttrs (_: v: v.import && v.path != null) cfg.blueprints); + + loadEnvFile = + lib.optionalString (cfg.environmentFile != null) + # Bash + '' + echo "Load extra authentic environment file '${cfg.environmentFile}'." + set -a + # shellcheck disable=SC1091 + . "${cfg.environmentFile}" + set +a + ''; + + runtimeEnv = + # Bash + '' + dataDir="$(realpath ${dataDir})" + staticDir="$dataDir/static" + + echo "Authentik data dir: '$dataDir'." + echo "Authentik static dir: '$staticDir'." + + mkdir -p "$dataDir/data" \ + "$dataDir/media" \ + "$dataDir/certs" \ + "$dataDir/prometheus" + + # Set temp. dir. + if [ ! -L "$dataDir/temp" ]; then + tmpDir=$(mktemp -d) + mkdir -p "$tmpDir" + ln -s "$tmpDir" "$dataDir/temp" + fi + export TMPDIR="$dataDir/temp" + export TEMPDIR="$TMPDIR" + + export PROMETHEUS_MULTIPROC_DIR="$dataDir/prometheus" + + export PATH="${pythonEnv}/bin:$PATH" + + # Bring in Authentik's working-directory dependencies + # (authentik/, templates/, static assets, ...). + if [ ! -d "$staticDir" ]; then + ln -s "${staticWorkdirDeps}" "$staticDir" + fi + ls -al "$staticDir" + + echo "Settings file '$staticDir/authentik/lib/default.yml':" + echo "=====================" + cat "$staticDir/authentik/lib/default.yml" + echo "=====================" + + cd "$staticDir" + echo "Working dir: $(pwd)" + ''; + + setup = + # Bash + '' + set -euo pipefail + ${runtimeEnv} + ${loadEnvFile} + ''; authentik-migrate = pkgs.writeShellScriptBin "authentik-migrate" @@ -150,8 +210,7 @@ let pkgs.writeShellScriptBin "authentik-worker" # Bash '' - ${runtimeEnv} - ${loadEnvFile} + ${setup} echo "Starting authentik worker ..." exec ${rust}/bin/authentik worker ''; @@ -160,8 +219,7 @@ let pkgs.writeShellScriptBin "authentik-server" # Bash '' - ${runtimeEnv} - ${loadEnvFile} + ${setup} echo "Starting authentik server ..." exec ${gopkgs}/bin/server ''; @@ -170,7 +228,7 @@ let pkgs.writeShellScriptBin "authentik-health" # Bash '' - ${pkgs.curl}/bin/curl -fsS "http://localhost:${httpPort}/-/health/ready/" + ${pkgs.curl}/bin/curl -fsS "http://${cfg.server.http.host}:${toString cfg.server.http.port}/-/health/ready/" ''; in { @@ -178,59 +236,75 @@ in postgres = { "${name}-pg-db" = { enable = true; - port = config.settings.postgres.port; + port = cfg.postgres.port; initialScript.before = '' - CREATE USER \"${config.settings.postgres.user}\" WITH PASSWORD '${config.settings.postgres.password}' CREATEDB; - CREATE DATABASE \"${config.settings.postgres.name}\" OWNER \"${config.settings.postgres.user}\" + CREATE USER \"${cfg.postgres.user}\" WITH PASSWORD '${cfg.postgres.password}' CREATEDB; + CREATE DATABASE \"${cfg.postgres.name}\" OWNER \"${cfg.postgres.user}\" ''; }; }; + }; + + settings = { + log_level = lib.mkDefault cfg.logLevel; + + cert_discovery_dir = lib.mkDefault "${dataDir}/certs"; + + postgresql = { + user = lib.mkDefault cfg.postgres.user; + name = lib.mkDefault cfg.postgres.name; + host = lib.mkDefault cfg.postgres.host; + port = lib.mkDefault cfg.postgres.port; + }; - redis = { - "${name}-redis-db" = { - enable = true; - port = config.settings.redis.port; + storage = { + file = lib.mkDefault { + path = "${dataDir}/data"; + }; + + media = { + backend = lib.mkDefault "file"; + file = lib.mkDefault { + path = "${dataDir}/media"; + }; }; }; }; - outputs = { - settings.processes = { - "${name}-migrate" = { - description = "Authentik database migrations: a prerequisite that creates the schema."; - environment = baseEnv; - command = "${lib.getExe authentik-migrate}"; + outputs.settings.processes = lib.mkIf cfg.enable { + "${name}-migrate" = { + description = "Authentik database migrations: a prerequisite that creates the schema."; + environment = connEnv; + command = "${lib.getExe authentik-migrate}"; - depends_on = { - "${name}-pg-db".condition = "process_healthy"; - "${name}-redis-db".condition = "process_healthy"; - }; + depends_on = { + "${name}-pg-db".condition = "process_healthy"; }; + }; - "${name}-worker" = { - description = "Authentik background worker: processes tasks and applies blueprints."; - environment = baseEnv; - command = "${lib.getExe authentik-worker}"; - depends_on."${name}-migrate".condition = "process_completed_successfully"; - }; + "${name}-worker" = { + description = "Authentik background worker: processes tasks and applies blueprints."; + environment = connEnv // listenEnv "worker"; + command = "${lib.getExe authentik-worker}"; + depends_on."${name}-migrate".condition = "process_completed_successfully"; + }; - "${name}" = { - description = "Authentik HTTP/API server."; - environment = baseEnv; - command = "${lib.getExe authentik-server}"; + "${name}" = { + description = "Authentik HTTP/API server."; + environment = connEnv // listenEnv "server"; + command = "${lib.getExe authentik-server}"; - depends_on = { - "${name}-migrate".condition = "process_completed_successfully"; - "${name}-worker".condition = "process_started"; - }; + depends_on = { + "${name}-migrate".condition = "process_completed_successfully"; + "${name}-worker".condition = "process_started"; + }; - readiness_probe = { - exec.command = "${lib.getExe authentik-health}"; - initial_delay_seconds = 20; - period_seconds = 5; - timeout_seconds = 4; - failure_threshold = 30; - }; + readiness_probe = { + exec.command = "${lib.getExe authentik-health}"; + initial_delay_seconds = 20; + period_seconds = 5; + timeout_seconds = 4; + failure_threshold = 30; }; }; }; diff --git a/nix/services/authentik_test.nix b/nix/services/authentik_test.nix index 2d007e2f..a3240914 100644 --- a/nix/services/authentik_test.nix +++ b/nix/services/authentik_test.nix @@ -1,43 +1,39 @@ -{ - config, - pkgs, - ... +{ config +, pkgs +, ... }: let name = "ak"; - httpPort = 9002; + httpPort = 9001; ak = config.services.authentik.${name}; in { services.postgres = ak.services.postgres; - services.redis = ak.services.redis; services.authentik.${name} = { enable = true; - components = pkgs.authentik-nix; + components = pkgs.authentikComponents; secretKey = "test"; - settings = { - logLevel = "info"; - - listen.http = "0.0.0.0:${toString httpPort}"; - - postgres = { - port = 5433; - user = "authentik"; - name = "authentik"; - password = "authentik"; - }; + postgres = { + port = 5433; + user = "authentik"; + name = "authentik"; + password = "authentik"; + }; - redis.port = 6378; + server.http.port = 9001; + worker.http.port = 9002; - blueprints.example = { - path = ./authentik/test-blueprints/example.yaml; - }; + blueprints.example = { + path = ./authentik/test-blueprints/example.yaml; }; + settings = { + logLevel = "info"; + }; }; settings.processes.test = { diff --git a/test/flake.nix b/test/flake.nix index 4f5f7a94..b3f8df73 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -5,7 +5,7 @@ systems.url = "github:nix-systems/default"; process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; - authentik-nix.url = "github:nix-community/authentik-nix/version/2026.5.4"; + authentik-nix.url = "github:gabyx/authentik-nix/version/2026.5.4"; }; outputs = inputs: diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index 8741b4b1..5247f9f3 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -31,7 +31,7 @@ } // { # Add authentik packages cause its not in nixpkgs. - authentik-nix = inputs'.authentik-nix.packages; + authentikComponents = inputs'.authentik-nix.legacyPackages.authentikComponents; } ) ]; From 054ae1aab5527141261dbe0598326c4c7e3ec316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Mon, 20 Jul 2026 11:31:02 +0200 Subject: [PATCH 05/16] fix: add enable option to postgres service --- nix/services/authentik/service.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index 63871c37..3f5a25e4 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -235,7 +235,7 @@ in services = { postgres = { "${name}-pg-db" = { - enable = true; + enable = cfg.enable; port = cfg.postgres.port; initialScript.before = '' CREATE USER \"${cfg.postgres.user}\" WITH PASSWORD '${cfg.postgres.password}' CREATEDB; From d924ec72f05067aaba74836544544278a1234c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Tue, 21 Jul 2026 10:29:52 +0200 Subject: [PATCH 06/16] fix: blueprint import --- nix/services/authentik/service.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index 3f5a25e4..fb48a7da 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -58,8 +58,8 @@ let echo "Placing blueprints." rm -v $out/blueprints - cp -vr ${authentik-src}/blueprints $out/blueprints - cd "$out" + cp -vr --no-preserve=mode,ownership ${authentik-src}/blueprints $out/blueprints + cd "$out/blueprints" ${lib.concatStringsSep "\n" blueprintImport} ''; }); From 5fde5b4b0ce1bbc63c58787b19358b905c89dd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Tue, 21 Jul 2026 10:34:16 +0200 Subject: [PATCH 07/16] docs: add docs entry --- doc/services.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/services.md b/doc/services.md index 00a499bf..840ca41c 100644 --- a/doc/services.md +++ b/doc/services.md @@ -5,6 +5,7 @@ short-title: Services # Supported services - [[apache-kafka]]# +- [[authentik]]# - [[azurite]]# - [[cassandra]]# - [[chromadb]]# From a59d0c6b94d817bcf175a84917514c8621c0f578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Tue, 21 Jul 2026 10:46:50 +0200 Subject: [PATCH 08/16] fix: change to port type --- nix/services/authentik/options.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/services/authentik/options.nix b/nix/services/authentik/options.nix index 69555b74..6e19fcc4 100644 --- a/nix/services/authentik/options.nix +++ b/nix/services/authentik/options.nix @@ -19,7 +19,7 @@ let description = "Host of the ${name}."; }; port = mkOption { - type = types.number; + type = types.port; default = port; description = "Port of the ${name}."; }; From 3b6b7407bd40593b3a79f0d5be3ce456a5112f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Tue, 21 Jul 2026 10:56:04 +0200 Subject: [PATCH 09/16] fix: revert format --- nix/services/default.nix | 83 ++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/nix/services/default.nix b/nix/services/default.nix index f53e44c0..7c7c5465 100644 --- a/nix/services/default.nix +++ b/nix/services/default.nix @@ -2,47 +2,46 @@ let inherit (import ../lib.nix) multiService; in { - imports = - (map multiService [ - ./apache-kafka.nix - ./azurite.nix - ./clickhouse - ./dynamodb-local.nix - ./elasticmq.nix - ./elasticsearch.nix - ./mongodb.nix - ./mysql - ./nginx - ./ollama.nix - ./postgres - ./open-webui.nix - ./plantuml.nix - ./redis-cluster.nix - ./redis.nix - ./seaweedfs.nix - ./zookeeper.nix - ./grafana.nix - ./memcached.nix - ./minio.nix - ./nats-server.nix - ./prometheus.nix - ./pgadmin.nix - ./cassandra.nix - ./pyroscope.nix - ./tempo.nix - ./weaviate.nix - ./searxng.nix - ./tika.nix - ./loki.nix - ./phpfpm.nix - ./pubsub-emulator.nix - ./qdrant.nix - ./chromadb.nix - ./neo4j.nix - ./authentik.nix - ]) - ++ [ - ./devshell.nix - ]; + imports = (builtins.map multiService [ + ./apache-kafka.nix + ./azurite.nix + ./clickhouse + ./dynamodb-local.nix + ./elasticmq.nix + ./elasticsearch.nix + ./mongodb.nix + ./mysql + ./nginx + ./ollama.nix + ./postgres + ./open-webui.nix + ./plantuml.nix + ./redis-cluster.nix + ./redis.nix + ./seaweedfs.nix + ./zookeeper.nix + ./grafana.nix + ./memcached.nix + ./minio.nix + ./nats-server.nix + ./prometheus.nix + ./pgadmin.nix + ./cassandra.nix + ./pyroscope.nix + ./tempo.nix + ./weaviate.nix + ./searxng.nix + ./tika.nix + ./loki.nix + ./phpfpm.nix + ./pubsub-emulator.nix + ./qdrant.nix + ./chromadb.nix + ./neo4j.nix + ./authentik.nix + ]) + ++ [ + ./devshell.nix + ]; } From c7200cc8d3bbb60452257062970fd93613467551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Tue, 21 Jul 2026 18:26:32 +0200 Subject: [PATCH 10/16] fix: add blueprint export and test --- nix/services/authentik/options.nix | 63 +++++++---------- nix/services/authentik/service.nix | 109 +++++++++++++++++------------ nix/services/authentik_test.nix | 53 ++++++++++++-- 3 files changed, 141 insertions(+), 84 deletions(-) diff --git a/nix/services/authentik/options.nix b/nix/services/authentik/options.nix index 6e19fcc4..644419f9 100644 --- a/nix/services/authentik/options.nix +++ b/nix/services/authentik/options.nix @@ -1,4 +1,5 @@ -{ lib +{ config +, lib , pkgs , name , ... @@ -7,6 +8,7 @@ let inherit (lib) mkOption + mkEnableOption types ; @@ -137,43 +139,32 @@ in }; }; - blueprints = mkOption { - default = { }; - type = types.attrsOf ( - types.submodule { - options = { - path = mkOption { - type = types.pathWith { - inStore = true; - }; - default = null; - description = '' - Path of a blueprint YAML file to make available for import. - ''; - }; - - import = mkOption { - type = types.bool; - default = true; - description = "Whether to make this blueprint available for import."; - }; + blueprints = { + export = { + enable = mkEnableOption "blueprint export process on manual trigger"; + path = mkOption { + description = "The path to the export file."; + type = types.pathWith { + inStore = false; + absolute = false; }; - } - ); - - example = lib.literalExpression '' - { - my-app = { - path = ./blueprints/my-app.yaml; - }; - } - ''; + default = "${config.dataDir}/export/blueprint.yaml"; + }; + }; - description = '' - Blueprints to import on start up. - Enabled blueprints are copied into the blueprints directoryr and - auto-applied by the Authentik worker. - ''; + imports = mkOption { + description = '' + Blueprints to import on start up. + Enabled blueprints are copied into the blueprints directoryr and + auto-applied by the Authentik worker. + ''; + type = types.listOf ( + types.pathWith { + inStore = true; + } + ); + default = [ ]; + }; }; settings = mkOption { diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index fb48a7da..39e8d384 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -78,6 +78,7 @@ let ); # The authentik components. + inherit (finalComponents.authentikComponents) manage; inherit (finalComponents.authentikComponents) gopkgs; inherit (finalComponents.authentikComponents) rust; inherit (finalComponents.authentikComponents) migrate; @@ -121,19 +122,19 @@ let # Copy the enabled blueprints into the Authentik blueprints dir. # Copied (not symlinked) for the same reason as the built-in # blueprints: authentik rejects blueprints whose resolved path escapes `blueprints_dir`. - blueprintImport = lib.mapAttrsToList + blueprintImport = lib.map ( - bp: e: - # Bash - '' - filename="${bp}.yaml" - f="${e.path}" - echo "Copying blueprint '${bp}' from '$f' into './additional/${bp}'." - mkdir -p ./additional - cp -L "$f" "./additional/${bp}" - '' + p: + # Bash + '' + filename="${lib.baseNameOf p}" + f="${p}" + echo "Copying blueprint '${p}' from '$f' into './additional/$filename'." + mkdir -p ./additional + cp -L "$f" "./additional/$filename" + '' ) - (lib.filterAttrs (_: v: v.import && v.path != null) cfg.blueprints); + cfg.blueprints.imports; loadEnvFile = lib.optionalString (cfg.environmentFile != null) @@ -184,9 +185,6 @@ let echo "=====================" cat "$staticDir/authentik/lib/default.yml" echo "=====================" - - cd "$staticDir" - echo "Working dir: $(pwd)" ''; setup = @@ -202,6 +200,7 @@ let # Bash '' ${setup} + cd "$staticDir" echo "Starting authentik migrate ..." exec ${migrate}/bin/migrate.py ''; @@ -211,15 +210,27 @@ let # Bash '' ${setup} + cd "$staticDir" echo "Starting authentik worker ..." exec ${rust}/bin/authentik worker ''; + authentik-blueprint-export = + pkgs.writeShellScriptBin "authentik-worker" + # Bash + '' + ${setup} + echo "Starting authentik blueprint export to '${cfg.blueprints.export.path}'..." + mkdir -p "$(dirname "${cfg.blueprints.export.path}")" + exec ${manage}/bin/manage.py export_blueprint > "${cfg.blueprints.export.path}" + ''; + authentik-server = pkgs.writeShellScriptBin "authentik-server" # Bash '' ${setup} + cd "$staticDir" echo "Starting authentik server ..." exec ${gopkgs}/bin/server ''; @@ -271,41 +282,51 @@ in }; }; - outputs.settings.processes = lib.mkIf cfg.enable { - "${name}-migrate" = { - description = "Authentik database migrations: a prerequisite that creates the schema."; - environment = connEnv; - command = "${lib.getExe authentik-migrate}"; + outputs.settings.processes = lib.mkIf cfg.enable ( + { + "${name}-migrate" = { + description = "Authentik database migrations: a prerequisite that creates the schema."; + environment = connEnv; + command = "${lib.getExe authentik-migrate}"; - depends_on = { - "${name}-pg-db".condition = "process_healthy"; + depends_on = { + "${name}-pg-db".condition = "process_healthy"; + }; }; - }; - "${name}-worker" = { - description = "Authentik background worker: processes tasks and applies blueprints."; - environment = connEnv // listenEnv "worker"; - command = "${lib.getExe authentik-worker}"; - depends_on."${name}-migrate".condition = "process_completed_successfully"; - }; + "${name}-worker" = { + description = "Authentik background worker: processes tasks and applies blueprints."; + environment = connEnv // listenEnv "worker"; + command = "${lib.getExe authentik-worker}"; + depends_on."${name}-migrate".condition = "process_completed_successfully"; + }; - "${name}" = { - description = "Authentik HTTP/API server."; - environment = connEnv // listenEnv "server"; - command = "${lib.getExe authentik-server}"; + "${name}" = { + description = "Authentik HTTP/API server."; + environment = connEnv // listenEnv "server"; + command = "${lib.getExe authentik-server}"; - depends_on = { - "${name}-migrate".condition = "process_completed_successfully"; - "${name}-worker".condition = "process_started"; - }; + depends_on = { + "${name}-migrate".condition = "process_completed_successfully"; + "${name}-worker".condition = "process_started"; + }; - readiness_probe = { - exec.command = "${lib.getExe authentik-health}"; - initial_delay_seconds = 20; - period_seconds = 5; - timeout_seconds = 4; - failure_threshold = 30; + readiness_probe = { + exec.command = "${lib.getExe authentik-health}"; + initial_delay_seconds = 20; + period_seconds = 5; + timeout_seconds = 4; + failure_threshold = 30; + }; }; - }; - }; + } + // lib.optionalAttrs (cfg.blueprints.export.enable) { + "${name}-blueprint-export" = { + disabled = true; + description = "Authentik blueprint export."; + environment = connEnv // listenEnv "worker"; + command = "${lib.getExe authentik-blueprint-export}"; + }; + } + ); } diff --git a/nix/services/authentik_test.nix b/nix/services/authentik_test.nix index a3240914..3f615aa4 100644 --- a/nix/services/authentik_test.nix +++ b/nix/services/authentik_test.nix @@ -1,4 +1,5 @@ -{ config +{ lib +, config , pkgs , ... }: @@ -6,6 +7,8 @@ let name = "ak"; httpPort = 9001; ak = config.services.authentik.${name}; + + exportPath = config.services.authentik.${name}.blueprints.export.path; in { services.postgres = ak.services.postgres; @@ -27,8 +30,11 @@ in server.http.port = 9001; worker.http.port = 9002; - blueprints.example = { - path = ./authentik/test-blueprints/example.yaml; + blueprints = { + export.enable = true; + imports = [ + ./authentik/test-blueprints/example.yaml + ]; }; settings = { @@ -39,12 +45,51 @@ in settings.processes.test = { command = pkgs.writeShellApplication { name = "${name}-test"; - runtimeInputs = [ pkgs.curl ]; + runtimeInputs = [ + pkgs.curl + pkgs.jq + config.package + ]; text = '' + export PC_SOCKET_PATH="${config.cli.options.unix-socket}" + # Silence process-compose not finding a config home. + mkdir -p "$(pwd)/.config/process-compose" + # shellcheck disable=SC2155 + export XDG_CONFIG_HOME="$(pwd)/.config" + echo "Checking authentik health endpoints..." curl -fsS "http://localhost:${toString httpPort}/-/health/live/" curl -fsS "http://localhost:${toString httpPort}/-/health/ready/" echo "authentik is up." + + echo "Check blueprint export." + process-compose process start "${name}-blueprint-export" + + completed="false" + for _ in $(seq 1 30); do + if + [ "$( + process-compose process get "${name}-blueprint-export" \ + -o json | + jq -r ".[0].status" + )" = "Completed" ] + then + completed="true" + break + fi + + sleep 2 + done + + if [ "$completed" != "true" ]; then + echo "!! Blueprint export did not complete in time." + exit 1 + fi + + if [ ! -f "${exportPath}" ]; then + echo "!! Blueprint file '${exportPath}' did not get exported." + exit 1 + fi ''; }; depends_on.${name}.condition = "process_healthy"; From 6dbd24d45019cd1d4cc240754f6f5b3ca7066efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Tue, 21 Jul 2026 18:37:25 +0200 Subject: [PATCH 11/16] fix: make sandbox safe also for `manage` --- nix/services/authentik/service.nix | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index 39e8d384..7ded8b10 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -15,18 +15,20 @@ let # `/usr/bin/env` does not exist in the Nix build sandbox that # `nix build .#checks…` (`just test`) runs the process-compose stack in, # so the migration crashes with "bad interpreter". - modMigrate = - prev: pythonEnv: + makeSandboxSafe = + prev: name: pythonEnv: pkgs.runCommand "authentik-migrate-sandbox-safe" { } # Bash '' cp -R --no-preserve=mode,ownership ${prev} $out + wrapped="$out/bin/.${name}.py-wrapped" + normal="$out/bin/${name}.py" - substituteInPlace $out/bin/.migrate.py-wrapped \ + substituteInPlace "$wrapped" \ --replace-fail '#!/usr/bin/env python' '#!${pythonEnv}/bin/python' - substituteInPlace $out/bin/migrate.py \ - --replace-fail '${prev}/bin/.migrate.py-wrapped' "$out/bin/.migrate.py-wrapped" - chmod +x "$out/bin/.migrate.py-wrapped" "$out/bin/migrate.py" + substituteInPlace "$normal" \ + --replace-fail '${prev}/bin/.${name}.py-wrapped' "$wrapped" + chmod +x "$out/bin/.${name}.py-wrapped" "$out/bin/${name}.py" ''; settingsFile = settingsFormat.generate "authentik.yml" cfg.settings; @@ -71,7 +73,8 @@ let in { authentikComponents = prevComps // { - migrate = modMigrate prevComps.migrate prevComps.pythonEnv; + manage = makeSandboxSafe prevComps.manage "manage" prevComps.pythonEnv; + migrate = makeSandboxSafe prevComps.migrate "migrate" prevComps.pythonEnv; staticWorkdirDeps = modStaticWorkdirDeps prevComps.staticWorkdirDeps prev.authentik-src; }; } From 45f7d57b252a74bebce9d38ec28e12446db00e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Wed, 22 Jul 2026 15:35:01 +0200 Subject: [PATCH 12/16] chore: command takes direct derivation --- nix/services/authentik/service.nix | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index 7ded8b10..e83fae65 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -290,24 +290,21 @@ in "${name}-migrate" = { description = "Authentik database migrations: a prerequisite that creates the schema."; environment = connEnv; - command = "${lib.getExe authentik-migrate}"; - - depends_on = { - "${name}-pg-db".condition = "process_healthy"; - }; + command = authentik-migrate; + depends_on."${name}-pg-db".condition = "process_healthy"; }; "${name}-worker" = { description = "Authentik background worker: processes tasks and applies blueprints."; environment = connEnv // listenEnv "worker"; - command = "${lib.getExe authentik-worker}"; + command = authentik-worker; depends_on."${name}-migrate".condition = "process_completed_successfully"; }; "${name}" = { description = "Authentik HTTP/API server."; environment = connEnv // listenEnv "server"; - command = "${lib.getExe authentik-server}"; + command = authentik-server; depends_on = { "${name}-migrate".condition = "process_completed_successfully"; From 8fde622f9afa6c5cd2bfae2447e176fe53304c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 23 Jul 2026 09:17:59 +0200 Subject: [PATCH 13/16] fix: formatting in flake.nix --- test/flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/test/flake.nix b/test/flake.nix index b3f8df73..6440f0a8 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -54,6 +54,7 @@ builtins.listToAttrs ( builtins.map mkPackageFor ( [ + "${inputs.services-flake}/nix/services/authentik_test.nix" "${inputs.services-flake}/nix/services/apache-kafka-kraft_test.nix" "${inputs.services-flake}/nix/services/azurite_test.nix" "${inputs.services-flake}/nix/services/chromadb_test.nix" From e3169ea4352a8239d1da41c69434a88c49a7e3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 23 Jul 2026 09:20:55 +0200 Subject: [PATCH 14/16] fix: formatting in pkgs.nix --- test/nix/pkgs.nix | 52 +++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/test/nix/pkgs.nix b/test/nix/pkgs.nix index 5247f9f3..f593eb3f 100644 --- a/test/nix/pkgs.nix +++ b/test/nix/pkgs.nix @@ -1,40 +1,30 @@ { inputs, ... }: { - perSystem = - { self' - , inputs' - , pkgs - , system - , lib - , ... - }: - { - _module.args.pkgs = import inputs.nixpkgs { - inherit system; + perSystem = { self', inputs', pkgs, system, lib, ... }: { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; - # Required for elastic search - config.allowUnfree = true; + # Required for elastic search + config.allowUnfree = true; - overlays = [ - ( - self: super: - lib.optionalAttrs super.stdenv.isDarwin - { + overlays = [ + (self: super: lib.optionalAttrs super.stdenv.isDarwin + { - # Disable tests, because they are failing on darwin: - # https://github.com/NixOS/nixpkgs/issues/281214 - pgadmin4 = super.pgadmin4.overrideAttrs (_: { - doInstallCheck = false; - }); + # Disable tests, because they are failing on darwin: + # https://github.com/NixOS/nixpkgs/issues/281214 + pgadmin4 = super.pgadmin4.overrideAttrs (_: { + doInstallCheck = + false; + }); - } - // { - # Add authentik packages cause its not in nixpkgs. - authentikComponents = inputs'.authentik-nix.legacyPackages.authentikComponents; - } - ) - ]; - }; + } // { + # Add authentik packages cause its not in nixpkgs. + authentikComponents = inputs'.authentik-nix.legacyPackages.authentikComponents; + } + ) + ]; }; + }; } From 7c1f6206e0e8f8e9466bb64554cb53fba7f67566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Mon, 27 Jul 2026 09:24:33 +0200 Subject: [PATCH 15/16] fix: switch to upstream authentik flake --- nix/services/authentik/service.nix | 21 ++++++++++----------- test/flake.lock | 20 ++++++++++---------- test/flake.nix | 2 +- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index e83fae65..06183da1 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -193,7 +193,6 @@ let setup = # Bash '' - set -euo pipefail ${runtimeEnv} ${loadEnvFile} ''; @@ -218,24 +217,24 @@ let exec ${rust}/bin/authentik worker ''; - authentik-blueprint-export = - pkgs.writeShellScriptBin "authentik-worker" + authentik-server = + pkgs.writeShellScriptBin "authentik-server" # Bash '' ${setup} - echo "Starting authentik blueprint export to '${cfg.blueprints.export.path}'..." - mkdir -p "$(dirname "${cfg.blueprints.export.path}")" - exec ${manage}/bin/manage.py export_blueprint > "${cfg.blueprints.export.path}" + cd "$staticDir" + echo "Starting authentik server ..." + exec ${gopkgs}/bin/authentik-server ''; - authentik-server = - pkgs.writeShellScriptBin "authentik-server" + authentik-blueprint-export = + pkgs.writeShellScriptBin "authentik-worker" # Bash '' ${setup} - cd "$staticDir" - echo "Starting authentik server ..." - exec ${gopkgs}/bin/server + echo "Starting authentik blueprint export to '${cfg.blueprints.export.path}'..." + mkdir -p "$(dirname "${cfg.blueprints.export.path}")" + exec ${manage}/bin/manage.py export_blueprint > "${cfg.blueprints.export.path}" ''; authentik-health = diff --git a/test/flake.lock b/test/flake.lock index 07a0ea70..8edfab5a 100644 --- a/test/flake.lock +++ b/test/flake.lock @@ -13,11 +13,11 @@ "uv2nix": "uv2nix" }, "locked": { - "lastModified": 1784059115, - "narHash": "sha256-HDox7X6IKv0tgURi1DoWX9NYjY/ngTOfMvlOJsEl0oI=", + "lastModified": 1785064442, + "narHash": "sha256-qMigDNdmkbAEGeL2kndiuJzT0tBTt14gJqOkZhZx2g4=", "owner": "nix-community", "repo": "authentik-nix", - "rev": "1a0767799b4be2fc6d0dcf8b77d86f5838eafbc6", + "rev": "5fbbce90f7cbe1bec0afa7e31598d8da7fa101dc", "type": "github" }, "original": { @@ -29,16 +29,16 @@ "authentik-src": { "flake": false, "locked": { - "lastModified": 1783473460, - "narHash": "sha256-pGOd9+Una59JUgOcPC3PoqOqY08GkJtY+jgtk13rJ1Y=", + "lastModified": 1784731584, + "narHash": "sha256-/HdXzjjvuSW7zjbCNJKm3Fj8gvIwfrDf8mOYev0yuIg=", "owner": "goauthentik", "repo": "authentik", - "rev": "c2942671a5b98dfa596de7bf247accb48a5c71ee", + "rev": "0c67ea476be6319f1b2a41cb0f5ed128af37b99b", "type": "github" }, "original": { "owner": "goauthentik", - "ref": "version/2026.5.4", + "ref": "version/2026.5.6", "repo": "authentik", "type": "github" } @@ -118,11 +118,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1783776592, - "narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=", + "lastModified": 1784497964, + "narHash": "sha256-vlHUuqAcbcH2RKmHbPiuQzbv1pnzzavXnI62RD0bqCU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3", + "rev": "241313f4e8e508cb9b13278c2b0fa25b9ca27163", "type": "github" }, "original": { diff --git a/test/flake.nix b/test/flake.nix index 6440f0a8..c9643272 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -5,7 +5,7 @@ systems.url = "github:nix-systems/default"; process-compose-flake.url = "github:Platonic-Systems/process-compose-flake"; services-flake.url = "github:juspay/services-flake"; - authentik-nix.url = "github:gabyx/authentik-nix/version/2026.5.4"; + authentik-nix.url = "github:nix-community/authentik-nix"; }; outputs = inputs: From b410737384b8bdecc0dd3e54f881f3a8160722a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20N=C3=BCtzi?= Date: Thu, 30 Jul 2026 10:53:01 +0200 Subject: [PATCH 16/16] fix: temp folder to not interfer with max sock. length --- nix/services/authentik/service.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/services/authentik/service.nix b/nix/services/authentik/service.nix index 06183da1..1d13fc0a 100644 --- a/nix/services/authentik/service.nix +++ b/nix/services/authentik/service.nix @@ -170,7 +170,7 @@ let mkdir -p "$tmpDir" ln -s "$tmpDir" "$dataDir/temp" fi - export TMPDIR="$dataDir/temp" + export TMPDIR="$tmpDir" export TEMPDIR="$TMPDIR" export PROMETHEUS_MULTIPROC_DIR="$dataDir/prometheus"