From 924869c66e213c9648e0945a2570c79eb5042028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Tue, 10 Feb 2026 17:23:45 +0100 Subject: [PATCH 01/10] Enable system manager * Create default system manager configuration * chores: add nix run .#check-system-module to github actions workflows * feat: replace Docker-based system-manager tests with container test framework Switch from building Docker images and running pytest+testinfra externally to using system-manager's built-in makeContainerTest API backed by systemd-nspawn. The test is now a Nix check derivation that runs inside the build sandbox. It requires auto-allocating UIDs in the ephemeral Nix installation, which is now enabled by default in the GitHub Action. Rebased from #2010 Co-authored-by: Yvan Sraka --- .../actions/nix-install-ephemeral/action.yml | 2 + .github/workflows/check-system-manager.yml | 27 +++ ansible/playbook.yml | 6 +- ansible/tasks/setup-nix.yml | 11 ++ ansible/tasks/setup-system-manager.yml | 7 + flake.lock | 156 ++++++++++++++++++ flake.nix | 4 + nix/packages/default.nix | 1 + nix/systemConfigs.nix | 30 ++++ nix/systemModules/default.nix | 14 ++ nix/systemModules/tests/default.nix | 33 ++++ stage2-nix-psql.pkr.hcl | 5 + 12 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/check-system-manager.yml create mode 100644 ansible/tasks/setup-nix.yml create mode 100644 ansible/tasks/setup-system-manager.yml create mode 100644 nix/systemConfigs.nix create mode 100644 nix/systemModules/default.nix create mode 100644 nix/systemModules/tests/default.nix diff --git a/.github/actions/nix-install-ephemeral/action.yml b/.github/actions/nix-install-ephemeral/action.yml index 5dbdabe8e8..c999971e46 100644 --- a/.github/actions/nix-install-ephemeral/action.yml +++ b/.github/actions/nix-install-ephemeral/action.yml @@ -48,4 +48,6 @@ runs: substituters = https://cache.nixos.org https://nix-postgres-artifacts.s3.amazonaws.com trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ${{ inputs.push-to-cache == 'true' && 'post-build-hook = /etc/nix/upload-to-cache.sh' || '' }} + extra-experimental-features = auto-allocate-uids cgroups + auto-allocate-uids = true max-jobs = 4 diff --git a/.github/workflows/check-system-manager.yml b/.github/workflows/check-system-manager.yml new file mode 100644 index 0000000000..9c1fb934d5 --- /dev/null +++ b/.github/workflows/check-system-manager.yml @@ -0,0 +1,27 @@ +name: Check System Manager + +on: + pull_request: + merge_group: + workflow_dispatch: + +permissions: + id-token: write + +jobs: + check-system-manager: + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: supabase/postgres/.github/actions/shared-checkout@HEAD + + - name: Install nix + uses: ./.github/actions/nix-install-ephemeral + with: + push-to-cache: 'true' + env: + DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }} + NIX_SIGN_SECRET_KEY: ${{ secrets.NIX_SIGN_SECRET_KEY }} + + - name: Run check-system-manager + run: nix build .#checks.x86_64-linux.check-system-manager -L diff --git a/ansible/playbook.yml b/ansible/playbook.yml index fbc3d5d81b..2e9a7d8ba1 100644 --- a/ansible/playbook.yml +++ b/ansible/playbook.yml @@ -146,7 +146,11 @@ tags: - install-supabase-internal when: debpkg_mode or nixpkg_mode - + + - name: deploy system-manager + import_tasks: tasks/setup-system-manager.yml + when: debpkg_mode or stage2_nix + - name: Enhance fail2ban import_tasks: tasks/setup-fail2ban.yml when: debpkg_mode or nixpkg_mode diff --git a/ansible/tasks/setup-nix.yml b/ansible/tasks/setup-nix.yml new file mode 100644 index 0000000000..9675677dd9 --- /dev/null +++ b/ansible/tasks/setup-nix.yml @@ -0,0 +1,11 @@ +--- +- name: Check if nix is installed + ansible.builtin.command: which nix + register: nix_installed + failed_when: nix_installed.rc != 0 + ignore_errors: true + +- name: Install nix + ansible.builtin.shell: curl --proto '=https' --tlsv1.2 -sSf -L https://artifacts.nixos.org/experimental-installer | sh -s -- install --no-confirm --extra-conf 'substituters = https://cache.nixos.org https://nix-postgres-artifacts.s3.amazonaws.com' --extra-conf 'trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI=% cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=' + when: nix_installed.rc != 0 + become: true diff --git a/ansible/tasks/setup-system-manager.yml b/ansible/tasks/setup-system-manager.yml new file mode 100644 index 0000000000..cd76bbb4d4 --- /dev/null +++ b/ansible/tasks/setup-system-manager.yml @@ -0,0 +1,7 @@ +--- +- name: Deploy system manager + ansible.builtin.shell: | + . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh + cd /tmp + nix run --accept-flake-config /tmp/flake#system-manager -- switch --flake /tmp/flake 2>&1 | tee /tmp/system-manager-deploy.log + become: true diff --git a/flake.lock b/flake.lock index 7634474387..e19c3aef42 100644 --- a/flake.lock +++ b/flake.lock @@ -36,6 +36,22 @@ "type": "github" } }, + "flake-compat_2": { + "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" @@ -54,6 +70,28 @@ "type": "github" } }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": [ + "system-manager", + "userborn", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768135262, + "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -115,6 +153,29 @@ "type": "github" } }, + "gitignore_2": { + "inputs": { + "nixpkgs": [ + "system-manager", + "userborn", + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "multigres": { "flake": false, "locked": { @@ -292,6 +353,34 @@ "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" } }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": [ + "system-manager", + "userborn", + "flake-compat" + ], + "gitignore": "gitignore_2", + "nixpkgs": [ + "system-manager", + "userborn", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1769069492, + "narHash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "a1ef738813b15cf8ec759bdff5761b027e3e1d23", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "devshell": "devshell", @@ -306,6 +395,7 @@ "nixpkgs": "nixpkgs_2", "nixpkgs-oldstable": "nixpkgs-oldstable", "rust-overlay": "rust-overlay", + "system-manager": "system-manager", "treefmt-nix": "treefmt-nix" } }, @@ -329,6 +419,28 @@ "type": "github" } }, + "system-manager": { + "inputs": { + "flake-compat": "flake-compat_2", + "nixpkgs": [ + "nixpkgs" + ], + "userborn": "userborn" + }, + "locked": { + "lastModified": 1773396529, + "narHash": "sha256-jSnyKvIEqzmK+y0/4zb34sE7PMAd09p0Y+JNGR0nTmA=", + "owner": "numtide", + "repo": "system-manager", + "rev": "1272183d7798866ecf1bbdb29fc0387581a3317a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "system-manager", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, @@ -344,6 +456,21 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "treefmt-nix": { "inputs": { "nixpkgs": [ @@ -363,6 +490,35 @@ "repo": "treefmt-nix", "type": "github" } + }, + "userborn": { + "inputs": { + "flake-compat": [ + "system-manager", + "flake-compat" + ], + "flake-parts": "flake-parts_2", + "nixpkgs": [ + "system-manager", + "nixpkgs" + ], + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "systems": "systems_2" + }, + "locked": { + "lastModified": 1770377964, + "narHash": "sha256-q2pnlX2IW0kg80GLFnwWd/GigIpkuZnyKPLhrgJql3E=", + "owner": "jfroche", + "repo": "userborn", + "rev": "55c2cd7952c207a62736a5bbd9499ea73da18d24", + "type": "github" + }, + "original": { + "owner": "jfroche", + "ref": "system-manager", + "repo": "userborn", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index dbabf994c2..7f2ebe31d3 100644 --- a/flake.nix +++ b/flake.nix @@ -33,6 +33,8 @@ rust-overlay.url = "github:oxalica/rust-overlay"; treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; treefmt-nix.url = "github:numtide/treefmt-nix"; + system-manager.inputs.nixpkgs.follows = "nixpkgs"; + system-manager.url = "github:numtide/system-manager"; }; outputs = @@ -55,6 +57,8 @@ nix/nixpkgs.nix nix/packages nix/overlays + nix/systemModules + nix/systemConfigs.nix ]; }); } diff --git a/nix/packages/default.nix b/nix/packages/default.nix index 7b1a6ea546..658d61c16d 100644 --- a/nix/packages/default.nix +++ b/nix/packages/default.nix @@ -91,6 +91,7 @@ inherit (self'.packages) overlayfs-on-package; }; sync-exts-versions = pkgs.callPackage ./sync-exts-versions.nix { inherit (inputs') nix-editor; }; + system-manager = inputs'.system-manager.packages.default; trigger-nix-build = pkgs.callPackage ./trigger-nix-build.nix { }; update-readme = pkgs.callPackage ./update-readme.nix { }; supabase-cli = pkgs.callPackage ./supabase-cli.nix { }; diff --git a/nix/systemConfigs.nix b/nix/systemConfigs.nix new file mode 100644 index 0000000000..7f50ded93e --- /dev/null +++ b/nix/systemConfigs.nix @@ -0,0 +1,30 @@ +{ self, inputs, ... }: +let + mkModules = system: [ + ({ + services.nginx.enable = true; + nixpkgs.hostPlatform = system; + }) + ]; + + systems = [ + "aarch64-linux" + "x86_64-linux" + ]; + + mkSystemConfig = system: { + name = system; + value.default = inputs.system-manager.lib.makeSystemConfig { + modules = mkModules system; + extraSpecialArgs = { + inherit self; + inherit system; + }; + }; + }; +in +{ + flake = { + systemConfigs = builtins.listToAttrs (map mkSystemConfig systems); + }; +} diff --git a/nix/systemModules/default.nix b/nix/systemModules/default.nix new file mode 100644 index 0000000000..4810dff46e --- /dev/null +++ b/nix/systemModules/default.nix @@ -0,0 +1,14 @@ +{ + flake-parts-lib, + withSystem, + self, + ... +}: +{ + imports = [ ./tests ]; + flake = { + systemModules = { + nginx = flake-parts-lib.importApply ./nginx.nix { inherit withSystem self; }; + }; + }; +} diff --git a/nix/systemModules/tests/default.nix b/nix/systemModules/tests/default.nix new file mode 100644 index 0000000000..e959f63216 --- /dev/null +++ b/nix/systemModules/tests/default.nix @@ -0,0 +1,33 @@ +{ self, inputs, ... }: +{ + perSystem = + { + lib, + pkgs, + ... + }: + { + checks = lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { + check-system-manager = + let + toplevel = self.systemConfigs.${pkgs.system}.default; + in + inputs.system-manager.lib.containerTest.makeContainerTest { + hostPkgs = pkgs; + name = "check-system-manager"; + inherit toplevel; + testScript = '' + start_all() + + machine.wait_for_unit("multi-user.target") + + machine.activate() + machine.wait_for_unit("system-manager.target") + + with subtest("Verify nginx service"): + assert machine.service("nginx").is_running, "nginx should be running" + ''; + }; + }; + }; +} diff --git a/stage2-nix-psql.pkr.hcl b/stage2-nix-psql.pkr.hcl index 032dd71e52..d751a506d4 100644 --- a/stage2-nix-psql.pkr.hcl +++ b/stage2-nix-psql.pkr.hcl @@ -120,6 +120,11 @@ build { destination = "/tmp/ansible-playbook" } + provisioner "file" { + source = "${abspath(path.root)}" + destination = "/tmp/flake" + } + provisioner "shell" { environment_vars = [ "GIT_SHA=${var.git_sha}", From 1beb443da2cd7a83e638883510755b61708a5d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Tue, 24 Feb 2026 14:27:18 +0100 Subject: [PATCH 02/10] fix: update user baseline for system-manager managed paths system-manager's userborn service rewrites /etc/passwd entries: - root shell: /bin/bash -> /run/system-manager/sw/bin/bash - nobody shell: /usr/sbin/nologin -> /run/system-manager/sw/bin/nologin --- audit-specs/baselines/ami-build/user.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audit-specs/baselines/ami-build/user.yml b/audit-specs/baselines/ami-build/user.yml index e764be6268..a3b1e716c8 100644 --- a/audit-specs/baselines/ami-build/user.yml +++ b/audit-specs/baselines/ami-build/user.yml @@ -6,14 +6,14 @@ user: root: exists: true home: /root - shell: /bin/bash + shell: /run/system-manager/sw/bin/bash ubuntu: exists: true home: /home/ubuntu shell: /bin/bash nobody: exists: true - shell: /usr/sbin/nologin + shell: /run/system-manager/sw/bin/nologin # PostgreSQL ecosystem postgres: From d18d9800150e6a0d528489de912b5525aa73a0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Tue, 24 Feb 2026 14:32:56 +0100 Subject: [PATCH 03/10] docs: add system-manager integration documentation --- nix/docs/README.md | 1 + nix/docs/nix-directory-structure.md | 18 ++- nix/docs/system-manager.md | 210 ++++++++++++++++++++++++++++ nix/mkdocs.yml | 1 + 4 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 nix/docs/system-manager.md diff --git a/nix/docs/README.md b/nix/docs/README.md index 5d177b13de..9b2f8c03c4 100644 --- a/nix/docs/README.md +++ b/nix/docs/README.md @@ -19,6 +19,7 @@ learn how to play with `postgres` in the [build guide](./build-postgres.md). - **[Start Client/Server](./start-client-server.md)** - Running PostgreSQL client and server - **[Docker](./docker.md)** - Docker integration and usage - **[Docker Image Size Analyzer](./image-size-analyzer-usage.md)** - Tool to analyze the Docker image sizes +- **[System Manager](./system-manager.md)** - Declarative system configuration with system-manager - **[Use direnv](./use-direnv.md)** - Development environment with direnv - **[Pre-commit Hooks](./pre-commit-hooks.md)** - Automatic formatting and code checks before commits - **[Nix Formatter](./nix-formatter.md)** - Code formatting with treefmt diff --git a/nix/docs/nix-directory-structure.md b/nix/docs/nix-directory-structure.md index c58441b4bc..33b8feaaca 100644 --- a/nix/docs/nix-directory-structure.md +++ b/nix/docs/nix-directory-structure.md @@ -22,7 +22,9 @@ nix/ ├── ext/ # PostgreSQL extensions ├── overlays/ # Nixpkgs overlays ├── packages/ # Custom packages - └── postgresql/ # PostgreSQL packages + ├── postgresql/ # PostgreSQL packages + ├── systemConfigs.nix # system-manager configuration definitions + └── systemModules/ # system-manager service modules ``` ## Module Descriptions @@ -150,6 +152,20 @@ Nixpkgs overlays for package customization: - `cargo-pgrx-0-11-3.nix` - PGRX toolchain overlay - `psql_16-oriole.nix` - OrioleDB PostgreSQL variant +#### `nix/systemConfigs.nix` + +System configuration definitions for [system-manager](https://github.com/numtide/system-manager). +Calls `system-manager.lib.makeSystemConfig` to produce a configuration for each supported architecture (`aarch64-linux`, `x86_64-linux`) from the enabled modules. +See [System manager](./system-manager.md) for details. + +#### `nix/systemModules/` + +Service module definitions managed by system-manager: + +- `default.nix` - Module registry that exports modules under `flake.systemModules` +- Individual `.nix` files - Service modules (e.g. nginx) loaded via `flake-parts-lib.importApply` +- `tests/default.nix` - Container-based tests using `makeContainerTest` + #### `nix/cargo-pgrx/` Rust-based PostgreSQL extension building: diff --git a/nix/docs/system-manager.md b/nix/docs/system-manager.md new file mode 100644 index 0000000000..5f790d17c8 --- /dev/null +++ b/nix/docs/system-manager.md @@ -0,0 +1,210 @@ +# System manager + +[system-manager](https://github.com/numtide/system-manager) provides declarative, Nix-based system configuration management for non-NixOS Linux systems. +It replaces imperative service setup with reproducible Nix module definitions, bringing NixOS-style service management to the AMI build without requiring a full NixOS installation. + +## How it fits into the AMI build pipeline + +The AMI build uses a two-stage pipeline orchestrated by Packer and Ansible. +Stage 1 installs Nix itself, while stage 2 uses Nix to build and deploy all services. +system-manager is deployed during stage 2 via the Ansible task `ansible/tasks/setup-system-manager.yml`: + +```yaml +- name: Deploy system manager + ansible.builtin.shell: | + . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh + cd /tmp + nix run --accept-flake-config /tmp/flake#system-manager -- switch --flake /tmp/flake 2>&1 | tee /tmp/system-manager-deploy.log + become: true +``` + +This sources the Nix daemon profile, then runs `system-manager switch` against the flake to apply the declared system configuration. + +## Nix configuration walkthrough + +### Flake input + +The system-manager flake input is declared in `flake.nix` (lines 34-35), pinned to the upstream repository with nixpkgs following the main input: + +```nix +system-manager.inputs.nixpkgs.follows = "nixpkgs"; +system-manager.url = "github:numtide/system-manager"; +``` + +The flake outputs import both the module registry and the system configurations: + +```nix +imports = [ + # ... + nix/systemModules + nix/systemConfigs.nix +]; +``` + +### System configurations + +`nix/systemConfigs.nix` defines the top-level system configurations for each supported architecture. +It calls `system-manager.lib.makeSystemConfig` to produce a configuration from the enabled modules: + +```nix +mkSystemConfig = system: { + name = system; + value.default = inputs.system-manager.lib.makeSystemConfig { + modules = mkModules system; + extraSpecialArgs = { + inherit self; + inherit system; + }; + }; +}; +``` + +The `mkModules` function returns the list of modules to enable. +Currently it enables the nginx service and sets the host platform: + +```nix +mkModules = system: [ + ({ + services.nginx.enable = true; + nixpkgs.hostPlatform = system; + }) +]; +``` + +Configurations are built for both `aarch64-linux` and `x86_64-linux`. + +### System modules + +`nix/systemModules/default.nix` is the module registry. +It is a flake-parts module that exports individual system modules under `flake.systemModules`: + +```nix +{ + imports = [ ./tests ]; + flake = { + systemModules = { + nginx = flake-parts-lib.importApply ./nginx.nix { inherit withSystem self; }; + }; + }; +} +``` + +Each module is loaded with `flake-parts-lib.importApply`, which passes `withSystem` and `self` as arguments to the module file. + +## Adding a new system module + +To add a new system module: + +1. Create a new `.nix` file under `nix/systemModules/`, for example `nix/systemModules/my-service.nix`. + The module is a standard NixOS-style module with options and config: + + ```nix + { + lib, + config, + ... + }: + let + cfg = config.supabase.services.my-service; + in + { + options = { + supabase.services.my-service = { + enable = lib.mkEnableOption "Whether to enable the my-service systemd service."; + }; + }; + + config = lib.mkIf cfg.enable { + # systemd units, environment.etc entries, etc. + }; + } + ``` + +2. Register the module in `nix/systemModules/default.nix` by adding it to the `systemModules` attribute set: + + ```nix + systemModules = { + my-service = ./my-service.nix; + }; + ``` + +3. Include and enable the module in `nix/systemConfigs.nix` by adding it to the `mkModules` list and setting the enable option: + + ```nix + mkModules = system: [ + self.systemModules.my-service + ({ + services.nginx.enable = true; + supabase.services.my-service.enable = true; + nixpkgs.hostPlatform = system; + }) + ]; + ``` + +4. Add a test assertion to the test script in `nix/systemModules/tests/default.nix` (see below). + +## Testing + +### Container tests + +Tests are defined in `nix/systemModules/tests/default.nix` using `system-manager.lib.containerTest.makeContainerTest`. +This creates a lightweight container-based NixOS test that validates the system configuration: + +```nix +check-system-manager = + let + toplevel = self.systemConfigs.${pkgs.system}.default; + in + inputs.system-manager.lib.containerTest.makeContainerTest { + hostPkgs = pkgs; + name = "check-system-manager"; + inherit toplevel; + testScript = '' + start_all() + + machine.wait_for_unit("multi-user.target") + + machine.activate() + machine.wait_for_unit("system-manager.target") + + with subtest("Verify nginx service"): + assert machine.service("nginx").is_running, "nginx should be running" + ''; + }; +``` + +The test script starts the container, waits for systemd to reach `multi-user.target`, activates the system-manager configuration, then verifies that managed services are running. +When adding a new module, extend the `testScript` with an additional `subtest` block that asserts the new service is running. + +### Running tests locally + +Run the system-manager check locally with: + +```bash +nix build .#checks.x86_64-linux.check-system-manager -L +``` + +The `-L` flag streams build logs for visibility. +These checks only run on Linux (gated by `lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux`). + +## CI integration + +The `check-system-manager` derivation is part of the flake's `checks` output, so it runs automatically in the `nix-build-checks-*` jobs of the main `nix-build.yml` workflow alongside all other checks. + +## Runtime effects + +After `system-manager switch` runs, managed software is available under `/run/system-manager/sw/`. +This affects paths throughout the system. +For example, the audit baseline `audit-specs/baselines/ami-build/user.yml` references these paths for user shells: + +```yaml +root: + exists: true + home: /root + shell: /run/system-manager/sw/bin/bash +nobody: + exists: true + shell: /run/system-manager/sw/bin/nologin +``` + +When adding new services or modifying system-manager configuration, update the audit baselines accordingly to reflect any changes to user shells, service users, or file paths that `supascan` validates during AMI builds. diff --git a/nix/mkdocs.yml b/nix/mkdocs.yml index 52c2be5392..b4374ced18 100644 --- a/nix/mkdocs.yml +++ b/nix/mkdocs.yml @@ -26,6 +26,7 @@ nav: - Create pgrx Extension: creating-pgrx-extension.md - Update pgrx Extensions: updating-pgrx-extensions.md - Receipt Files: receipt-files.md + - System manager: system-manager.md - Package Management: - Adding New Packages: adding-new-package.md - Update Extensions: update-extension.md From 1148db88c9e41b333cb8c30435b105d857a253a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Tue, 24 Feb 2026 15:20:45 +0100 Subject: [PATCH 04/10] chore: remove redundant check-system-manager workflow The check should be part of the regular nix-build workflow --- .github/workflows/check-system-manager.yml | 27 ---------------------- 1 file changed, 27 deletions(-) delete mode 100644 .github/workflows/check-system-manager.yml diff --git a/.github/workflows/check-system-manager.yml b/.github/workflows/check-system-manager.yml deleted file mode 100644 index 9c1fb934d5..0000000000 --- a/.github/workflows/check-system-manager.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Check System Manager - -on: - pull_request: - merge_group: - workflow_dispatch: - -permissions: - id-token: write - -jobs: - check-system-manager: - runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: supabase/postgres/.github/actions/shared-checkout@HEAD - - - name: Install nix - uses: ./.github/actions/nix-install-ephemeral - with: - push-to-cache: 'true' - env: - DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }} - NIX_SIGN_SECRET_KEY: ${{ secrets.NIX_SIGN_SECRET_KEY }} - - - name: Run check-system-manager - run: nix build .#checks.x86_64-linux.check-system-manager -L From f5ce58df35311e931b2612176f868c29e37dd5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Roche?= Date: Fri, 27 Feb 2026 10:19:34 +0100 Subject: [PATCH 05/10] fix: configure basic ssh config file Enabling the nginx service in the system configuration was a good start, but it had implications for the test suite verifying that the AMI was correctly configured. We change the configuration to set up a basic ssh config file that matches the expected configuration for the AMI, and update the tests to verify that the file is created with the correct content and permissions. --- nix/systemConfigs.nix | 2 +- nix/systemModules/default.nix | 17 +++++++++++++---- nix/systemModules/tests/default.nix | 8 ++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/nix/systemConfigs.nix b/nix/systemConfigs.nix index 7f50ded93e..05161369d8 100644 --- a/nix/systemConfigs.nix +++ b/nix/systemConfigs.nix @@ -1,8 +1,8 @@ { self, inputs, ... }: let mkModules = system: [ + self.systemModules.ssh-config ({ - services.nginx.enable = true; nixpkgs.hostPlatform = system; }) ]; diff --git a/nix/systemModules/default.nix b/nix/systemModules/default.nix index 4810dff46e..89d6c8de84 100644 --- a/nix/systemModules/default.nix +++ b/nix/systemModules/default.nix @@ -1,14 +1,23 @@ { - flake-parts-lib, - withSystem, - self, ... }: { imports = [ ./tests ]; flake = { systemModules = { - nginx = flake-parts-lib.importApply ./nginx.nix { inherit withSystem self; }; + ssh-config = { + environment.etc."ssh/sshd_config.d/local.conf" = { + text = '' + Match Address 127.0.0.1,::1 + ForceCommand /bin/false + DisableForwarding yes + PermitTunnel no + ''; + user = "root"; + group = "root"; + mode = "0644"; + }; + }; }; }; } diff --git a/nix/systemModules/tests/default.nix b/nix/systemModules/tests/default.nix index e959f63216..a40d49c630 100644 --- a/nix/systemModules/tests/default.nix +++ b/nix/systemModules/tests/default.nix @@ -24,8 +24,12 @@ machine.activate() machine.wait_for_unit("system-manager.target") - with subtest("Verify nginx service"): - assert machine.service("nginx").is_running, "nginx should be running" + with subtest("Verify ssh config"): + assert machine.file("/etc/ssh/sshd_config.d/local.conf").exists, "/etc/ssh/sshd_config.d/local.conf should exist" + assert machine.file("/etc/ssh/sshd_config.d/local.conf").mode == 0o644, "/etc/ssh/sshd_config.d/local.conf should have mode 0644" + assert machine.file("/etc/ssh/sshd_config.d/local.conf").user == "root", "/etc/ssh/sshd_config.d/local.conf should be owned by root" + assert machine.file("/etc/ssh/sshd_config.d/local.conf").group == "root", "/etc/ssh/sshd_config.d/local.conf should be owned by root" + assert machine.file("/etc/ssh/sshd_config.d/local.conf").contains("Match Address"), "/etc/ssh/sshd_config.d/local.conf should contain 'Match Address'" ''; }; }; From 80dae49cfc34e6cd7d1bd9d701e5f2f81249170d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Sun, 8 Mar 2026 20:42:01 -0400 Subject: [PATCH 06/10] docs: more info on running tests --- nix/docs/system-manager.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/nix/docs/system-manager.md b/nix/docs/system-manager.md index 5f790d17c8..0b8f8386de 100644 --- a/nix/docs/system-manager.md +++ b/nix/docs/system-manager.md @@ -178,7 +178,31 @@ When adding a new module, extend the `testScript` with an additional `subtest` b ### Running tests locally -Run the system-manager check locally with: +The container tests use `systemd-nspawn` which requires the `uid-range` nix feature. This in turn requires `auto-allocate-uids` and the `auto-allocate-uids` experimental feature to be enabled on the Linux machine running the tests. + +**On macOS:** These tests cannot run natively on macOS. You need to enter the shell of a Linux VM (e.g. an Ubuntu VM via OrbStack, UTM, or similar) and run the tests from there. + +Ensure your Linux machine's `/etc/nix/nix.conf` includes: + +``` +auto-allocate-uids = true +extra-experimental-features = nix-command flakes auto-allocate-uids cgroups +trusted-users = root @wheel @sudo +``` + +After updating the config, restart the nix daemon: + +```bash +sudo systemctl restart nix-daemon +``` + +Then run the system-manager check: + +```bash +nix build .#checks.aarch64-linux.check-system-manager -L +``` + +Or for x86_64: ```bash nix build .#checks.x86_64-linux.check-system-manager -L From d5e30592bc21ef36c23a79b8c8ecd1d321a843b7 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 13 Mar 2026 10:00:03 -0400 Subject: [PATCH 07/10] fix: this pr is wrong place to harden ssh --- nix/systemConfigs.nix | 2 +- nix/systemModules/default.nix | 13 +++++-------- nix/systemModules/tests/default.nix | 11 +++++------ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/nix/systemConfigs.nix b/nix/systemConfigs.nix index 05161369d8..2dda5edea7 100644 --- a/nix/systemConfigs.nix +++ b/nix/systemConfigs.nix @@ -1,7 +1,7 @@ { self, inputs, ... }: let mkModules = system: [ - self.systemModules.ssh-config + self.systemModules.genesis ({ nixpkgs.hostPlatform = system; }) diff --git a/nix/systemModules/default.nix b/nix/systemModules/default.nix index 89d6c8de84..c29192f64a 100644 --- a/nix/systemModules/default.nix +++ b/nix/systemModules/default.nix @@ -5,14 +5,11 @@ imports = [ ./tests ]; flake = { systemModules = { - ssh-config = { - environment.etc."ssh/sshd_config.d/local.conf" = { - text = '' - Match Address 127.0.0.1,::1 - ForceCommand /bin/false - DisableForwarding yes - PermitTunnel no - ''; + genesis = { + #this file is just a placeholder to bootstrap + #the system manager, it will be replaced by real configurations + environment.etc."system-manager-genesis" = { + text = ""; user = "root"; group = "root"; mode = "0644"; diff --git a/nix/systemModules/tests/default.nix b/nix/systemModules/tests/default.nix index a40d49c630..a9178a9639 100644 --- a/nix/systemModules/tests/default.nix +++ b/nix/systemModules/tests/default.nix @@ -24,12 +24,11 @@ machine.activate() machine.wait_for_unit("system-manager.target") - with subtest("Verify ssh config"): - assert machine.file("/etc/ssh/sshd_config.d/local.conf").exists, "/etc/ssh/sshd_config.d/local.conf should exist" - assert machine.file("/etc/ssh/sshd_config.d/local.conf").mode == 0o644, "/etc/ssh/sshd_config.d/local.conf should have mode 0644" - assert machine.file("/etc/ssh/sshd_config.d/local.conf").user == "root", "/etc/ssh/sshd_config.d/local.conf should be owned by root" - assert machine.file("/etc/ssh/sshd_config.d/local.conf").group == "root", "/etc/ssh/sshd_config.d/local.conf should be owned by root" - assert machine.file("/etc/ssh/sshd_config.d/local.conf").contains("Match Address"), "/etc/ssh/sshd_config.d/local.conf should contain 'Match Address'" + with subtest("Verify genesis file"): + assert machine.file("/etc/system-manager-genesis").exists, "/etc/system-manager-genesis should exist" + assert machine.file("/etc/system-manager-genesis").mode == 0o644, "/etc/system-manager-genesis should have mode 0644" + assert machine.file("/etc/system-manager-genesis").user == "root", "/etc/system-manager-genesis should be owned by root" + assert machine.file("/etc/system-manager-genesis").group == "root", "/etc/system-manager-genesis should be owned by root" ''; }; }; From 9fb8bdfd2271c16a580e57876b684286b15ae930 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 13 Mar 2026 10:35:41 -0400 Subject: [PATCH 08/10] fix: rebase --- flake.lock | 12 ++++-------- nix/packages/default.nix | 4 +++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index e19c3aef42..6128330359 100644 --- a/flake.lock +++ b/flake.lock @@ -421,18 +421,17 @@ }, "system-manager": { "inputs": { - "flake-compat": "flake-compat_2", "nixpkgs": [ "nixpkgs" ], "userborn": "userborn" }, "locked": { - "lastModified": 1773396529, - "narHash": "sha256-jSnyKvIEqzmK+y0/4zb34sE7PMAd09p0Y+JNGR0nTmA=", + "lastModified": 1772184297, + "narHash": "sha256-hDt6ez1H5Cpkciose8cDclJWHbBPEMmJNphSAxKvkUc=", "owner": "numtide", "repo": "system-manager", - "rev": "1272183d7798866ecf1bbdb29fc0387581a3317a", + "rev": "6f5d1b4458db2b2776f76bb6449100bd0fabc1d8", "type": "github" }, "original": { @@ -493,10 +492,7 @@ }, "userborn": { "inputs": { - "flake-compat": [ - "system-manager", - "flake-compat" - ], + "flake-compat": "flake-compat_2", "flake-parts": "flake-parts_2", "nixpkgs": [ "system-manager", diff --git a/nix/packages/default.nix b/nix/packages/default.nix index 658d61c16d..48f56d626d 100644 --- a/nix/packages/default.nix +++ b/nix/packages/default.nix @@ -91,7 +91,6 @@ inherit (self'.packages) overlayfs-on-package; }; sync-exts-versions = pkgs.callPackage ./sync-exts-versions.nix { inherit (inputs') nix-editor; }; - system-manager = inputs'.system-manager.packages.default; trigger-nix-build = pkgs.callPackage ./trigger-nix-build.nix { }; update-readme = pkgs.callPackage ./update-readme.nix { }; supabase-cli = pkgs.callPackage ./supabase-cli.nix { }; @@ -116,6 +115,9 @@ cargo-pgrx_0_14_3 ; } + // lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { + system-manager = inputs'.system-manager.packages.default; + } // lib.optionalAttrs pkgs.stdenv.isDarwin { setup-darwin-linux-builder = pkgs.callPackage ./setup-darwin-linux-builder.nix { inherit inputs self; From e80d77140f61f8cef6127aad39ad28e585ed5db8 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 13 Mar 2026 10:37:29 -0400 Subject: [PATCH 09/10] tests: bump to test --- ansible/vars.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ansible/vars.yml b/ansible/vars.yml index ae5ecfb33b..39e6b20846 100644 --- a/ansible/vars.yml +++ b/ansible/vars.yml @@ -10,9 +10,9 @@ postgres_major: # Full version strings for each major version postgres_release: - postgresorioledb-17: "17.6.0.053-orioledb" - postgres17: "17.6.1.096" - postgres15: "15.14.1.096" + postgresorioledb-17: "17.6.0.053-orioledb-sysmg-1" + postgres17: "17.6.1.096-sysmg-1" + postgres15: "15.14.1.096-sysmg-1" # Non Postgres Extensions pgbouncer_release: 1.25.1 From f6e2bb49c018321ce534a8e851a82cfea3d71bf4 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 13 Mar 2026 10:42:13 -0400 Subject: [PATCH 10/10] chore: fmt --- nix/systemModules/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/systemModules/default.nix b/nix/systemModules/default.nix index c29192f64a..04161ef9f1 100644 --- a/nix/systemModules/default.nix +++ b/nix/systemModules/default.nix @@ -6,7 +6,7 @@ flake = { systemModules = { genesis = { - #this file is just a placeholder to bootstrap + #this file is just a placeholder to bootstrap #the system manager, it will be replaced by real configurations environment.etc."system-manager-genesis" = { text = "";