I incorporated RuntimeDirectory, as suggested, which removes the trap and mktemp. Also changed the paths to use agenix variables. I will mark this as the solution, but still welcome further critique.
systemd.services."postgresql-db-name-setup" = {
serviceConfig = {
Type = "oneshot";
User = "postgres";
};
requiredBy = "service-that-uses-db-name.service";
after = ["postgresql.service"];
path = with pkgs; [ postgresql_16 replace-secret];
serviceConfig = {
RuntimeDirectory = "postgresql-setup";
RuntimeDirectoryMode = "700";
};
script = ''
# set bash options for early fail and error output
set -o errexit -o pipefail -o nounset -o errtrace -o xtrace
shopt -s inherit_errexit
# Copy SQL template into temporary folder. The value of RuntimeDirectory is written into
# environment variable RUNTIME_DIRECTORY by systemd.
install --mode 600 ${./db-name.sql} ''$RUNTIME_DIRECTORY/init.sql
# fill SQL template with passwords
${pkgs.replace-secret}/bin/replace-secret @DB_ADMIN_PASSWORD@ ${config.age.secrets.postgresql-db-admin-password.path} ''$RUNTIME_DIRECTORY/init.sql
${pkgs.replace-secret}/bin/replace-secret @DB_USER_PASSWORD@ ${config.age.secrets.postgresql-db-user-password.path} ''$RUNTIME_DIRECTORY/init.sql
# run filled SQL template
psql db-name --file "''$RUNTIME_DIRECTORY/init.sql"
'';
};