Hello *,
I have some changes planned for the past few weeks to the PostgreSQL packaging and NixOS module, and I plan on committing the first revision of them soon. The basics are outlined in issue #38616. The pull request implementing these changes is #38698. Here’s the overview of the changes as they stand.
I’m sending this to spread out the ideas to a wider audience and hopefully field any suggestions – I think these changes are strictly an improvement and will be welcome to anyone running PostgreSQL, but there are certainly things to still improve.
Nixpkgs updates
The main change that motivates all this is due to deficiencies in the Nixpkgs packaging for PostgreSQL, the main two being:
-
PostgreSQL extensions are not modular. Current extension builds of things like
timescaledb
are built against against a single version of Postgres – the version that is blessed with the namepostgres
inall-packages.nix
. This has various ramifications:- Extensions are only built against one version of PG, not all of them, so you get cache misses if you pick a non-default version
- There is no way to specify whether an extension builds against a PostgreSQL version or not. If you use something like Postgres 9.3 with timescaledb, you get a build failure, rather than an evaluation failure. This isn’t friendly.
- Usage of a non-default
postgres
version in NixOS requires ugly overrides, to make sure extensions built against the version specified. Using PostgreSQL 9.3 with extensions, but without overriding the default version (currently: 9.6) will result in strange runtime errors.
- There is no attribute set namespace for PostgreSQL versions. Following from 1), there is no set of namespaces for different versions of postgresql. Much like how we have package sets for major versions of Python or Haskell, we should also have package sets for major versions of PostgreSQL.
These changes introduce these additions. There is now a set of packages for PostgreSQL major versions, each set containing the extensions that are supported. This means top-level.nix
now has something more like:
postgresql93Packages = ... ;
postgresql94Packages = ... ;
postgresql95Packages = ... ;
postgresql96Packages = ... ;
postgresql10Packages = ... ;
postgresql11Packages = ... ;
# default version is 9.6
postgresqlPackages = postgresql96Packages;
Furthermore, each attribute set contains a .postgres
attribute, pointing to the actual server package, as well as an assortment of various extensions you can install – provided they can be built against the actual version of Postgres you want. For example, the following attributes exist:
postgresql96Packages.timescaledb
postgresql10Packages.timescaledb
But postgresql94Packages.timescaledb
does not exist – the timescale
extension is guarded as only supporting versions of PostgreSQL 9.6 or newer, using a new passthru
predicate in the expression
stdenv.mkDerivation rec {
name = "timescaledb-${version}";
version = "0.9.1";
...
passthru = {
versionCheck = builtins.compareVersions postgresql.version "9.6" >= 0;
};
meta = with stdenv.lib; {
....
};
};
There are some other knock-on changes, like the fact all the old attribute names have been deprecated for the new attrset-based naming. This means usage of things like plv8
will now throw an evaluation error, telling you to use postgresqlPackages.plv8
or whatnot.
Enhanced NixOS Module
The new NixOS module takes the above Nixpkgs enhancements and extends them to the module interface. In particular, there are two new service configuration options.
-
The new
.packages
attribute points to the postgres package set you want to use. It should be set to an attrset and not a derivation itself. For example, if you want to use PostgreSQL 9.4 instead of the default version, you would specifyservices.postgresql.packages = postgresql94Packages;
-
The new
.plugins
attribute specifies a predicate, which returns a list of extensions we should enable for Postgres. This is similar to other functions likeghcWithPackages
– the idea is that the function is provided an attrset, and it simply selects out some attributes it wants.
These changes keep backwards compatibility with the old NixOS module interface, so you don’t necessarily need to upgrade immediately. However, it’s unclear if backwards compatibility is so necessary, since it requires keeping old attribute names too.
For example, here was a snippet of my old NixOS module, running on my NixOS Ryzen server:
services.postgresql =
let
mkPlug = p: p.override { postgresql = config.services.postgresql.package; };
in {
enable = true;
package = pkgs.postgresql100;
dataDir = "/data/pgsql";
extraConfig = "shared_preload_libraries = 'timescaledb'";
extraPlugins = with pkgs; map mkPlug
[ postgis
timescaledb
cstore_fdw
pg_hll
pg_cron
pg_topn
];
};
This is the new snippet using this improved module, which I’ve been running nicely for several weeks:
services.postgresql = {
enable = true;
dataDir = "/data/pgsql";
packages = pkgs.postgresql10Packages;
plugins = p: with p; # brings the following names into scope
[ cstore_fdw
pg_cron
pg_hll
pg_jobmon
pg_repack
pg_topn
pgtap
plv8
postgis
timescaledb
];
extraConfig = "shared_preload_libraries = 'timescaledb'";
};
While the overall LOC isn’t really reduced, the scope for errors is greatly reduced – and the actual logic reads much better without being cluttered by local definitions.
Immediate improvements before pushing
My immediate work before pushing will be to upgrade all the NixOS module tests to the new API, but this is not yet done. This is the major blocker still remaining, IMO.
Future improvements
While I am not yet done, what you see above is basically what’s there right now. I believe this can be committed on its own and is a strict improvement.
But there are three major things I’d like to do still before 18.09:
-
Extract the logic from the NixOS module that bundles PostgreSQL with its extensions into an environment, necessary for loading extensions. Add it to Nixpkgs – and offer a
postgresWithPackages
function. For example, it’s completely reasonable to want to start a local database withpg_init
myself, with some extensions, for testing things. I don’t need NixOS at all for this, but it’s currently impossible to do this, because there’s no way of building an environment (buildEnv
) containing the right filesystem hierarchy. -
I’d like to improve the NixOS manual further and include all the documentation that was meant to be written.
-
I’d like to bump the default PostgreSQL version to PostgreSQL 10 in NixOS 18.09.
After NixOS 18.09 is released, all of the old supporting code for the old NixOS module, attribute names, etc can be deleted.
Please review & Feedback
If you have time to give it a good view, I’d appreciate any reviews on #38698. Even if you don’t have time to review, any feedback or a simple +1 would be much appreciated; I hope these are welcome changes.