{ pkgs, lib, inputs, config, ... }: let admin_dbPasswordFile = lib.optionalString (lib.hasAttr "sops-nix" inputs) config.sops.secrets."software/postgres/admin_db/password".path; initScript = pkgs.writeText "init.sh" '' #!/bin/bash function create_user_and_database() { local database=$1 local user=$2 local extensions=$3 echo "### admin user: $POSTGRES_USER ###" echo " Creating database '$database'" echo " Creating user '$user'" psql -v --username "$POSTGRES_USER" -d "$POSTGRES_DB" <<-EOSQL CREATE USER $user; CREATE DATABASE $database; GRANT ALL PRIVILEGES ON DATABASE $database TO $user; EOSQL # Loop through extensions and create them for ext in $(echo "$extensions" | tr ',' ' '); do echo " - Installing extention $ext" psql -v --username "$POSTGRES_USER" -d "$database" -c "CREATE EXTENSION $ext;" done } if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then # Parse the JSON string database_names=$(echo "$POSTGRES_MULTIPLE_DATABASES" | jq -r '.[0] | keys[]') echo "Multiple database creation requested: $(echo "$database_names" | tr "\n" " ")" # Loop through each database and create it for db_name in $database_names; do user=$(echo "$POSTGRES_MULTIPLE_DATABASES" | jq -r ".[0] | .''${db_name} | .user") extensions=$(echo "$POSTGRES_MULTIPLE_DATABASES" | jq -r ".[0] | .''${db_name} | .extensions | join(\",\")") create_user_and_database "$db_name" "$user" "$extensions" done fi ''; pg_hbaConfig = pkgs.writeText "pg_hba.conf" '' none ''; pgsqlConfig = pkgs.writeText "postgresql.conf" '' listen_addresses = '*' port = 5432 max_connections = 100 shared_buffers = 24GB work_mem = 1GB maintenance_work_mem = 10GB autovacuum_work_mem = 2GB dynamic_shared_memory_type = posix wal_level = minimal checkpoint_timeout = 60min checkpoint_completion_target = 0.9 max_wal_size = 10GB min_wal_size = 80MB max_wal_senders = 0 random_page_cost = 1.0 effective_cache_size = 25GB jit = off log_line_prefix = '%m [%p] %q%u@%d ' log_timezone = 'Etc/UTC' cluster_name = 'postgres-docker' datestyle = 'iso, dmy' timezone = 'Etc/UTC' default_text_search_config = 'pg_catalog.english' ''; in { sops.secrets = { "software/postgres/admin_db/password" = {}; }; virtualisation.arion = { backend = "docker"; projects = { "db".settings.services."db".service = { restart = "unless-stopped"; build.context = "/nix/store"; build.dockerfile = builtins.baseNameOf "${pkgs.writeText "pgDockerfile" '' FROM postgres:16 # install packages RUN apt-get update \ && apt-get install -y --no-install-recommends \ postgresql-16-postgis \ jq \ && rm -rf /var/lib/apt/lists/* ''}"; command = ["postgres" "-c" "config_file=/etc/postgresql/postgresql.conf"]; environment = { POSTGRES_PASSWORD_FILE = admin_dbPasswordFile; POSTGRES_USER = "admin"; POSTGRES_DB = "admin_db"; PGDATA = "/var/lib/postgresql/data/pgdata"; POSTGRES_MULTIPLE_DATABASES = '' [ { "osm": { "user": "gis", "extensions": [ "hstore", "postgis" ] }, "bitcoin": { "user": "satoshi", "extensions": [] }, "btc_models": { "user": "dbt", "extensions": [] }, "dev_btc_models": { "user": "dbt", "extensions": [] } } ] ''; }; ports = ["5432:5432"]; volumes = [ # Mount pgdata to external zfs volume "/mnt/postgres:/var/lib/postgresql/data" # Mount config files # "${pg_hbaConfig}:/var/lib/postgres/data/pgdata/pg_hba.conf" "${pgsqlConfig}:/etc/postgresql/postgresql.conf" # Need to mount secret file "${admin_dbPasswordFile}:${admin_dbPasswordFile}" # PG init script to parse json specified in POSTGRES_MULTIPLE_DATABASES # creates databases, users and installs extensions for each database. "${initScript}:/docker-entrypoint-initdb.d/init.sh" ]; }; }; }; }