 
					Introduction
In this article, a PostgreSQL 9.6 instance is installed and customized (port, database and configuration files…) on Ubuntu 18.04.
 
          Preparing the environment
User postgres
As user root, create the user postgres (group dba, id 10005)
root@vpsfrsqlpac1% useradd postgres -g dba -d /opt/postgres -m -s /bin/bash -u 10005Binaries are installed in the directory /opt/postgres with the appropriate owner and rights :
root@vpsfrsqlpac1% mkdir /opt/postgres
root@vpsfrsqlpac1% chown postgres:dba /opt/postgresThe database will be installed in the directory /sqlpac/postgres/srvpg1 : 
root@vpsfrsqlpac1% mkdir /sqlpac/postgres
root@vpsfrsqlpac1% chown postgres:dba /sqlpac/postgres
postgres@vpsfrsqlpac1% mkdir /sqlpac/postgres/srvpg1Postgresql 9.6 distribution installation
Download the archive file PostgreSQL 9.6 for Linux 64 bits from EnterpriseDB.
          The distribution (version 9.6.15) is installed in the directory /opt/postgres/pgsql-9.6.15 :
postgres@vpsfrsqlpac1:/opt/postgres% gunzip postgresql-9.6.15-1-linux-x64-binaries.tar.gz
postgres@vpsfrsqlpac1:/opt/postgres% mkdir pgsql-9.6.15
postgres@vpsfrsqlpac1:/opt/postgres% tar xvf postgresql-9.6.15-2-linux-x64-binaries.tar \
                                              -C /opt/postgres/pgsql-9.6.15 \
                                              --strip-components=1A symbolic link pgsql-9.6 is created for an ease of use managing minor versions changes.
postgres@vpsfrsqlpac1:/opt/postgres% ln -fs  pgsql-9.6.15 pgsql-9.6For the user postgres, the environment variables below are initialized :
postgres@vpsfrsqlpac1:/opt/postgres%
          
export PATH=/opt/postgres/pgsql-9.6/bin
export LD_LIBRARY_PATH=/opt/postgres/pgsql-9.6/lib
export PGLIB=/opt/postgres/pgsql-9.6/lib
# Database directory
export PGDATA=/sqlpac/postgres/srvpg1
export PGBINHOME=/opt/postgres/pgsql-9.6Database initialization
With the environment variables previously defined ($PATH, $PGLIB…), run the utility pg_ctl with the option intidb
          from the directory where the database will be installed ($PGDATA : /sqlpac/postgres/srvpg1).
          No extra options are specified here (collations, timezones…), default ones will be applied.
