postjevsql.git / nix / sidecar-image.nix
1# The sidecar as an OCI image (CLAUDE.md *Launching it*), for hosts
2# without nix: `docker load < result`. PostgreSQL 18 with postjevsql
3# (the per-major package) and contrib postgres_fdw, and the
4# `postjevsql-sidecar` CLI, whose `serve` is the entrypoint: it
5# initialises the cluster if empty, converges and syncs, and then execs
6# postgres. The one shell is dash as /bin/sh, for PostgreSQL's own
7# popen() and system(): initdb finds `postgres` by running
8# `postgres -V` through /bin/sh, and fails without it.
9#
10# Configuration is mounted, never built in:
11#
12# - the CLI's config at /etc/postjevsql-sidecar/config.toml;
13# - each secret from a file the config names (such as /run/secrets/…)
14#   or its env var, exactly one; both is refused by the CLI;
15# - postgres options after the command, e.g. `-- -c jev.model=…`.
16#
17# The server runs as `postgres` (uid 999), and the cluster lives in the
18# volume /var/lib/postgresql. Remote clients log in with SCRAM, so a
19# role needs a password before anyone outside the container can use it.
20{
21  lib,
22  dockerTools,
23  runCommand,
24  dash,
25  postgresql_18,
26  extension,
27  cli,
28}:
29let
30  postgresql = postgresql_18.withPackages (ps: [ (extension ps) ]);
31  uid = "999";
32  # initdb, peer auth and the server itself need the user to have a
33  # name; initdb and the server need /bin/sh.
34  nss = runCommand "postjevsql-sidecar-nss" { } ''
35    mkdir -p $out/etc
36    printf 'root:x:0:0::/root:/noshell\npostgres:x:${uid}:${uid}::/var/lib/postgresql:/noshell\n' > $out/etc/passwd
37    printf 'root:x:0:\npostgres:x:${uid}:\n' > $out/etc/group
38    mkdir -p $out/bin
39    ln -s ${dash}/bin/dash $out/bin/sh
40  '';
41in
42dockerTools.buildLayeredImage {
43  name = "postjevsql-sidecar";
44  tag = postgresql.version;
45  contents = [
46    postgresql
47    cli
48    nss
49  ];
50  fakeRootCommands = ''
51    mkdir -p var/lib/postgresql/data run/postgresql tmp etc/postjevsql-sidecar
52    chown -R ${uid}:${uid} var/lib/postgresql run/postgresql
53    chmod 0700 var/lib/postgresql/data
54    chmod 1777 tmp
55  '';
56  enableFakechroot = false;
57  config = {
58    User = "${uid}:${uid}";
59    Entrypoint = [
60      (lib.getExe cli)
61      "/etc/postjevsql-sidecar/config.toml"
62      "serve"
63    ];
64    Env = [
65      "PGDATA=/var/lib/postgresql/data"
66      "PATH=${postgresql}/bin"
67    ];
68    ExposedPorts."5432/tcp" = { };
69    Volumes."/var/lib/postgresql" = { };
70    WorkingDir = "/var/lib/postgresql";
71  };
72}