PostgreSQL DBA Step by Step Learning
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/
1. Environment
2. Verify Existing DB Setup
3. Pre-requisites
4. Take Backup
5. Prepare & Restore
5.1 Stop PostgreSQL and Clean Directories
5.2 Remove all from Data & WAL directories
5.3 Restore Data and WAL
A. Copy Backup files to $PGDATA
B. Copy WAL files from $PGDATA/pg_wal to /pgWal/pgsql17/wal/
C. Remove the existing $PGDATA/pg_wal directory
D. Create a symbolic link pointing $PGDATA/pg_wal to a separate WAL directory
E. Set Permissions
6. Start PostgreSQL
7. Final Verification
| ASPECT | Env |
|---|---|
| Hostname | lxicbpgdsgv01 |
| IP Address | 192.168.2.51 |
| OS | RHEL 9 |
| DB Version | PostgreSQL v17.6 |
| Archive mode | archive_mode=off |
| pgData | /pgData/pgsql17/data |
| WAL Directory | /pgWal/pgsql17/wal |
| Tablespace | pg_default |
| Databases | DELL, ORCL |
[postgres@lxicbpgdsgv01 ~]$ psql
psql (17.6)
Type "help" for help.
postgres=# \l+
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules | Access privileges | Size | Tablespace | Description
-----------+----------+----------+-----------------+-------------+-------------+--------+-----------+-----------------------+---------+------------+--------------------------------------------
dell | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | | 7763 kB | pg_default |
orcl | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | | 7907 kB | pg_default |
postgres | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | | 492 MB | pg_default | default administrative connection database
template0 | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | =c/postgres +| 7545 kB | pg_default | unmodifiable empty database
| | | | | | | | postgres=CTc/postgres | | |
template1 | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | =c/postgres +| 7723 kB | pg_default | default template for new databases
| | | | | | | | postgres=CTc/postgres | | |
(5 rows)
postgres=# \db
List of tablespaces
Name | Owner | Location
------------+----------+----------
pg_default | postgres |
pg_global | postgres |
(2 rows)
postgres=# \c dell
You are now connected to database "dell" as user "postgres".
dell=# select * from test.emp;
name | designation | project | company
------+-------------+---------+---------
Sugi | DBA | Jetstar | iGATE
Teja | DBA | RCM | iGATE
RAJ | DBA | RCM | iGATE
(3 rows)
dell=#
postgres=# SHOW wal_level;
wal_level
-----------
replica <----
(1 row)
postgres=#
postgres=# SHOW max_wal_senders;
max_wal_senders
-----------------
10
(1 row)
postgres=# SHOW archive_mode;
archive_mode
--------------
off
(1 row)
postgres=#
# TYPE DATABASE USER ADDRESS METHOD
# Local connections for replication (for pg_basebackup run locally)
local replication all trust
# Remote connections for replication (for pg_basebackup run remotely)
#host replication repl_user 192.168.2.52/32 scram-sha-256
postgres=# \du
List of roles
Role name | Attributes
-----------+------------------------------------------------------------
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
postgres=#
[postgres@lxicbpgdsgv01 ~]$ du -sh /pgData/pgsql17/data/
524M /pgData/pgsql17/data/
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ du -sh /pgWal/pgsql17/wal/
801M /pgWal/pgsql17/wal/
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ df -h /pgBackup/
Filesystem Size Used Avail Use% Mounted on
/dev/sdd1 100G 746M 100G 1% /pgBackup <----
[postgres@lxicbpgdsgv01 ~]$ mkdir -p /pgBackup/pgsql17/backup/basebackup_10OCT2025
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ mkdir -p /pgBackup/pgsql17/backup/log/
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -ltrh /pgWal/pgsql17/wal | wc -l
53 <----
[postgres@lxicbpgdsgv01 ~]$
pg_basebackup does not include all files from the pg_wal directory in the backup. Instead, it selectively includes only the WAL files required to make the base backup consistent at the point in time the backup was taken.
-- If you want to store the base backup and WAL files in separate backup directories.
nohup pg_basebackup -U postgres -D /pgBackup/pgsql17/backup/basebackup_10OCT2025 --waldir=/pgBackup/pgsql17/backup/wal_backup -Fp -Xs -P -v > /pgBackup/pgsql17/backup/log/basebackup_10OCT2025.log 2>&1 &
The --waldir option in pg_basebackup is supported only when using the plain format (-Fp), not with the tar format (-Ft).
If we want symlinks preserved → use (both -Fp & --waldir use together) -Fp --waldir=/pgWal/pgsql17/wal
-- OR --
The directory mush be empty: /pgBackup/pgsql17/backup/basebackup_10OCT2025
[postgres@lxicbpgdsgv01 ~]$ nohup pg_basebackup -U postgres -D /pgBackup/pgsql17/backup/basebackup_10OCT2025 -Fp -Xs -P -v > /pgBackup/pgsql17/backup/log/basebackup_10OCT2025.log 2>&1 &
[1] 4973
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ cat /pgBackup/pgsql17/backup/log/basebackup_10OCT2025.log
nohup: ignoring input
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/85000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_4975"
279413/536037 kB (52%), 0/1 tablespace (...asebackup_10OCT2025/base/5/16533)
536047/536047 kB (100%), 0/1 tablespace (...ckup_10OCT2025/global/pg_control)
536047/536047 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/85000158
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: renaming backup_manifest.tmp to backup_manifest
pg_basebackup: base backup completed <-----
[postgres@lxicbpgdsgv01 ~]$
[root@lxicbpgdsgv01 ~]# systemctl stop postgresql-17.service
[root@lxicbpgdsgv01 ~]#
[root@lxicbpgdsgv01 ~]# ps -ef | grep postgres
root 5057 3151 0 18:57 pts/0 00:00:00 grep --color=auto postgres
[root@lxicbpgdsgv01 ~]#
-- Remove all from PGDATA directory
[root@lxicbpgdsgv01 ~]# rm -rf /pgData/pgsql17/data/*
[root@lxicbpgdsgv01 ~]# ls -ltr /pgData/pgsql17/data/
total 0
[root@lxicbpgdsgv01 ~]#
-- Remove all from WAL directory
[root@lxicbpgdsgv01 ~]# rm -rf /pgWal/pgsql17/wal/*
[root@lxicbpgdsgv01 ~]# ls -ltr /pgWal/pgsql17/wal/
total 0
[root@lxicbpgdsgv01 ~]#
Since we did not use the -Fp format with the --waldir option, all required WAL files will be included in the backup under the default path: PGDATA/pg_wal.
[postgres@lxicbpgdsgv01 ~]$ ls -ltr /pgData/pgsql17/data/
total 0
[postgres@lxicbpgdsgv01 ~]$ cd /pgBackup/pgsql17/backup/basebackup_10OCT2025/
[postgres@lxicbpgdsgv01 basebackup_10OCT2025]$ cp -Rp * /pgData/pgsql17/data/
[postgres@lxicbpgdsgv01 basebackup_10OCT2025]$
[postgres@lxicbpgdsgv01 basebackup_10OCT2025]$ ls -ltr /pgData/pgsql17/data/
total 296
-rw-------. 1 postgres postgres 227 Oct 10 18:41 backup_label
drwx------. 4 postgres postgres 77 Oct 10 18:41 pg_wal
drwx------. 7 postgres postgres 59 Oct 10 18:41 base
drwx------. 4 postgres postgres 68 Oct 10 18:41 pg_logical
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_dynshmem
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_commit_ts
drwx------. 2 postgres postgres 110 Oct 10 18:41 log
-rw-------. 1 postgres postgres 1169 Oct 10 18:41 postgresql.conf.bkp_10sep2025
-rw-------. 1 postgres postgres 30702 Oct 10 18:41 postgresql.conf.bkp
-rw-------. 1 postgres postgres 88 Oct 10 18:41 postgresql.auto.conf
drwx------. 2 postgres postgres 18 Oct 10 18:41 pg_xact
-rw-------. 1 postgres postgres 3 Oct 10 18:41 PG_VERSION
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_twophase
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_tblspc
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_subtrans
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_stat_tmp
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_stat
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_snapshots
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_serial
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_replslot
drwx------. 2 postgres postgres 6 Oct 10 18:41 pg_notify
drwx------. 4 postgres postgres 36 Oct 10 18:41 pg_multixact
-rw-------. 1 postgres postgres 2640 Oct 10 18:41 pg_ident.conf
-rw-------. 1 postgres postgres 5600 Oct 10 18:41 pg_hba.conf
-rw-------. 1 postgres postgres 1171 Oct 10 18:41 postgresql.conf
drwx------. 2 postgres postgres 4096 Oct 10 18:41 global
-rw-------. 1 postgres postgres 30 Oct 10 18:41 current_logfiles
-rw-------. 1 postgres postgres 227736 Oct 10 18:41 backup_manifest
[postgres@lxicbpgdsgv01 basebackup_10OCT2025]$
[postgres@lxicbpgdsgv01 ~]$ ls -ltr /pgWal/pgsql17/wal/
total 0
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ cd /pgData/pgsql17/data/pg_wal/
[postgres@lxicbpgdsgv01 pg_wal]$ cp -Rp * /pgWal/pgsql17/wal
[postgres@lxicbpgdsgv01 pg_wal]$
[postgres@lxicbpgdsgv01 pg_wal]$ ls -ltr /pgWal/pgsql17/wal
total 16384
drwx------. 2 postgres postgres 6 Oct 10 18:41 summaries
drwx------. 2 postgres postgres 6 Oct 10 18:41 archive_status
-rw-------. 1 postgres postgres 16777216 Oct 10 18:41 000000010000000000000085
[postgres@lxicbpgdsgv01 pg_wal]$ cd
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ rm -rf /pgData/pgsql17/data/pg_wal
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ln -s /pgWal/pgsql17/wal /pgData/pgsql17/data/pg_wal
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -ld /pgData/pgsql17/data/pg_wal
lrwxrwxrwx. 1 postgres postgres 18 Oct 10 19:43 /pgData/pgsql17/data/pg_wal -> /pgWal/pgsql17/wal
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ chown -R postgres:postgres /pgData/pgsql17/data
[postgres@lxicbpgdsgv01 ~]$ chmod 700 /pgData/pgsql17/data
[postgres@lxicbpgdsgv01 ~]$ chown -R postgres:postgres /pgWal/pgsql17/wal
[postgres@lxicbpgdsgv01 ~]$ chmod 700 /pgWal/pgsql17/wal
[root@lxicbpgdsgv01 ~]# systemctl start postgresql-17.service
[root@lxicbpgdsgv01 ~]# systemctl status postgresql-17.service
● postgresql-17.service - PostgreSQL 17 database server
Loaded: loaded (/usr/lib/systemd/system/postgresql-17.service; enabled; preset: disabled)
Active: active (running) since Fri 2025-10-10 19:45:21 +08; 5s ago
Docs: https://www.postgresql.org/docs/17/static/
Process: 5230 ExecStartPre=/usr/pgsql-17/bin/postgresql-17-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
Main PID: 5235 (postgres)
Tasks: 7 (limit: 15835)
Memory: 34.2M
CPU: 94ms
CGroup: /system.slice/postgresql-17.service
├─5235 /usr/pgsql-17/bin/postgres -D /pgData/pgsql17/data/
├─5236 "postgres: logger "
├─5237 "postgres: checkpointer "
├─5238 "postgres: background writer "
├─5240 "postgres: walwriter "
├─5241 "postgres: autovacuum launcher "
└─5242 "postgres: logical replication launcher "
Oct 10 19:45:21 lxicbpgdsgv01.rajasekhar.com systemd[1]: Starting PostgreSQL 17 database server...
Oct 10 19:45:21 lxicbpgdsgv01.rajasekhar.com postgres[5235]: 2025-10-10 19:45:21.177 +08 [5235] LOG: redirecting log output to logging collector process
Oct 10 19:45:21 lxicbpgdsgv01.rajasekhar.com postgres[5235]: 2025-10-10 19:45:21.177 +08 [5235] HINT: Future log output will appear in directory "log".
Oct 10 19:45:21 lxicbpgdsgv01.rajasekhar.com systemd[1]: Started PostgreSQL 17 database server.
[root@lxicbpgdsgv01 ~]#
[postgres@lxicbpgdsgv01 ~]$ psql
psql (17.6)
Type "help" for help.
postgres=# \l+
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | Locale | ICU Rules | Access privileges | Size | Tablespace | Description
-----------+----------+----------+-----------------+-------------+-------------+--------+-----------+-----------------------+---------+------------+--------------------------------------------
dell | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | | 7609 kB | pg_default |
orcl | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | | 7753 kB | pg_default |
postgres | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | | 492 MB | pg_default | default administrative connection database
template0 | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | =c/postgres +| 7545 kB | pg_default | unmodifiable empty database
| | | | | | | | postgres=CTc/postgres | | |
template1 | postgres | UTF8 | libc | en_SG.UTF-8 | en_SG.UTF-8 | | | =c/postgres +| 7569 kB | pg_default | default template for new databases
| | | | | | | | postgres=CTc/postgres | | |
(5 rows)
postgres=# \db
List of tablespaces
Name | Owner | Location
------------+----------+----------
pg_default | postgres |
pg_global | postgres |
(2 rows)
postgres=# \c dell
You are now connected to database "dell" as user "postgres".
dell=# select * from test.emp;
name | designation | project | company
------+-------------+---------+---------
Sugi | DBA | Jetstar | iGATE
Teja | DBA | RCM | iGATE
RAJ | DBA | RCM | iGATE
(3 rows)
dell=#
Caution: Your use of any information or materials on this website is entirely at your own risk. It is provided for educational purposes only. It has been tested internally, however, we do not guarantee that it will work for you. Ensure that you run it in your test environment before using.
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/
1. Verify Existing Archive Mode
2. Edit the archive settings
3. Restart PostgreSQL
4. Verify Current Mode
5. Verify WAL Archiving Behavior
postgres=# SHOW archive_mode;
archive_mode
--------------
on <------
(1 row)
postgres=#
postgres=# SHOW archive_command;
archive_command
-------------------------------
cp %p /pgArch/pgsql17/arch/%f <-----
(1 row)
postgres=#
[postgres@lxicbpgdsgv01 ~]$ cp /pgData/pgsql17/data/postgresql.conf /pgData/pgsql17/data/postgresql.conf.bkp_10sep2025
[postgres@lxicbpgdsgv01 ~]$ vi /pgData/pgsql17/data/postgresql.conf
#archive_mode = on
#archive_command = 'cp %p /pgArch/pgsql17/arch/%f'
[root@lxicbpgdsgv01 ~]# systemctl stop postgresql-17.service
[root@lxicbpgdsgv01 ~]#
[root@lxicbpgdsgv01 ~]# systemctl start postgresql-17.service
[root@lxicbpgdsgv01 ~]#
[root@lxicbpgdsgv01 ~]# systemctl status postgresql-17.service
● postgresql-17.service - PostgreSQL 17 database server
Loaded: loaded (/usr/lib/systemd/system/postgresql-17.service; enabled; preset: disabled)
Active: active (running) since Thu 2025-10-09 16:34:01 +08; 3s ago
Docs: https://www.postgresql.org/docs/17/static/
Process: 3492 ExecStartPre=/usr/pgsql-17/bin/postgresql-17-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
Main PID: 3497 (postgres)
Tasks: 7 (limit: 15835)
Memory: 17.6M
CPU: 92ms
CGroup: /system.slice/postgresql-17.service
├─3497 /usr/pgsql-17/bin/postgres -D /pgData/pgsql17/data/
├─3498 "postgres: logger "
├─3499 "postgres: checkpointer "
├─3500 "postgres: background writer "
├─3502 "postgres: walwriter "
├─3503 "postgres: autovacuum launcher "
└─3504 "postgres: logical replication launcher "
Oct 09 16:34:01 lxicbpgdsgv01.rajasekhar.com systemd[1]: Starting PostgreSQL 17 database server...
Oct 09 16:34:01 lxicbpgdsgv01.rajasekhar.com postgres[3497]: 2025-10-09 16:34:01.929 +08 [3497] LOG: redirecting log output to logging collector process
Oct 09 16:34:01 lxicbpgdsgv01.rajasekhar.com postgres[3497]: 2025-10-09 16:34:01.929 +08 [3497] HINT: Future log output will appear in directory "log".
Oct 09 16:34:01 lxicbpgdsgv01.rajasekhar.com systemd[1]: Started PostgreSQL 17 database server.
[root@lxicbpgdsgv01 ~]#
[postgres@lxicbpgdsgv01 ~]$ psql
psql (17.6)
Type "help" for help.
postgres=# SHOW archive_mode;
archive_mode
--------------
off <------ it's disabled
(1 row)
postgres=# SHOW archive_command;
archive_command
-----------------
(disabled) <-------
(1 row)
postgres=#
postgres=# CHECKPOINT;
CHECKPOINT
postgres=#
postgres=# CHECKPOINT;
CHECKPOINT
postgres=# CHECKPOINT;
CHECKPOINT
postgres=#
postgres=# exit
postgres=# SELECT pg_switch_wal();
pg_switch_wal
---------------
0/44000000
(1 row)
postgres=# SELECT pg_switch_wal();
pg_switch_wal
---------------
0/44000000
(1 row)
postgres=# SELECT pg_switch_wal();
pg_switch_wal
---------------
0/44000000
(1 row)
postgres=#
[postgres@lxicbpgdsgv01 ~]$ ls -ltr /pgArch/pgsql17/arch/
total 0 <---- Archivelogs not generating
[postgres@lxicbpgdsgv01 ~]$
Caution: Your use of any information or materials on this website is entirely at your own risk. It is provided for educational purposes only. It has been tested internally, however, we do not guarantee that it will work for you. Ensure that you run it in your test environment before using.
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/
0. Aim
1. Verify Existing pg_wal Directory
2. Create the New Directory
3. Stop PostgreSQL Service
4. Copy WAL Files to New Location
5. Backup Old WAL Directory
6. Create Symlink
7. Fix Permissions
8. Start PostgreSQL Service
9. Verify WAL Functionality
10. Remove Old WAL Directory (Optional)
To change the PostgreSQL 17 WAL File directory from its default location to new mount point
From : /var/lib/pgsql/17/data
TO : /pgData/pgsql17/data
[postgres@lxicbpgdsgv01 ~]$ psql
psql (17.6)
Type "help" for help.
postgres=# SELECT current_setting('data_directory') || '/pg_wal' AS wal_directory;
wal_directory
-----------------------------
/pgData/pgsql17/data/pg_wal <-----
(1 row)
postgres=# exit
[postgres@lxicbpgdsgv01 ~]$ ls -lrth /pgData/pgsql17/data/pg_wal
total 48M
drwx------. 2 postgres postgres 6 Sep 30 21:50 summaries
-rw-------. 1 postgres postgres 16M Oct 8 04:24 000000010000000000000008
-rw-------. 1 postgres postgres 16M Oct 8 16:08 000000010000000000000006
-rw-------. 1 postgres postgres 16M Oct 8 16:08 000000010000000000000007
drwx------. 2 postgres postgres 43 Oct 8 16:08 archive_status
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ du -sh /pgData/pgsql17/data/pg_wal
48M /pgData/pgsql17/data/pg_wal
[postgres@lxicbpgdsgv01 ~]$
[root@lxicbpgdsgv01 ~]# mkdir -p /pgData/pgsql17/data
[root@lxicbpgdsgv01 ~]# chown postgres:postgres /pgData/pgsql17/data
[root@lxicbpgdsgv01 ~]# chmod 700 /pgData/pgsql17/data
[root@lxicbpgdsgv01 ~]#
[root@lxicbpgdsgv01 ~]# systemctl stop postgresql-17.service
[root@lxicbpgdsgv01 ~]#
[root@lxicbpgdsgv01 ~]# ps -ef | grep postgres
root 6887 6721 0 16:08 pts/0 00:00:00 grep --color=auto postgres
[root@lxicbpgdsgv01 ~]#
[postgres@lxicbpgdsgv01 ~]$ nohup rsync -avh --progress /pgData/pgsql17/data/pg_wal/ /pgWal/pgsql17/wal/ > rsync_pgwal.log 2>&1 &
[1] 6943
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$
[1]+ Done nohup rsync -avh --progress /pgData/pgsql17/data/pg_wal/ /pgWal/pgsql17/wal/ > rsync_pgwal.log 2>&1
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ cat rsync_pgwal.log
nohup: ignoring input
sending incremental file list
./
000000010000000000000006
16.78M 100% 81.89MB/s 0:00:00 (xfr#1, to-chk=5/7)
000000010000000000000007
16.78M 100% 43.84MB/s 0:00:00 (xfr#2, to-chk=4/7)
000000010000000000000008
16.78M 100% 31.07MB/s 0:00:00 (xfr#3, to-chk=3/7)
archive_status/
archive_status/000000010000000000000006.done
0 100% 0.00kB/s 0:00:00 (xfr#4, to-chk=0/7)
summaries/
sent 50.34M bytes received 107 bytes 33.56M bytes/sec
total size is 50.33M speedup is 1.00
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -lrth /pgWal/pgsql17/wal/
total 48M
drwx------. 2 postgres postgres 6 Sep 30 21:50 summaries
-rw-------. 1 postgres postgres 16M Oct 8 04:24 000000010000000000000008
-rw-------. 1 postgres postgres 16M Oct 8 16:08 000000010000000000000006
-rw-------. 1 postgres postgres 16M Oct 8 16:08 000000010000000000000007
drwx------. 2 postgres postgres 43 Oct 8 16:08 archive_status
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ du -sh /pgWal/pgsql17/wal/
48M /pgWal/pgsql17/wal/
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ mv /pgData/pgsql17/data/pg_wal /pgData/pgsql17/data/pg_wal.bak
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -ld /pgData/pgsql17/data/pg_wal
ls: cannot access '/pgData/pgsql17/data/pg_wal': No such file or directory
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -ld /pgData/pgsql17/data/pg_wal.bak
drwx------. 4 postgres postgres 141 Oct 8 16:08 /pgData/pgsql17/data/pg_wal.bak
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ln -s /pgWal/pgsql17/wal /pgData/pgsql17/data/pg_wal
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -ltr /pgData/pgsql17/data/pg_wal
lrwxrwxrwx. 1 postgres postgres 18 Oct 8 16:16 /pgData/pgsql17/data/pg_wal -> /pgWal/pgsql17/wal
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ chown -R postgres:postgres /pgWal/pgsql17/wal[root@lxicbpgdsgv01 ~]# systemctl start postgresql-17.service
[root@lxicbpgdsgv01 ~]#
[root@lxicbpgdsgv01 ~]# systemctl status postgresql-17.service
● postgresql-17.service - PostgreSQL 17 database server
Loaded: loaded (/usr/lib/systemd/system/postgresql-17.service; enabled; preset: disabled)
Active: active (running) since Wed 2025-10-08 16:20:46 +08; 7s ago
Docs: https://www.postgresql.org/docs/17/static/
Process: 7079 ExecStartPre=/usr/pgsql-17/bin/postgresql-17-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
Main PID: 7084 (postgres)
Tasks: 8 (limit: 15835)
Memory: 18.1M
CPU: 94ms
CGroup: /system.slice/postgresql-17.service
├─7084 /usr/pgsql-17/bin/postgres -D /pgData/pgsql17/data/
├─7086 "postgres: logger "
├─7087 "postgres: checkpointer "
├─7088 "postgres: background writer "
├─7090 "postgres: walwriter "
├─7091 "postgres: autovacuum launcher "
├─7092 "postgres: archiver "
└─7093 "postgres: logical replication launcher "
Oct 08 16:20:46 lxicbpgdsgv01.rajasekhar.com systemd[1]: Starting PostgreSQL 17 database server...
Oct 08 16:20:46 lxicbpgdsgv01.rajasekhar.com postgres[7084]: 2025-10-08 16:20:46.162 +08 [7084] LOG: redirecting log output to logging collector process
Oct 08 16:20:46 lxicbpgdsgv01.rajasekhar.com postgres[7084]: 2025-10-08 16:20:46.162 +08 [7084] HINT: Future log output will appear in directory "log".
Oct 08 16:20:46 lxicbpgdsgv01.rajasekhar.com systemd[1]: Started PostgreSQL 17 database server.
[root@lxicbpgdsgv01 ~]#
-- Load WAL File generation (Testing)
postgres=# -- Create test table
DROP TABLE IF EXISTS wal_test;
CREATE TABLE wal_test (
id serial PRIMARY KEY,
data text
);
-- Generate WAL traffic
DO $$
DECLARE
i integer;
BEGIN
FOR i IN 1..50 LOOP
-- INSERT: 10,000 rows
INSERT INTO wal_test (data)
SELECT repeat('wal_test_data_', 50)
FROM generate_series(1, 10000);
-- UPDATE: 5,000 rows using CTE with LIMIT
WITH to_update AS (
SELECT id FROM wal_test WHERE id % 2 = 0 LIMIT 5000
)
UPDATE wal_test
SET data = data || '_updated'
WHERE id IN (SELECT id FROM to_update);
-- DELETE: 5,000 rows using CTE with LIMIT
WITH to_delete AS (
SELECT id FROM wal_test WHERE id % 3 = 0 LIMIT 5000
)
DELETE FROM wal_test
WHERE id IN (SELECT id FROM to_delete);
-- Commit to flush WAL
COMMIT;
-- Optional pause to slow down the loop
PERFORM pg_sleep(0.5);
END LOOP;
END$$;
DROP TABLE
CREATE TABLE
DO
postgres=# exit
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -lrth /pgWal/pgsql17/wal
total 752M
drwx------. 2 postgres postgres 6 Sep 30 21:50 summaries
-rw-------. 1 postgres postgres 16M Oct 8 16:34 000000010000000000000009
-rw-------. 1 postgres postgres 16M Oct 8 16:34 00000001000000000000000A
-rw-------. 1 postgres postgres 16M Oct 8 16:34 00000001000000000000000B
-rw-------. 1 postgres postgres 16M Oct 8 16:34 00000001000000000000000C
-rw-------. 1 postgres postgres 16M Oct 8 16:34 00000001000000000000000D
-rw-------. 1 postgres postgres 16M Oct 8 16:34 00000001000000000000000E
-rw-------. 1 postgres postgres 16M Oct 8 16:34 00000001000000000000000F
-rw-------. 1 postgres postgres 16M Oct 8 16:34 000000010000000000000010
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000011
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000012
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000013
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000014
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000015
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000016
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000017
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000018
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000019
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000001A
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000001B
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000001C
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000001D
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000001E
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000001F
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000020
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000021
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000022
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000023
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000024
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000025
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000026
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000027
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000028
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000029
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000002A
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000002B
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000002C
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000002D
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000002E
-rw-------. 1 postgres postgres 16M Oct 8 16:35 00000001000000000000002F
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000030
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000031
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000032
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000033
-rw-------. 1 postgres postgres 16M Oct 8 16:35 000000010000000000000034
-rw-------. 1 postgres postgres 16M Oct 8 16:36 000000010000000000000035
drwx------. 2 postgres postgres 4.0K Oct 8 16:36 archive_status
-rw-------. 1 postgres postgres 16M Oct 8 16:36 xlogtemp.7289
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ ls -ld /pgData/pgsql17/data/pg_wal.bak
drwx------. 4 postgres postgres 141 Oct 8 16:08 /pgData/pgsql17/data/pg_wal.bak
[postgres@lxicbpgdsgv01 ~]$ rm -rf /pgData/pgsql17/data/pg_wal.bak
[postgres@lxicbpgdsgv01 ~]$
Caution: Your use of any information or materials on this website is entirely at your own risk. It is provided for educational purposes only. It has been tested internally, however, we do not guarantee that it will work for you. Ensure that you run it in your test environment before using.
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/
Table of Contents
1. Backup All Databases
2. Backup Users/Roles Definition
3. Backup Tablespaces Definition
4. Backup Schema Only (No Data)
5. Backup Data Only (No Schema)
6. Backup Data as INSERT Commands
7. Backup Global Objects Only (No Databases)
9. Restore to Another Host (Different Tablespace Paths)
[postgres@lxtrdpgdsgv01 ~]$ pg_dumpall --help
pg_dumpall extracts a PostgreSQL database cluster into an SQL script file.
Usage:
pg_dumpall [OPTION]...
General options:
-f, --file=FILENAME output file name
-v, --verbose verbose mode
-V, --version output version information, then exit
--lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock
-?, --help show this help, then exit
Options controlling the output content:
-a, --data-only dump only the data, not the schema
-c, --clean clean (drop) databases before recreating
-E, --encoding=ENCODING dump the data in encoding ENCODING
-g, --globals-only dump only global objects, no databases
-O, --no-owner skip restoration of object ownership
-r, --roles-only dump only roles, no databases or tablespaces
-s, --schema-only dump only the schema, no data
-S, --superuser=NAME superuser user name to use in the dump
-t, --tablespaces-only dump only tablespaces, no databases or roles
-x, --no-privileges do not dump privileges (grant/revoke)
--binary-upgrade for use by upgrade utilities only
--column-inserts dump data as INSERT commands with column names
--disable-dollar-quoting disable dollar quoting, use SQL standard quoting
--disable-triggers disable triggers during data-only restore
--exclude-database=PATTERN exclude databases whose name matches PATTERN
--extra-float-digits=NUM override default setting for extra_float_digits
--if-exists use IF EXISTS when dropping objects
--inserts dump data as INSERT commands, rather than COPY
--load-via-partition-root load partitions via the root table
--no-comments do not dump comments
--no-publications do not dump publications
--no-role-passwords do not dump passwords for roles
--no-security-labels do not dump security label assignments
--no-subscriptions do not dump subscriptions
--no-sync do not wait for changes to be written safely to disk
--no-table-access-method do not dump table access methods
--no-tablespaces do not dump tablespace assignments
--no-toast-compression do not dump TOAST compression methods
--no-unlogged-table-data do not dump unlogged table data
--on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands
--quote-all-identifiers quote all identifiers, even if not key words
--restrict-key=RESTRICT_KEY use provided string as psql \restrict key
--rows-per-insert=NROWS number of rows per INSERT; implies --inserts
--use-set-session-authorization
use SET SESSION AUTHORIZATION commands instead of
ALTER OWNER commands to set ownership
Connection options:
-d, --dbname=CONNSTR connect using connection string
-h, --host=HOSTNAME database server host or socket directory
-l, --database=DBNAME alternative default database
-p, --port=PORT database server port number
-U, --username=NAME connect as specified database user
-w, --no-password never prompt for password
-W, --password force password prompt (should happen automatically)
--role=ROLENAME do SET ROLE before dump
If -f/--file is not used, then the SQL script will be written to the standard
output.
Report bugs to <pgsql-bugs@lists.postgresql.org>.
PostgreSQL home page: <https://www.postgresql.org/>
[postgres@lxtrdpgdsgv01 ~]$
Note: pg_dumpall does not support custom format backups. The pg_dumpall utility is used to back up an entire PostgreSQL environment, including: * Roles and users * Tablespaces * All databases (schemas and data) It is especially useful for: * Full cluster migrations * Disaster recovery * Environment replication across dev, QA, and prod When restoring to a different host, you'll need to: * Update tablespace paths (using tools like sed) * Pre-create required tablespace directories This ensures compatibility and successful restoration across different environments.
Backup
[postgres@lxtrdpgdsgv01 ~]$ nohup pg_dumpall -U postgres -v -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql > /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.log 2>&1 & [1] 5610 [postgres@lxtrdpgdsgv01 ~]$ [postgres@lxtrdpgdsgv01 ~]$ cat /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.log | grep -i /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql pg_dumpall: running ""/usr/pgsql-15/bin/pg_dump" -v -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql -Fa 'user=postgres dbname=template1'" pg_dumpall: running ""/usr/pgsql-15/bin/pg_dump" -v -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql --create -Fa 'user=postgres dbname=edpua'" pg_dumpall: running ""/usr/pgsql-15/bin/pg_dump" -v -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql --create -Fa 'user=postgres dbname=gebua'" pg_dumpall: running ""/usr/pgsql-15/bin/pg_dump" -v -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql --create -Fa 'user=postgres dbname=orcl'" pg_dumpall: running ""/usr/pgsql-15/bin/pg_dump" -v -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql -Fa 'user=postgres dbname=postgres'" [postgres@lxtrdpgdsgv01 ~]$ [postgres@lxtrdpgdsgv01 ~]$ psql psql (15.14) Type "help" for help. postgres=# \c orcl You are now connected to database "orcl" as user "postgres". orcl=# SELECT orcl-# schemaname || '.' || relname AS table_name, orcl-# pg_size_pretty(pg_total_relation_size(relid)) AS total_size, orcl-# pg_size_pretty(pg_relation_size(relid)) AS table_size, orcl-# pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS index_size orcl-# FROM orcl-# pg_catalog.pg_statio_user_tables orcl-# ORDER BY orcl-# pg_total_relation_size(relid) DESC; table_name | total_size | table_size | index_size ------------------+------------+------------+------------ trd.metrics_high | 799 MB | 498 MB | 301 MB trd.metrics_mid | 638 MB | 398 MB | 240 MB trd.employees | 493 MB | 322 MB | 172 MB trd.sales_2023 | 399 MB | 249 MB | 150 MB trd.sales_2024 | 399 MB | 249 MB | 150 MB trd.sales_2022 | 399 MB | 249 MB | 150 MB trd.sales_2021 | 398 MB | 248 MB | 150 MB trd.metrics_low | 159 MB | 99 MB | 60 MB trd.test_data | 71 MB | 50 MB | 21 MB trd.emp_summary | 24 kB | 8192 bytes | 16 kB trd.metrics_rest | 8192 bytes | 0 bytes | 8192 bytes (11 rows) orcl=#
2. Backup users/roles definition
[postgres@lxtrdpgdsgv01 ~]$ nohup pg_dumpall -U postgres --roles-only -v -f /pgBackup/pgsql15/backup/roles.sql > /pgBackup/pgsql15/backup/roles.log 2>&1 &
[1] 5205
[postgres@lxtrdpgdsgv01 ~]$
3. Backup tablespaces definition
[postgres@lxtrdpgdsgv01 ~]$ nohup pg_dumpall -U postgres --tablespaces-only -v -f /pgBackup/pgsql15/backup/tablespaces.sql > /pgBackup/pgsql15/backup/tablespaces.log 2>&1 &
[1] 5183
[postgres@lxtrdpgdsgv01 ~]$
4. Backup dump only the schema, no data
[postgres@lxtrdpgdsgv01 ~]$ nohup pg_dumpall -U postgres --schema-only -v -f /pgBackup/pgsql15/backup/schemas.sql > /pgBackup/pgsql15/backup/schemas.log 2>&1 &
[1] 4890
[postgres@lxtrdpgdsgv01 ~]$
5. Backup dump only the data, not the schema
[postgres@lxtrdpgdsgv01 ~]$ nohup pg_dumpall -U postgres --data-only -v -f /pgBackup/pgsql15/backup/dataonly.sql > /pgBackup/pgsql15/backup/dataonly.log 2>&1 &
[1] 5233
[postgres@lxtrdpgdsgv01 ~]$
6. Backup dump data as INSERT commands, rather than COPY
[postgres@lxtrdpgdsgv01 ~]$ nohup pg_dumpall -U postgres --inserts -v -f /pgBackup/pgsql15/backup/inserts.sql > /pgBackup/pgsql15/backup/inserts.log 2>&1 &
[1] 5274
[postgres@lxtrdpgdsgv01 ~]$
7. Backup dump only global objects, no databases
[postgres@lxtrdpgdsgv01 ~]$ nohup pg_dumpall -U postgres --globals-only -v -f /pgBackup/pgsql15/backup/globals.sql > /pgBackup/pgsql15/backup/globals.log 2>&1 &
[1] 5331
[postgres@lxtrdpgdsgv01 ~]$
Restore
A. Drop Existing Databases (Optional)
[postgres@lxtrdpgdsgv01 ~]$ psql
psql (15.14)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
-----------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
edpua | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
gebua | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
orcl | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
| | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | postgres=CTc/postgres+
| | | | | | | =c/postgres
(6 rows)
postgres=# drop database edpua;
DROP DATABASE
postgres=# drop database gebua;
DROP DATABASE
postgres=# drop database orcl;
DROP DATABASE
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
-----------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
| | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | postgres=CTc/postgres+
| | | | | | | =c/postgres
(3 rows)
postgres=#
postgres=# \db
List of tablespaces
Name | Owner | Location
------------+----------+---------------------
edpua_ix | postgres | /pgIx/pgsql15/edpua
edpua_tb | postgres | /pgTb/pgsql15/edpua
gebua_ix | postgres | /pgIx/pgsql15/gebua
gebua_tb | postgres | /pgTb/pgsql15/gebua
orcl_ix | postgres | /pgIx/pgsql15/orcl
orcl_tb | postgres | /pgTb/pgsql15/orcl
pg_default | postgres |
pg_global | postgres |
(8 rows)
postgres=#
[postgres@lxtrdpgdsgv01 ~]$ nohup psql -U postgres -X -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql -d postgres > /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025_restore.log [1] 4132 [postgres@lxtrdpgdsgv01 ~]$ -- OR -- nohup psql -U postgres -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql > /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025_restore.log 2>&1 & -- Log: backup_all_databases_10OCT2025_restore.log
[postgres@lxtrdpgdsgv01 ~]$ psql
psql (15.14)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
-----------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
edpua | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
gebua | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
orcl | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
| | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | postgres=CTc/postgres+
| | | | | | | =c/postgres
(6 rows)
postgres=#
9. Restore to Another Host (Different Tablespace Paths)
A. Copy Backup File
[postgres@lxtrdpgdsgv01 backup]$ scp backup_all_databases_10OCT2025.sql 192.168.2.31:/pgBackup/pgsql15/backup/ postgres@192.168.2.31's password: backup_all_databases_10OCT2025.sql 100% 3556MB 51.5MB/s 01:09 [postgres@lxtrdpgdsgv01 backup]$ [postgres@pg17 backup]$ ls -ltr backup_all_databases_10OCT2025.sql -rw-r--r--. 1 postgres postgres 3729147646 Oct 6 06:17 backup_all_databases_10OCT2025.sql [postgres@pg17 backup]$
B. Extract Tablespace/Database Info
[postgres@lxtrdpgdsgv01 backup]$ grep -i "CREATE TABLESPACE" /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql CREATE TABLESPACE orcl_tb OWNER postgres LOCATION '/pgTb/pgsql15/orcl'; CREATE TABLESPACE orcl_ix OWNER postgres LOCATION '/pgIx/pgsql15/orcl'; CREATE TABLESPACE gebua_tb OWNER postgres LOCATION '/pgTb/pgsql15/gebua'; CREATE TABLESPACE gebua_ix OWNER postgres LOCATION '/pgIx/pgsql15/gebua'; CREATE TABLESPACE edpua_tb OWNER postgres LOCATION '/pgTb/pgsql15/edpua'; CREATE TABLESPACE edpua_ix OWNER postgres LOCATION '/pgIx/pgsql15/edpua'; [postgres@lxtrdpgdsgv01 backup]$ [postgres@lxtrdpgdsgv01 backup]$ grep -i "CREATE DATABASE" /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql CREATE DATABASE edpua WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.UTF-8' TABLESPACE = edpua_tb; CREATE DATABASE gebua WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.UTF-8' TABLESPACE = gebua_tb; CREATE DATABASE orcl WITH TEMPLATE = template0 ENCODING = 'UTF8' LOCALE_PROVIDER = libc LOCALE = 'en_US.UTF-8' TABLESPACE = orcl_tb; [postgres@lxtrdpgdsgv01 backup]$
C. Create Tablespace Directories on Target
[postgres@pg17 ~]$ psql psql (15.13) Type "help" for help. postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges -----------+----------+----------+-------------+-------------+------------+-----------------+----------------------- postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres + | | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | postgres=CTc/postgres+ | | | | | | | =c/postgres (3 rows) postgres=# postgres=# \db List of tablespaces Name | Owner | Location ------------+----------+---------- pg_default | postgres | pg_global | postgres | (2 rows) postgres=# # Create Directory [postgres@pg17 ~]$ mkdir -p /pgData/pgsql15/orcl_tb [postgres@pg17 ~]$ mkdir -p /pgData/pgsql15/orcl_ix [postgres@pg17 ~]$ mkdir -p /pgData/pgsql15/gebua_tb [postgres@pg17 ~]$ mkdir -p /pgData/pgsql15/gebua_ix [postgres@pg17 ~]$ mkdir -p /pgData/pgsql15/edpua_tb [postgres@pg17 ~]$ mkdir -p /pgData/pgsql15/edpua_ix
D. Create Tablespaces in PostgreSQL
postgres=# CREATE TABLESPACE orcl_tb OWNER postgres LOCATION '/pgData/pgsql15/orcl_tb'; CREATE TABLESPACE postgres=# CREATE TABLESPACE orcl_ix OWNER postgres LOCATION '/pgData/pgsql15/orcl_ix'; CREATE TABLESPACE postgres=# CREATE TABLESPACE gebua_tb OWNER postgres LOCATION '/pgData/pgsql15/gebua_tb'; CREATE TABLESPACE postgres=# CREATE TABLESPACE gebua_ix OWNER postgres LOCATION '/pgData/pgsql15/gebua_ix'; CREATE TABLESPACE postgres=# CREATE TABLESPACE edpua_tb OWNER postgres LOCATION '/pgData/pgsql15/edpua_tb'; CREATE TABLESPACE postgres=# CREATE TABLESPACE edpua_ix OWNER postgres LOCATION '/pgData/pgsql15/edpua_ix'; CREATE TABLESPACE postgres=# -- OR --- You can modify the tablespace paths directly in the backup file; however, this approach is not recommended for large backup files due to reliability concerns. cd /pgBackup/pgsql15/backup/ # Replace ORCL paths sed -i 's|/pgTb/pgsql15/orcl|/pgData/pgsql15/orcl_tb|g' backup_all_databases_10OCT2025.sql sed -i 's|/pgIx/pgsql15/orcl|/pgData/pgsql15/orcl_ix|g' backup_all_databases_10OCT2025.sql # Replace GEBUA paths sed -i 's|/pgTb/pgsql15/gebua|/pgData/pgsql15/gebua_tb|g' backup_all_databases_10OCT2025.sql sed -i 's|/pgIx/pgsql15/gebua|/pgData/pgsql15/gebua_ix|g' backup_all_databases_10OCT2025.sql # Replace EDPUA paths sed -i 's|/pgTb/pgsql15/edpua|/pgData/pgsql15/edpua_tb|g' backup_all_databases_10OCT2025.sql sed -i 's|/pgIx/pgsql15/edpua|/pgData/pgsql15/edpua_ix|g' backup_all_databases_10OCT2025.sql [postgres@pg17 ~]$ psql psql (15.13) Type "help" for help. postgres=# \db List of tablespaces Name | Owner | Location ------------+----------+-------------------------- edpua_ix | postgres | /pgData/pgsql15/edpua_ix edpua_tb | postgres | /pgData/pgsql15/edpua_tb gebua_ix | postgres | /pgData/pgsql15/gebua_ix gebua_tb | postgres | /pgData/pgsql15/gebua_tb orcl_ix | postgres | /pgData/pgsql15/orcl_ix orcl_tb | postgres | /pgData/pgsql15/orcl_tb pg_default | postgres | pg_global | postgres | (8 rows) postgres=#
E. Optional: Backup on Target Host
[postgres@pg17 ~]$ nohup pg_dumpall -U postgres -v -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025_pg17.sql > /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025_pg17.log 2>&1 &
[1] 4246
[postgres@pg17 ~]$
[postgres@pg17 ~]$ nohup psql -U postgres -X -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql -d postgres > /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025_restore_pg17.log 2>&1 &
[1] 5213
[postgres@pg17 ~]$
-- OR --
nohup psql -U postgres -f /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025.sql > /pgBackup/pgsql15/backup/backup_all_databases_10OCT2025_restore_pg17.log 2>&1 &
-- Log: backup_all_databases_10OCT2025_restore_pg17.log[postgres@pg17 ~]$ psql psql (15.13) Type "help" for help. postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges -----------+----------+----------+-------------+-------------+------------+-----------------+----------------------- edpua | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | gebua | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | orcl | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres + | | | | | | | postgres=CTc/postgres template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | postgres=CTc/postgres+ | | | | | | | =c/postgres (6 rows) postgres=# [postgres@pg17 ~]$ psql psql (15.13) Type "help" for help. postgres=# \c orcl You are now connected to database "orcl" as user "postgres". orcl=# SELECT orcl-# schemaname || '.' || relname AS table_name, orcl-# pg_size_pretty(pg_total_relation_size(relid)) AS total_size, orcl-# pg_size_pretty(pg_relation_size(relid)) AS table_size, orcl-# pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS index_size orcl-# FROM orcl-# pg_catalog.pg_statio_user_tables orcl-# ORDER BY orcl-# pg_total_relation_size(relid) DESC; table_name | total_size | table_size | index_size ------------------+------------+------------+------------ trd.metrics_high | 799 MB | 498 MB | 301 MB trd.metrics_mid | 638 MB | 398 MB | 240 MB trd.employees | 493 MB | 322 MB | 172 MB trd.sales_2023 | 399 MB | 249 MB | 150 MB trd.sales_2024 | 399 MB | 249 MB | 150 MB trd.sales_2022 | 399 MB | 249 MB | 150 MB trd.sales_2021 | 398 MB | 248 MB | 150 MB trd.metrics_low | 159 MB | 99 MB | 60 MB trd.test_data | 71 MB | 50 MB | 21 MB trd.emp_summary | 24 kB | 8192 bytes | 16 kB trd.metrics_rest | 8192 bytes | 0 bytes | 8192 bytes (11 rows) orcl=#
Caution: Your use of any information or materials on this website is entirely at your own risk. It is provided for educational purposes only. It has been tested internally, however, we do not guarantee that it will work for you. Ensure that you run it in your test environment before using.
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/
Table of Contents
Step 1: Create a User
Step 2: Change User Password
Step 3: Grant Database Access
Step 4: Expire Password
Step 5: Set Password Never Expire
Step 6: Lock Account
Step 7: Unlock Account
Step 8: Create Schema
Step 9: Create Roles & Users
Step 10: Assign Ownership
Step 11: Grant Schema Privileges
Step 12: Grant RW Privileges
Step 13: Grant RO Privileges
Step 14: Assign Roles to Users
Step 15: Testing
Step 16: Set Schema Search Path
Step 17: Groups
Step 18: PostgreSQL — Table-View Privileges
Note: Users are just roles with LOGIN privilege; roles without LOGIN cannot connect.
-- Option 1: Using CREATE USER CREATE USER john WITH PASSWORD 'mypassword'; -- Option 2: Using CREATE ROLE with LOGIN CREATE ROLE john LOGIN PASSWORD 'mypassword'; postgres=# CREATE USER john WITH PASSWORD 'mypassword'; CREATE ROLE postgres=# CREATE ROLE john LOGIN PASSWORD 'mypassword'; ERROR: role "john" already exists
Passwords can be changed by either an admin or the user. By Admin: postgres=# ALTER USER john WITH PASSWORD 'newpassword'; ALTER ROLE postgres=# -- OR -- postgres=# \password john Enter new password for user "john": Enter it again: postgres=# By User (self-service): From the psql prompt: [postgres@pg17 ~]$ psql -h 192.168.2.31 -U john -d mydb -W Password: mydb=> \conninfo You are connected to database "mydb" as user "john" on host "192.168.2.31" at port "5432". mydb=> \password Enter new password for user "john": Enter it again: mydb=>
To allow a user to connect to a database: postgres=# GRANT CONNECT ON DATABASE mydb TO john; GRANT postgres=# Verify user login: -- Connect as user [postgres@pg17 ~]$ psql -h 192.168.2.31 -U john -d mydb -W Password: -- Check current user mydb=> SELECT CURRENT_USER; current_user -------------- john (1 row) mydb=> mydb=> select session_user; session_user -------------- john (1 row) -- Connection info mydb=> \conninfo You are connected to database "mydb" as user "john" on host "192.168.2.31" at port "5432". mydb=>
postgres=# ALTER USER john VALID UNTIL '2025-09-11'; ALTER ROLE postgres=# postgres=# \du+ List of roles Role name | Attributes | Member of | Description -----------+------------------------------------------------------------+-----------+------------- john | Password valid until 2025-09-11 00:00:00-04 | {} | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | postgres=#
Step 5: Set Password to Never Expire
postgres=# \du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- john | Password valid until 2025-09-11 00:00:00-04 | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} -- Without changing existing password postgres=# ALTER USER john VALID UNTIL 'infinity'; ALTER ROLE postgres=# -- With changing existing password postgres=# ALTER USER john WITH PASSWORD 'newpassword' VALID UNTIL 'infinity'; ALTER ROLE postgres=# postgres=# \du List of roles Role name | Attributes | Member of -----------+------------------------------------------------------------+----------- john | Password valid until infinity | {} postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} postgres=# -- Set to future date postgres=# ALTER USER john VALID UNTIL '2025-12-31'; ALTER ROLE postgres=# \du+ List of roles Role name | Attributes | Member of | Description -----------+------------------------------------------------------------+-----------+------------- john | Password valid until 2025-12-31 00:00:00-05 | {} | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | postgres=#
postgres=# ALTER USER john NOLOGIN; ALTER ROLE postgres=# \du+ List of roles Role name | Attributes | Member of | Description -----------+------------------------------------------------------------+-----------+------------- john | Cannot login +| {} | | Password valid until infinity | | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | postgres=#
postgres=# ALTER USER john LOGIN; ALTER ROLE postgres=# \du+ List of roles Role name | Attributes | Member of | Description -----------+------------------------------------------------------------+-----------+------------- john | Password valid until infinity | {} | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | postgres=#
[postgres@pg17 ~]$ psql psql (15.13) Type "help" for help. postgres=# \c mydb You are now connected to database "mydb" as user "postgres". mydb=# mydb=# CREATE SCHEMA BLP; CREATE SCHEMA mydb=# \dn List of schemas Name | Owner --------+------------------- blp | postgres public | pg_database_owner (2 rows) mydb=#
postgres=# CREATE USER "BLP" WITH PASSWORD 'blp'; CREATE ROLE postgres=# postgres=# CREATE ROLE blp_rw NOLOGIN; CREATE ROLE postgres=# CREATE ROLE blp_ro NOLOGIN; CREATE ROLE postgres=# postgres=# CREATE USER alice WITH PASSWORD 'alice123'; CREATE ROLE postgres=# CREATE USER bob WITH PASSWORD 'bob123'; CREATE ROLE postgres=# CREATE USER charlie WITH PASSWORD 'charlie123'; CREATE ROLE postgres=# postgres=# \du+ List of roles Role name | Attributes | Member of | Description -----------+------------------------------------------------------------+-----------+------------- BLP | | {} | alice | | {} | blp_ro | Cannot login | {} | blp_rw | Cannot login | {} | bob | | {} | charlie | | {} | john | Password valid until 2025-12-31 00:00:00-05 | {} | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | trduser | | {} | postgres=#
Step 10: Assign Ownership on Schema
-- Please do NOT grant this privillege, Owner can drop the schema, change privileges, and has full control over all objects inside. postgres=# \c mydb You are now connected to database "mydb" as user "postgres". mydb=# mydb=# \dn List of schemas Name | Owner --------+------------------- blp | postgres public | pg_database_owner (2 rows) mydb=# ALTER SCHEMA BLP OWNER TO "BLP"; ALTER SCHEMA mydb=# mydb=# \dn+ List of schemas Name | Owner | Access privileges | Description --------+-------------------+----------------------------------------+------------------------ blp | BLP | | public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema | | =U/pg_database_owner | (2 rows) mydb=#
Step 11: Grant Schema Privileges to Owner
-- Full control on schema: usage + create GRANT USAGE, CREATE ON SCHEMA blp TO "BLP"; mydb=# \dn+ List of schemas Name | Owner | Access privileges | Description --------+-------------------+----------------------------------------+------------------------ blp | BLP | | public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema | | =U/pg_database_owner | (2 rows) mydb=# GRANT USAGE, CREATE ON SCHEMA blp TO "BLP"; GRANT mydb=# \dn+ List of schemas Name | Owner | Access privileges | Description --------+-------------------+----------------------------------------+------------------------ blp | BLP | BLP=UC/BLP | public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema | | =U/pg_database_owner | (2 rows) mydb=#
USAGE → allows the role to see the schema and its objects. -- Grant schema access without CREATE GRANT USAGE ON SCHEMA BLP TO blp_rw; -- Grant DML on all existing tables GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA BLP TO blp_rw; -- Future tables ALTER DEFAULT PRIVILEGES IN SCHEMA BLP GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO blp_rw; mydb=# GRANT USAGE ON SCHEMA BLP TO blp_rw; GRANT mydb=# GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA BLP TO blp_rw; GRANT mydb=# ALTER DEFAULT PRIVILEGES IN SCHEMA BLP GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO blp_rw; ALTER DEFAULT PRIVILEGES mydb=# mydb=# \dn+ List of schemas Name | Owner | Access privileges | Description --------+-------------------+----------------------------------------+------------------------ blp | BLP | BLP=UC/BLP +| | | blp_rw=U/BLP | public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema | | =U/pg_database_owner | (2 rows) mydb=#
-- Grant schema access without CREATE GRANT USAGE ON SCHEMA BLP TO BLP_RO; -- Grant SELECT on all existing tables GRANT SELECT ON ALL TABLES IN SCHEMA BLP TO BLP_RO; -- Future tables ALTER DEFAULT PRIVILEGES IN SCHEMA BLP GRANT SELECT ON TABLES TO BLP_RO; mydb=# GRANT USAGE ON SCHEMA BLP TO BLP_RO; GRANT mydb=# GRANT SELECT ON ALL TABLES IN SCHEMA BLP TO BLP_RO; GRANT mydb=# ALTER DEFAULT PRIVILEGES IN SCHEMA BLP GRANT SELECT ON TABLES TO BLP_RO; ALTER DEFAULT PRIVILEGES mydb=# mydb=# \dn+ List of schemas Name | Owner | Access privileges | Description --------+-------------------+----------------------------------------+------------------------ blp | BLP | BLP=UC/BLP +| | | blp_rw=U/BLP +| | | blp_ro=U/BLP | public | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema | | =U/pg_database_owner | (2 rows) mydb=#
Step 14: Assign Roles to Users
mydb=# GRANT BLP_RW TO ALICE; GRANT ROLE mydb=# mydb=# GRANT BLP_RO TO BOB,CHARLIE; GRANT ROLE mydb=# mydb=# \du+ List of roles Role name | Attributes | Member of | Description -----------+------------------------------------------------------------+-----------+------------- BLP | | {} | alice | | {blp_rw} | blp_ro | Cannot login | {} | blp_rw | Cannot login | {} | bob | | {blp_ro} | charlie | | {blp_ro} | john | Password valid until 2025-12-31 00:00:00-05 | {} | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} | trduser | | {} | mydb=#
-- Login to BLP user on mydb database and create table on blp schema. [postgres@pg17 ~]$ psql -h 192.168.2.31 -U BLP -d mydb -W Password: psql (15.13) Type "help" for help. mydb=> \conninfo You are connected to database "mydb" as user "BLP" on host "192.168.2.31" at port "5432". mydb=> CREATE TABLE blp.employees ( mydb(> emp_id SERIAL PRIMARY KEY, mydb(> first_name VARCHAR(50), mydb(> last_name VARCHAR(50), mydb(> hire_date DATE, mydb(> salary NUMERIC(10,2) mydb(> ); CREATE TABLE mydb=> mydb=> INSERT INTO blp.employees (first_name, last_name, hire_date, salary) VALUES mydb-> ('John', 'Doe', '2023-01-15', 5000.00), mydb-> ('Jane', 'Smith', '2022-11-20', 6000.00), mydb-> ('Alice', 'Johnson', '2024-03-01', 5500.00); INSERT 0 3 mydb=> mydb=> select * from blp.employees; emp_id | first_name | last_name | hire_date | salary --------+------------+-----------+------------+--------- 1 | John | Doe | 2023-01-15 | 5000.00 2 | Jane | Smith | 2022-11-20 | 6000.00 3 | Alice | Johnson | 2024-03-01 | 5500.00 (3 rows) mydb=> mydb=> drop table blp.employees; DROP TABLE mydb=> -- Login to alice user on mydb database and update table on blp schema. [root@pg17 ~]# psql -h 192.168.2.31 -U alice -d mydb -W Password: psql (15.13) Type "help" for help. mydb=> \conninfo You are connected to database "mydb" as user "alice" on host "192.168.2.31" at port "5432". mydb=> mydb=> \du+ alice List of roles Role name | Attributes | Member of | Description -----------+------------+-----------+------------- alice | | {blp_rw} | mydb=> mydb=> \dt+ blp.* List of relations Schema | Name | Type | Owner | Persistence | Access method | Size | Description --------+-----------+-------+-------+-------------+---------------+------------+------------- blp | employees | table | BLP | permanent | heap | 8192 bytes | (1 row) mydb=> select * from blp.employees; emp_id | first_name | last_name | hire_date | salary --------+------------+-----------+------------+--------- 1 | John | Doe | 2023-01-15 | 5000.00 2 | Jane | Smith | 2022-11-20 | 6000.00 3 | Alice | Johnson | 2024-03-01 | 5500.00 (3 rows) mydb=> UPDATE blp.employees SET salary = CASE WHEN first_name = 'John' THEN 7000.00 WHEN first_name = 'Alice' THEN 6500.00 END WHERE first_name IN ('John', 'Alice'); UPDATE 2 mydb=> select * from blp.employees; emp_id | first_name | last_name | hire_date | salary --------+------------+-----------+------------+--------- 2 | Jane | Smith | 2022-11-20 | 6000.00 1 | John | Doe | 2023-01-15 | 7000.00 3 | Alice | Johnson | 2024-03-01 | 6500.00 (3 rows) mydb=> -- Note, we have granted only DML privilleges, hence create and alter table command failing mydb=> CREATE TABLE blp.departments ( mydb(> dept_id SERIAL PRIMARY KEY, mydb(> dept_name VARCHAR(100) NOT NULL, mydb(> location VARCHAR(100) mydb(> ); ERROR: permission denied for schema blp LINE 1: CREATE TABLE blp.departments ( ^ mydb=> mydb=> ALTER TABLE blp.employees mydb-> ADD COLUMN department VARCHAR(50); ERROR: must be owner of table employees mydb=> -- Login to bob user on mydb database and select table on blp schema. [postgres@pg17 ~]$ psql -h 192.168.2.31 -U bob -d mydb -W Password: psql (15.13) Type "help" for help. mydb=> \conninfo You are connected to database "mydb" as user "bob" on host "192.168.2.31" at port "5432". mydb=> mydb=> \du+ bob List of roles Role name | Attributes | Member of | Description -----------+------------+-----------+------------- bob | | {blp_ro} | mydb-> \dt+ blp.* List of relations Schema | Name | Type | Owner | Persistence | Access method | Size | Description --------+-----------+-------+-------+-------------+---------------+------------+------------- blp | employees | table | BLP | permanent | heap | 8192 bytes | (1 row) mydb=> select * from blp.employees; emp_id | first_name | last_name | hire_date | salary --------+------------+-----------+------------+--------- 2 | Jane | Smith | 2022-11-20 | 6000.00 1 | John | Doe | 2023-01-15 | 7000.00 3 | Alice | Johnson | 2024-03-01 | 6500.00 (3 rows) mydb=> mydb=> INSERT INTO blp.employees (first_name, last_name, hire_date, salary) VALUES mydb-> ('Bob', 'Williams', '2024-04-01', 5800.00), mydb-> ('Clara', 'Brown', '2024-05-10', 6200.00), mydb-> ('David', 'Lee', '2024-06-15', 5300.00); ERROR: permission denied for table employees mydb=>
Step 16: Set Schema Search Path
[postgres@pg17 ~]$ psql -h 192.168.2.31 -U BLP -d mydb -W Password: psql (15.13) Type "help" for help. mydb=> mydb=> \conninfo You are connected to database "mydb" as user "BLP" on host "192.168.2.31" at port "5432". mydb=> mydb=> \dt+ blp.* List of relations Schema | Name | Type | Owner | Persistence | Access method | Size | Description --------+-----------+-------+-------+-------------+---------------+------------+------------- blp | employees | table | BLP | permanent | heap | 8192 bytes | (1 row) mydb=> mydb=> select * from employees; ERROR: relation "employees" does not exist LINE 1: select * from employees; mydb=> mydb=> SET search_path to BLP; -- Temporarily for this session SET mydb=> select * from employees; emp_id | first_name | last_name | hire_date | salary --------+------------+-----------+------------+--------- 2 | Jane | Smith | 2022-11-20 | 6000.00 1 | John | Doe | 2023-01-15 | 7000.00 3 | Alice | Johnson | 2024-03-01 | 6500.00 (3 rows) mydb=> -- Make the Schema Default for the user BLP -- Permenant ALTER ROLE blp_owner SET search_path = BLP;
Step 17: Groups
postgres=# CREATE GROUP app_users; postgres=# ALTER GROUP app_users ADD USER alice; postgres=# ALTER GROUP app_users ADD USER bob; postgres=# ALTER GROUP app_users ADD USER charlie; postgres=# ALTER GROUP app_users DROP USER charlie; postgres=# ALTER GROUP app_users RENAME TO appusers; postgres=# DROP GROUP appusers; mydb=> select * from pg_group; groname | grosysid | grolist ---------------------------+----------+--------------- pg_database_owner | 6171 | {} pg_read_all_data | 6181 | {} pg_write_all_data | 6182 | {} pg_monitor | 3373 | {} pg_read_all_settings | 3374 | {3373} pg_read_all_stats | 3375 | {3373} pg_stat_scan_tables | 3377 | {3373} pg_read_server_files | 4569 | {} pg_write_server_files | 4570 | {} pg_execute_server_program | 4571 | {} pg_signal_backend | 4200 | {} pg_checkpoint | 4544 | {} blp_rw | 84444 | {84447} blp_ro | 84445 | {84448,84449} app_users | 84468 | {} (15 rows) mydb=>
Step 18: PostgreSQL Object-Level Privilege Summary
mydb=> \z blp.employees
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+-----------+-------+-------------------+-------------------+----------
blp | employees | table | BLP=arwdDxt/BLP +| |
| | | blp_ro=r/BLP +| |
| | | blp_rw=arwd/BLP | |
(1 row)Example:
| Short | Full Privilege | Example GRANT |
|---|---|---|
| r | SELECT | GRANT SELECT ON employees TO hr_user; |
| a | INSERT | GRANT INSERT ON employees TO hr_user; |
| w | UPDATE | GRANT UPDATE ON employees TO hr_user; |
| d | DELETE | GRANT DELETE ON employees TO hr_user; |
| D | TRUNCATE | GRANT TRUNCATE ON employees TO hr_user; |
| x | REFERENCES | GRANT REFERENCES ON employees TO hr_user; |
| t | TRIGGER | GRANT TRIGGER ON employees TO hr_user; |
| R | RULE | GRANT RULE ON employees TO hr_user; (rarely used) |
Tip: the shorthand string you see in \z (for example arwdDxt) can be expanded by mapping each letter to the rows above, then converting them into one or more GRANT statements.
Caution: Your use of any information or materials on this website is entirely at your own risk. It is provided for educational purposes only. It has been tested internally, however, we do not guarantee that it will work for you. Ensure that you run it in your test environment before using.
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
WhatsApp :
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/
Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/