postgres@vpsfrsqlpac1:/sqlpac/postgres/srvpg1% pg_ctl initdbThe files belonging to this database system will be owned by user "postgres". This user must also own the server process. The database cluster will be initialized with locale "en_US.UTF-8". The default database encoding has accordingly been set to "UTF8". The default text search configuration will be set to "english". Data page checksums are disabled. fixing permissions on existing directory /sqlpac/postgres/srvpg1 ... ok creating subdirectories ... ok selecting default max_connections ... 100 selecting default shared_buffers ... 128MB selecting default timezone ... Europe/Paris selecting dynamic shared memory implementation ... posix creating configuration files ... ok running bootstrap script ... ok performing post-bootstrap initialization ... ok syncing data to disk ... ok WARNING: enabling "trust" authentication for local connections You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. Success. You can now start the database server using: /opt/postgres/pgsql-9.6.15/bin/pg_ctl -D /sqlpac/postgres/srvpg1 -l logfile start
The instance structure is initialized.
postgres@vpsfrsqlpac1:/sqlpac/postgres/srvpg1% lsbase pg_commit_ts pg_ident.conf pg_notify pg_snapshots pg_subtrans PG_VERSION postgresql.conf global pg_dynshmem pg_logical pg_replslot pg_stat pg_tblspc pg_xlog pg_clog pg_hba.conf pg_multixact pg_serial pg_stat_tmp pg_twophase postgresql.auto.conf
Normalization
For this new instance srvpg1 : 2 environment variables are created as well as the associated directories.
| $CFG | /opt/postgres/dba/srvpg1/cfg | Configuration files ( postgresql.conf,pg_hba.conf,pg_ident.conf…) | 
| $LOG | /opt/postgres/dba/srvpg1/log | Log files | 
In the standard adopted, the configuration files postgresql.conf, pg_ident.conf and pg_hba.conf are 
moved from the database directory $PGDATA to the directory $CFG.
postgres@vpsfrsqlpac1:/sqlpac/postgres/srvpg1% mv $PGDATA/postgresql.conf $CFG
postgres@vpsfrsqlpac1:/sqlpac/postgres/srvpg1% mv $PGDATA/pg_ident.conf $CFG
postgres@vpsfrsqlpac1:/sqlpac/postgres/srvpg1% mv $PGDATA/pg_hba.conf $CFGRelocating the configuration files. Why this choice ? A human error can occur at any time (rm -rf *, chown…),
installing the configuration files in a custom path outside the database data files directory is safier. This is not the case for
the file recovery.conf (PostgreSQL <= version 11) and the files standby.signal and recovery.signal
(PostgreSQL >= version 12) : these files must be installed in the root database directory $PGDATA. The
file postgresql.auto.conf must also reside in the directory $PGDATA, but this file should not be edited manually
and only managed through the command ALTER SYSTEM.
The postgresql.conf file is modified in order to apply the customization : database directory,
configuration and log files directories, port, WAL files archiving… .
$CFG/postgresql.conf
data_directory = '/sqlpac/postgres/srvpg1'                        
hba_file = '/opt/postgres/dba/srvpg1/cfg/pg_hba.conf'        
ident_file = '/opt/postgres/dba/srvpg1/cfg/pg_ident.conf'
listen_addresses = '*'
port = 30001
max_connections = 100
# Archiving
archive_mode = on
archive_command = 'test ! -f /backup/postgres/log_archive/srvpg1/%f && cp %p /backup/postgres/log_archive/srvpg1/%f'
# Log
log_destination = 'stderr, csvlog'                        
logging_collector = on          
log_directory = '/opt/postgres/dba/srvpg1/log'               
log_filename = 'srvpg1.log'
log_min_messages = info
log_line_prefix = '%m '    #   %m = timestamp with milliseconds
log_statement = 'ddl'
log_timezone = 'Europe/Paris'
datestyle = 'iso, mdy'
timezone = 'Europe/Paris'
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'Use pg_ctl to start the database:
postgres@vpsfrsqlpac1% pg_ctl start -D $CFGLOG: MultiXact member wraparound protections are now enabled LOG: database system is ready to accept connections LOG: autovacuum launcher started
To stop, restart the database with pg_ctl : 
postgres@vpsfrsqlpac1% pg_ctl stop -D $CFG -m fast
postgres@vpsfrsqlpac1% pg_ctl restart -D $CFG -m fastBy default, the local user postgres is allowed to connect (definition in the file pg_hba.conf):
pg_hba.conf
# "local" is for Unix domain socket connections only
local   all             all                                     trustUse psql to connect. When the port is not the default one (5432), add the option -p <port>
postgres@vpsfrsqlpac1% psql postgres@vpsfrsqlpac1% psql -p 30001psql.bin (9.6.15) Type "help" for help. Cannot read termcap database; using dumb terminal settings. postgres=#
Sourcing a PostgreSQL environment
In a mutualized PostgreSQL environment on a host : multiple instances and versions… One can setup a bash script sourcing the environment for a PostgreSQL instance.
The default profile for the user postgres is the following : 
$HOME/.profile
## Custom profile
export DBA=$HOME/dba
export ETC=$DBA/etc
export PATH=$DBA/bin:$PATHFor each PostgreSQL instance, a configuration file $ETC/servername.pgq is created, it will initialize all the
useful environment variables. Example for srvpg1 / PostgreSQL 9.6 : 
source srvpg1.pgq| $PATH             PATH=/opt/postgres/pgsql-9.6/bin:$PATH$LD_LIBRARY_PATH  LD_LIBRARY_PATH=/opt/postgres/pgsql-9.6/lib
                  :$LD_LIBRARY_PATH | 
