{ pkgs, }: let inherit (pkgs) lib; in { name, default_config ? true, config ? { }, extra_config ? '''', package ? pkgs.postgresql, port ? 5432, bind ? "localhost", initialDatabases ? [ ], ... }: let merged_config = ( if default_config then { listen_addresses = "localhost"; port = 5432; shared_buffers = "128MB"; work_mem = "4MB"; logging_collector = false; } else { } ) // config // { unix_socket_directories = "."; listen_addresses = bind; inherit port; }; toStr = value: if true == value then "yes" else if false == value then "no" else if lib.isString value then "'${lib.replaceStrings [ "'" ] [ "''" ] value}'" else builtins.toString value; configFile = pkgs.writeTextFile { name = "postgresql.conf"; text = lib.concatStringsSep "\n" ( lib.mapAttrsToList (n: v: "${n} = ${toStr v}") ( lib.filterAttrs (lib.const (x: x != null)) merged_config ) ) + extra_config; }; setupInitialDatabases = if initialDatabases != [ ] then (lib.concatMapStrings (db: '' echo "Checking presence of database ${db.name}" dbAlreadyExists="$(echo "SELECT 1 AS result FROM pg_database WHERE datname='${db.name}';" | psql --dbname postgres | ${pkgs.gnugrep}/bin/grep -c 'exists = "1"' || true)" echo "$dbAlreadyExists" if [ 1 -ne "$dbAlreadyExists" ]; then echo "Creating database ${db.name}" echo 'CREATE DATABASE "${db.name}";' | psql --dbname postgres ${lib.optionalString (db.user != null && db.password != null) '' echo "Creating user ${db.user}" psql --dbname postgres <<'EOF' DO $$ BEGIN CREATE ROLE "${db.user}" WITH LOGIN PASSWORD '${db.password}'; EXCEPTION WHEN duplicate_object THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE; END $$; GRANT ALL PRIVILEGES ON DATABASE "${db.name}" TO "${db.user}"; \c ${db.name} GRANT ALL PRIVILEGES ON SCHEMA public TO "${db.user}"; EOF ''} else echo "Database ${db.name} already exists" fi '') initialDatabases) else ""; setupScript = pkgs.writeShellApplication { name = "setup-postgres"; runtimeInputs = [ package pkgs.coreutils ]; text = '' POSTGRES_RUN_INITIAL_SCRIPT="false" if [[ ! -d "$PGDATA" ]]; then initdb --no-instructions POSTGRES_RUN_INITIAL_SCRIPT="true" echo echo "PostgreSQL initdb process completed" echo fi cp ${configFile} "$PGDATA"/postgresql.conf if [[ "$POSTGRES_RUN_INITIAL_SCRIPT" == "true" ]]; then echo echo "PostgreSQL is setting up the initial database" echo pg_ctl -w start -o "-c listen_addresses= -p ${builtins.toString port}" ${setupInitialDatabases} pg_ctl -m fast -w stop else echo echo "Database directory exists. Skipping initialization" echo fi unset POSTGRES_RUN_INITIAL_SCRIPT ''; }; script = pkgs.writeShellApplication { name = "run-postgres"; text = '' set -euo pipefail PGDATA=$RUNTIME_PATH/${name} PGHOST=$RUNTIME_PATH/${name} PGPORT=${builtins.toString port} echo "Starting postgres with PGDATA=''${PGDATA} PGHOST=''${PGHOST} PGPORT=''${PGPORT}" export PGDATA PGHOST PGPORT ${setupScript}/bin/setup-postgres ${package}/bin/postgres ''; }; in # configFileCheck = pkgs.runCommand {} '' # ${package}/bin/postgres -D${configFile} -C config_file # touch $out # ''; { processes."${name}" = { command = "${script}/bin/run-postgres"; shutdown.signal = 2; readiness_probe = { exec.command = "${package}/bin/pg_isready -h $RUNTIME_PATH/${name} -p ${builtins.toString port} -d postgres"; initial_delay_seconds = 2; period_seconds = 2; timeout_seconds = 2; success_threshold = 1; failure_threshold = 5; }; availability.restart = "on_failure"; }; }