Hackers,
I'd like to propose adding a flake.nix at the repository root. Attached
is a first cut.
This is not another build system. It doesn't touch configure, Meson, or the
Makefiles, it wires up what we already have. A flake is a small, declarative
manifest that tells Nix how to fetch our build dependencies and drop a
developer into a shell where ./configure && make or meson setup just
works, with bison, flex, perl, the optional libraries, and the docs
toolchain all present and pinned to compatible versions. nix develop gives
you that shell; nix build produces a server.
The reason to carry it in-tree: Nix users are a growing segment of the
PostgreSQL developer base across Linux, macOS, and increasingly the BSDs and
even illumos. Today each of them keeps a private flake to hack on Postgres, I
have one, and I know I'm not alone. These private copies drift, disagree on
which features to enable, and have to be re-derived every time someone new
wants to contribute from a Nix machine. A single blessed flake in the tree
gives everyone the same reproducible starting point and one place to keep it
current, the same argument that motivates the .editorconfig and CI files we
already ship.
The flake exposes a toggle for every optional feature configure and Meson
support, both build systems, and platform-appropriate defaults (io_uring,
libnuma, systemd, PAM on Linux; Bonjour on macOS), so it's a starting point,
not a policy so flip anything with an override.
One deliberate omission: there is no flake.lock. A lock file pins the exact
nixpkgs revision, which is the right call for an application that wants a
frozen environment, but wrong for us. We don't want to freeze contributors to
one snapshot of the dependency universe, and we don't want a lock file to
become one more thing that goes stale in the tree and needs bumping. Leaving
it out means the flake tracks whatever nixpkgs the developer already runs,
which is exactly the behavior a build-from-source developer wants. Anyone who
needs reproducibility can generate a lock locally; the tree stays clean.
It's meant to sit at the root next to configure, not in a subdirectory, so
that a fresh checkout is immediately usable with nix develop.
To use it you'll need to be in a Nix environment. This means a Nix-based OS
like NixOS or the macOS/Windows layers that bring in the Nix environment.
Start by reading the https://nixos.org/learn/ information.
With a flake you can start a "developer shell" (a "dev shell") which will have
all the dependencies required to build and run Postgres, no installing All The
Things (TM) if you buy into this model you have a predictable environment, a
shell with developer tools prepared and present always at hand when needed.
So, first start the dev(eloper) shell:
```
nix develop
# then, meson (the default direction):
meson setup build && ninja -C build
# or classic autoconf:
./configure && make -j
```
You don't have to enter the dev shell, you could instead build a server:
```
nix build # default: meson, all features for your platform
./result/bin/postgres --version
```
The presets:
```
nix build .#postgresql-debug # cassert + debug syms + injection points
nix build .#postgresql-minimal # no optional libs — fastest compile
nix build .#postgresql-autoconf # configure/make instead of meson
```
Flip any single feature (26 toggles) without a preset:
```
nix build --expr '(builtins.getFlake (toString
./.)).packages.x86_64-linux.postgresql.override { llvmSupport = true; cassert =
true; }'
```
Run without installing:
```
nix run . -- --version # runs postgres (mainProgram is psql, so:)
nix run .#postgresql -- --version
```
There are many other things we could add into the flake such as
cross-compilation, but to start off this should suffice. I hope this generates
interest and constructive debate. I'll say upfront, I'm not a Nix master,
just a user and I find it invaluable. I'm sure I'm not alone.
Feedback welcome.
best.
-gregFrom 0ceb158a9c610a017a1cf5298b58d469cf0d7646 Mon Sep 17 00:00:00 2001
From: Greg Burd <[email protected]>
Date: Mon, 24 Aug 2026 12:53:12 -0400
Subject: [PATCH v1] Add a Nix flake for reproducible builds and dev shells
Provide a flake.nix at the repository root so contributors on Nix-based
systems (Linux, macOS, and increasingly BSD and illumos) get a single,
in-tree starting point instead of maintaining private, drifting copies.
It doesn't add a build system: `nix develop` drops you into a shell with
bison, flex, perl, the optional libraries, and the docs toolchain on PATH,
where `meson setup` (the default) or `./configure` just works; `nix build`
produces a server.
Every optional configure/Meson feature is a boolean toggle with
platform-appropriate defaults (io_uring, libnuma, systemd, PAM on Linux;
Bonjour on macOS), so it is a starting point, not a policy. Presets cover
debug, minimal, and autoconf builds.
No flake.lock is committed on purpose: pinning nixpkgs would freeze
contributors to one dependency snapshot and add a file that goes stale in
the tree. The flake instead tracks whatever nixpkgs the developer already
runs; anyone needing reproducibility can generate a lock locally.
---
flake.nix | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 261 insertions(+)
create mode 100644 flake.nix
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 00000000000..ab376df4a76
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,261 @@
+{
+ description = "PostgreSQL, built from this source tree";
+
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
+ outputs =
+ { self, nixpkgs }:
+ let
+ systems = [
+ "x86_64-linux"
+ "aarch64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ ];
+ forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
+
+ # A single builder covering every optional feature ./configure and
+ # meson expose. Each feature is a boolean toggle wired to its
+ # buildInput + flag; flip any of them via .override { ... }.
+ # Platform-specific features default to their availability.
+ postgresqlFor =
+ pkgs:
+ let
+ inherit (pkgs) lib stdenv;
+ onLinux = stdenv.hostPlatform.isLinux;
+ onDarwin = stdenv.hostPlatform.isDarwin;
+ in
+ lib.makeOverridable (
+ {
+ # Build system: "meson" (ninja) or "autoconf" (configure/make).
+ # Meson is the default; it is the project's eventual sole build system.
+ buildSystem ? "meson",
+
+ # Optional library features (buildInput + flag).
+ icuSupport ? true,
+ readlineSupport ? true,
+ zlibSupport ? true,
+ opensslSupport ? true,
+ libxmlSupport ? true,
+ libxsltSupport ? true,
+ lz4Support ? true,
+ zstdSupport ? true,
+ uuidSupport ? true,
+ curlSupport ? true,
+
+ # Procedural languages.
+ perlSupport ? true,
+ pythonSupport ? true,
+ tclSupport ? true,
+
+ # Platform-gated features.
+ systemdSupport ? onLinux,
+ selinuxSupport ? false,
+ liburingSupport ? onLinux,
+ numaSupport ? onLinux,
+ gssSupport ? true,
+ ldapSupport ? false,
+ pamSupport ? onLinux,
+ bonjourSupport ? onDarwin,
+ llvmSupport ? false,
+
+ # Developer / build-shape toggles (no extra deps).
+ cassert ? false,
+ debugSymbols ? false,
+ tapTests ? true,
+ injectionPoints ? false,
+
+ # Documentation build (SGML/DocBook toolchain).
+ docs ? false,
+ }:
+ let
+ useMeson = buildSystem == "meson";
+
+ # feature -> { flag = configure arg; dep = buildInput or null; }
+ featureFlag = cond: flag: lib.optional cond flag;
+ featureDep = cond: dep: lib.optional cond dep;
+
+ configureFeatures = lib.flatten [
+ (featureFlag icuSupport "--with-icu")
+ (featureFlag (!icuSupport) "--without-icu")
+ (featureFlag (!readlineSupport) "--without-readline")
+ (featureFlag (!zlibSupport) "--without-zlib")
+ (featureFlag opensslSupport "--with-ssl=openssl")
+ (featureFlag libxmlSupport "--with-libxml")
+ (featureFlag libxsltSupport "--with-libxslt")
+ (featureFlag lz4Support "--with-lz4")
+ (featureFlag zstdSupport "--with-zstd")
+ (featureFlag uuidSupport "--with-uuid=e2fs")
+ (featureFlag curlSupport "--with-libcurl")
+ (featureFlag perlSupport "--with-perl")
+ (featureFlag pythonSupport "--with-python")
+ (featureFlag tclSupport "--with-tcl")
+ (featureFlag systemdSupport "--with-systemd")
+ (featureFlag selinuxSupport "--with-selinux")
+ (featureFlag liburingSupport "--with-liburing")
+ (featureFlag numaSupport "--with-libnuma")
+ (featureFlag gssSupport "--with-gssapi")
+ (featureFlag ldapSupport "--with-ldap")
+ (featureFlag pamSupport "--with-pam")
+ (featureFlag bonjourSupport "--with-bonjour")
+ (featureFlag llvmSupport "--with-llvm")
+ (featureFlag cassert "--enable-cassert")
+ (featureFlag debugSymbols "--enable-debug")
+ (featureFlag tapTests "--enable-tap-tests")
+ (featureFlag injectionPoints "--enable-injection-points")
+ ];
+
+ # meson uses -Dfeature=enabled/disabled instead of --with-*.
+ mesonFeatures = lib.flatten [
+ [ "-Dicu=${if icuSupport then "enabled" else "disabled"}" ]
+ [ "-Dreadline=${if readlineSupport then "enabled" else "disabled"}" ]
+ [ "-Dzlib=${if zlibSupport then "enabled" else "disabled"}" ]
+ [ "-Dssl=${if opensslSupport then "openssl" else "none"}" ]
+ [ "-Dlibxml=${if libxmlSupport then "enabled" else "disabled"}" ]
+ [ "-Dlibxslt=${if libxsltSupport then "enabled" else "disabled"}" ]
+ [ "-Dlz4=${if lz4Support then "enabled" else "disabled"}" ]
+ [ "-Dzstd=${if zstdSupport then "enabled" else "disabled"}" ]
+ [ "-Duuid=${if uuidSupport then "e2fs" else "none"}" ]
+ [ "-Dlibcurl=${if curlSupport then "enabled" else "disabled"}" ]
+ [ "-Dplperl=${if perlSupport then "enabled" else "disabled"}" ]
+ [ "-Dplpython=${if pythonSupport then "enabled" else "disabled"}" ]
+ [ "-Dpltcl=${if tclSupport then "enabled" else "disabled"}" ]
+ [ "-Dsystemd=${if systemdSupport then "enabled" else "disabled"}" ]
+ [ "-Dselinux=${if selinuxSupport then "enabled" else "disabled"}" ]
+ [ "-Dliburing=${if liburingSupport then "enabled" else "disabled"}" ]
+ [ "-Dlibnuma=${if numaSupport then "enabled" else "disabled"}" ]
+ [ "-Dgssapi=${if gssSupport then "enabled" else "disabled"}" ]
+ [ "-Dldap=${if ldapSupport then "enabled" else "disabled"}" ]
+ [ "-Dpam=${if pamSupport then "enabled" else "disabled"}" ]
+ [ "-Dbonjour=${if bonjourSupport then "enabled" else "disabled"}" ]
+ [ "-Dllvm=${if llvmSupport then "enabled" else "disabled"}" ]
+ [ "-Dcassert=${lib.boolToString cassert}" ]
+ [ "-Dtap_tests=${if tapTests then "enabled" else "disabled"}" ]
+ [ "-Dinjection_points=${lib.boolToString injectionPoints}" ]
+ ];
+
+ buildInputs = lib.flatten (with pkgs; [
+ (featureDep icuSupport icu)
+ (featureDep readlineSupport readline)
+ (featureDep zlibSupport zlib)
+ (featureDep opensslSupport openssl)
+ (featureDep libxmlSupport libxml2)
+ (featureDep libxsltSupport libxslt)
+ (featureDep lz4Support lz4)
+ (featureDep zstdSupport zstd)
+ (featureDep uuidSupport libuuid)
+ (featureDep curlSupport curl)
+ (featureDep perlSupport perl)
+ (featureDep pythonSupport python3)
+ (featureDep tclSupport tcl)
+ (featureDep systemdSupport systemdLibs)
+ (featureDep selinuxSupport libselinux)
+ (featureDep liburingSupport liburing)
+ (featureDep numaSupport numactl)
+ (featureDep gssSupport libkrb5)
+ (featureDep ldapSupport openldap)
+ (featureDep pamSupport linux-pam)
+ (featureDep llvmSupport llvmPackages.llvm)
+ ]);
+ in
+ stdenv.mkDerivation {
+ pname = "postgresql";
+ version = "20devel";
+ src = self;
+
+ strictDeps = true;
+
+ nativeBuildInputs = lib.flatten (with pkgs; [
+ bison
+ flex
+ perl
+ pkg-config
+ (featureDep useMeson meson)
+ (featureDep useMeson ninja)
+ (featureDep llvmSupport llvmPackages.llvm.dev)
+ (featureDep docs docbook-xsl-nons)
+ (featureDep docs docbook_xml_dtd_45)
+ (featureDep docs libxslt)
+ ]);
+
+ inherit buildInputs;
+
+ # ---- autoconf path ----
+ configureFlags = lib.optionals (!useMeson) configureFeatures;
+
+ # ---- meson path ----
+ mesonBuildType = "release";
+ mesonFlags = lib.optionals useMeson mesonFeatures;
+ dontUseMesonConfigure = !useMeson;
+ mesonAutoFeatures = "disabled";
+
+ enableParallelBuilding = true;
+ doCheck = tapTests;
+
+ meta = with lib; {
+ description = "Object-relational database management system, built from source";
+ homepage = "https://www.postgresql.org/";
+ license = licenses.postgresql;
+ platforms = platforms.unix;
+ mainProgram = "psql";
+ };
+ }
+ )
+ { };
+ in
+ {
+ packages = forAllSystems (pkgs: rec {
+ postgresql = postgresqlFor pkgs;
+ # Common presets built on top of the fully-parameterized default.
+ postgresql-autoconf = postgresql.override { buildSystem = "autoconf"; };
+ postgresql-debug = postgresql.override {
+ cassert = true;
+ debugSymbols = true;
+ injectionPoints = true;
+ };
+ postgresql-minimal = postgresql.override {
+ icuSupport = false;
+ readlineSupport = false;
+ zlibSupport = false;
+ opensslSupport = false;
+ libxmlSupport = false;
+ libxsltSupport = false;
+ lz4Support = false;
+ zstdSupport = false;
+ uuidSupport = false;
+ curlSupport = false;
+ perlSupport = false;
+ pythonSupport = false;
+ tclSupport = false;
+ systemdSupport = false;
+ liburingSupport = false;
+ numaSupport = false;
+ gssSupport = false;
+ pamSupport = false;
+ };
+ default = postgresql;
+ });
+
+ # `nix develop`: full toolchain for configure/make, meson, docs,
+ # TAP tests, and all three PLs â regardless of which features a
+ # given build enables.
+ devShells = forAllSystems (pkgs: {
+ default = pkgs.mkShell {
+ inputsFrom = [ self.packages.${pkgs.stdenv.hostPlatform.system}.postgresql ];
+ packages = with pkgs; [
+ ccache
+ docbook_xml_dtd_45
+ docbook-xsl-nons
+ libxslt
+ meson
+ ninja
+ perl
+ python3
+ tcl
+ ];
+ };
+ });
+
+ formatter = forAllSystems (pkgs: pkgs.nixfmt);
+ };
+}
--
2.54.0