| $PGSRVNAME        srvpg1                                    Instance Name$PGVERSION        9.6                                       Version$PGPORT           30001                                     Port$PGBINHOME        /opt/postgres/pgsql-9.6                   PostgreSQL Distribution directory$PGLIB            /opt/postgres/pgsql-9.6/lib               PostgreSQL Librairies$PGDATA           /sqlpac/postgres/srvpg1                   Database files directory$PGWALDIR         /sqlpac/postgres/srvpg1/pg_xlog           WAL files directory$PGPASSFILE       /opt/postgres/dba/srvpg1/.srvpg1.pass     Password file$CFG              /opt/postgres/dba/srvpg1/cfg              Configuration files directory (postgresql.conf…)$LOG              /opt/postgres/dba/srvpg1/log              Log files directory$DMP              /backup/postgres/srvpg1                   Backup directory$ARC              /backup/postgres/log_archive/srvpg1       WAL archives directory | 
Some of the environment variables defined are system PostgreSQL variables : $PGLIB, $PGDATA, $PGPORT, $PGPASSFILE. For the complete liste of
PostgreSQL environment variables : Environment Variables | Chapter 33. libpq - C Library
Only the version $PGVERSION is defined in the file srvpg1.pgq, all the other environment variables are computed in the
bash script $DBA/etc/defaults_srv.bash.
$ETC/srvpg1.pgq
# PostgreSQL Server Instance Name
[ -z "$PGSRVNAME" ] && export PGSRVNAME=$(basename ${BASH_SOURCE[0]%.*})
# PostgreSQL  version
export PGVERSION=9.6
source $DBA/etc/defaults_srv.bash$ETC/defaults_srv.bash
#!/bin/bash
#
# PGBINHOME
PGBINHOME=/opt/postgres/pgsql-${PGVERSION}
export PGBINHOME
#
# Setting PATH and LD_LIBRARY_PATH Environment
LD_LIBRARY_PATH=$PGBINHOME/lib:$LD_LIBRARY_PATH
PATH=$PGBINHOME/bin:$PATH
export PATH LD_LIBRARY_PATH
export CFG=$DBA/$PGSRVNAME/cfg
export LOG=$DBA/$PGSRVNAME/log
export DMP=`readlink $DBA/$PGSRVNAME/dmp`
export ARC=`readlink $DBA/$PGSRVNAME/arc`
# Checking postgresql.conf file
[ ! -f ${CFG}/postgresql.conf ] && echo "Could not find postgresql.conf in $CFG, exiting..." && exit 3
#
# PG Variables
PGLIB=$PGBINHOME/lib
PGDATA=`cat $CFG/postgresql.conf | grep 'data_directory' | sed "s/ *= */=/g" | tr -d "'" | awk '{print $1}' | awk -F"=" '{print $2}'`
PGPORT=$(cat $CFG/postgresql.conf | grep -i '^port' | sed "s/ *= */=/g" | awk '{print $1}' | awk -F"=" '{print $2}')
[ -z "$PGPORT" ] && PGPORT=5432
PGPASSFILE=$DBA/$PGSRVNAME/.$PGSRVNAME.pass
# WAL DIR
# Automatic detection depending on the PostgreSQL version
PGWALDIR=$PGDATA/pg_wal   # By default pg_wal, PGSQL > 9
MAJOR_VERSION=`echo ${PGVERSION} | awk -F"." '{print $1}'`
[ $MAJOR_VERSION -lt 10 ] && PGWALDIR=$PGDATA/pg_xlog
export PGLIB PGDATA PGPORT PGWALDIR PGPASSFILEFor interactive Xterm sessions, useful aliases can be defined in the script .bash_profile,
especially the alias p which opens a PSQL session without prompting a password : 
$HOME/.bash_profile
if [ ! -z "$PGSRVNAME" ]; then
        PS1="\e[0;33m\u\e[m@\e[0;33m\h\e[m [\e[0;31m$PGSRVNAME\e[m] :\w > "
        # Title : 0 minimized, 1 maximized
        PROMPT_COMMAND='echo -ne "\033]0;${PGSRVNAME}@${HOSTNAME} ${PWD}\007"'
        # Alias
        
        alias p="psql"
        
        alias godat="cd $PGDATA"
        alias gowal="cd $PGWALDIR"
        alias godmp="cd $DMP"
        # Alias config file
        alias gocfg="cd $CFG"
        alias mcfg="vi ${CFG}/postgresql.conf"
        # Alias log files
        alias golog="cd ${LOG}"
        alias tlog="tail -50 ${LOG}/${PGSRVNAME}.log"
        alias flog="tail -f ${LOG}/${PGSRVNAME}.log"
        alias vlog="view ${LOG}/${PGSRVNAME}.log"
fiIf local connections methods are not set to trust in the configuration file pg_hba.conf due to the mutualization
with other users on the host,
check the secured permission 600 is given to the password file ($PGPASSFILE), otherwise psql raises a warning and
the password is prompted.
WARNING: password file "/opt/postgres/dba/srvpg1/.srvpg1.pass" has group or world access;
permissions should be u=rw (0600) or less
Password:postgres@vpsfrsqlpac1% chmod 600 /opt/postgres/dba/srvpg1/.srvpg1.passThe syntax in the password file is very easy :
HOST:PORT:DB_NAME:USER:PASSWORD
localhost:30001:postgres:postgres:**************There is no encryption or obfuscation of passwords in the password file (not yet), as is the case with --login-path and mysql_config_editor
for MySQL, but securing this file with the permission 600 is largely enough.