Tag Archives: database administration

Free Space Map (FSM) in PostgreSQL

Free Space Map (FSM) in PostgreSQL

Table of Contents



A. What is the Free Space Map (FSM)?

When you run an INSERT or UPDATE, how does PostgreSQL know which page on disk has enough empty space to fit the new row?

Scanning through gigabytes of table pages from disk to find empty spots would be terribly slow. Instead, PostgreSQL maintains a lightweight binary tree called the Free Space Map (FSM) for every table and index.

B. How PostgreSQL Stores Data (Pages & Blocks)

PostgreSQL splits table files on disk into chunks called Pages (or Blocks). By default, each page is 8 KB (8192 bytes).

  • Each page can hold multiple rows (tuples).
  • Each row has an address called a ctid written as (page_number, item_number). For example, (0, 1) means Page 0, 1st row.
  • The FSM records roughly how many bytes of free space remain on each 8 KB page.

1. Prerequisites & Extension Setup

To inspect the Free Space Map directly inside PostgreSQL, enable the official pg_freespacemap extension.

-- Enable extension
postgres=# CREATE EXTENSION IF NOT EXISTS pg_freespacemap;
CREATE EXTENSION
postgres=#

2. Create Demo Table & Insert Data

We will create a table and insert 10,000 rows. We explicitly set the storage of description to PLAIN to disable TOAST compression so each row consumes ~200 bytes directly on the 8 KB page.

-- 1. Create a simple table
postgres=# DROP TABLE IF EXISTS demo_table;
DROP TABLE
postgres=# CREATE TABLE demo_table (
    id SERIAL PRIMARY KEY,
    name TEXT,
    description TEXT
);
CREATE TABLE
postgres=#


-- 2. Disable compression to maintain exact byte sizes
postgres=# ALTER TABLE demo_table ALTER COLUMN description SET STORAGE PLAIN;
ALTER TABLE
postgres=#

-- 3. Insert 10,000 rows (~200 bytes per row)
postgres=# INSERT INTO demo_table (name, description)
SELECT
    'Name_' || generate_series,
    repeat('x', 200)
FROM generate_series(1, 10000);
INSERT 0 10000
postgres=#


-- 4. Check total disk size
postgres=# SELECT pg_size_pretty(pg_relation_size('demo_table')) AS table_size;
 table_size
------------
 2504 kB
(1 row)

postgres=#

-- 5. Check total 8 KB pages (blocks)
postgres=# SELECT pg_relation_size('demo_table') / 8192 AS total_pages;
 total_pages
-------------
         313
(1 row)

postgres=#

3. Inspect the Physical Files (Main, FSM, VM)

PostgreSQL splits table data on disk into multiple companion files (called forks).

-- Find the physical file path and relation filenode
postgres=# SELECT pg_relation_filepath('demo_table');
 pg_relation_filepath
----------------------
 base/5/65851
(1 row)

postgres=#

If you check your PostgreSQL data directory in the terminal, you will see three forks for this relation:

# Run in terminal inside $PGDATA
[postgres@pgdb01 ~]$ ls -la /pgData/pgsql17/data/base/5/65851*
-rw-------. 1 postgres postgres 2564096 Sep 23 17:44 /pgData/pgsql17/data/base/5/65851
-rw-------. 1 postgres postgres   24576 Sep 23 17:44 /pgData/pgsql17/data/base/5/65851_fsm  <-----------
-rw-------. 1 postgres postgres    8192 Sep 23 17:44 /pgData/pgsql17/data/base/5/65851_vm
[postgres@pgdb01 ~]$

65851        # Main data file (heap blocks/pages)
65851_fsm    # Free Space Map (tracks free bytes per block)
65851_vm     # Visibility Map (tracks all-visible / frozen blocks)

4. Check FSM Before Any Modifications

Let’s inspect the FSM to see how much room is available on each block.

-- Check free space in the first 20 pages
postgres=# SELECT
    blkno,
    avail AS free_space_bytes
FROM pg_freespace('demo_table')
ORDER BY blkno
LIMIT 20;
 blkno | free_space_bytes
-------+------------------
     0 |               96
     1 |               96
     2 |               96
     3 |               96
     4 |               96
     5 |               96
     6 |               96
     7 |               96
     8 |               96
     9 |               96
    10 |               96
    11 |               96
    12 |               96
    13 |               96
    14 |               96
    15 |               96
    16 |               96
    17 |               96
    18 |               96
    19 |               96
(20 rows)

postgres=#


-- Overall summary of free space across the entire table
postgres=# SELECT
    count(*) AS total_pages,
    avg(avail)::int AS avg_free_space,
    min(avail) AS min_free_space,
    max(avail) AS max_free_space,
    sum(avail) AS total_free_space
FROM pg_freespace('demo_table');
 total_pages | avg_free_space | min_free_space | max_free_space | total_free_space
-------------+----------------+----------------+----------------+------------------
         313 |            111 |             96 |           4864 |            34816
(1 row)

postgres=#

Observation: Most pages show very little available space (96 bytes free). The pages are packed full.

5. Delete Rows to Create Free Space (The FSM Lag)

Now let’s delete 50% of the rows (all even IDs) and immediately check the FSM.

-- Delete 50% of rows
postgres=# DELETE FROM demo_table WHERE id % 2 = 0;
DELETE 5000
postgres=#

-- Verify remaining count (should be 5,000)
postgres=# SELECT count(*) FROM demo_table;
 count
-------
  5000
(1 row)

postgres=#

-- Check FSM IMMEDIATELY after DELETE (before VACUUM)
postgres=# SELECT
    blkno,
    avail AS free_space_bytes
FROM pg_freespace('demo_table')
ORDER BY blkno
LIMIT 20;
 blkno | free_space_bytes
-------+------------------
     0 |               96
     1 |               96
     2 |               96
     3 |               96
     4 |               96
     5 |               96
     6 |               96
     7 |               96
     8 |               96
     9 |               96
    10 |               96
    11 |               96
    12 |               96
    13 |               96
    14 |               96
    15 |               96
    16 |               96
    17 |               96
    18 |               96
    19 |               96
(20 rows)

postgres=#

⚠️ Crucial Observation: The FSM still shows the exact same low free space as before! Because of MVCC, DELETE only flags rows as dead. The space has not been reclaimed, and the FSM has not been updated.

6. Run VACUUM & Observe FSM Changes

Running standard VACUUM clears dead rows and registers all newly available space into the FSM.

-- Run VACUUM to reclaim dead space and update FSM
postgres=# VACUUM demo_table;
VACUUM
postgres=#

-- Check FSM per block after VACUUM
postgres=# SELECT
    blkno,
    avail AS free_space_bytes
FROM pg_freespace('demo_table')
ORDER BY blkno
LIMIT 20;
 blkno | free_space_bytes
-------+------------------
     0 |             3936
     1 |             4192
     2 |             3936
     3 |             4064
     4 |             4064
     5 |             4064
     6 |             4064
     7 |             4064
     8 |             4064
     9 |             4064
    10 |             4064
    11 |             4064
    12 |             4064
    13 |             4064
    14 |             4064
    15 |             4064
    16 |             4064
    17 |             4064
    18 |             4064
    19 |             4064
(20 rows)

postgres=#

-- Summary after VACUUM
postgres=# SELECT
    count(*) AS total_pages,
    avg(avail)::int AS avg_free_space,
    min(avail) AS min_free_space,
    max(avail) AS max_free_space,
    sum(avail) AS total_free_space
FROM pg_freespace('demo_table');
 total_pages | avg_free_space | min_free_space | max_free_space | total_free_space
-------------+----------------+----------------+----------------+------------------
         313 |           4072 |           3936 |           6624 |          1274464
(1 row)

postgres=#

Result: Each page now reports substantial free space (roughly 3,936 to 4,064 bytes available per page).

7. Insert New Rows & Watch FSM Guide Placement

Let’s insert 2,000 new rows. Watch how PostgreSQL uses the FSM to place them inside existing pages instead of growing the file.

-- 1. Record current page count
postgres=# SELECT pg_relation_size('demo_table') / 8192 AS pages_before_insert;
 pages_before_insert
---------------------
                 313
(1 row)

postgres=#

-- 2. Insert 2,000 new rows
postgres=# INSERT INTO demo_table (name, description)
SELECT
    'NewName_' || generate_series,
    repeat('y', 200)
FROM generate_series(1, 2000);
INSERT 0 2000
postgres=#

-- 3. Check page count again
postgres=# SELECT pg_relation_size('demo_table') / 8192 AS pages_after_insert;
 pages_after_insert
--------------------
                313
(1 row)

postgres=#

What happened?

  • pages_before_insert and pages_after_insert are identical!
  • The table file did not expand at all.
  • The FSM directed PostgreSQL to pages with available free space, allowing the new rows to be inserted into the existing space.
-- Check free space reduction after inserts
postgres=# SELECT
    count(*) AS total_pages,
    avg(avail)::int AS avg_free_space,
    sum(avail) AS total_free_space
FROM pg_freespace('demo_table');
 total_pages | avg_free_space | total_free_space
-------------+----------------+------------------
         313 |           2489 |           778976
(1 row)

postgres=#

8. Compare VACUUM vs VACUUM FULL on FSM

Let’s see how standard VACUUM differs from VACUUM FULL regarding physical disk size and the FSM.

-- 1. Delete more rows
postgres=# DELETE FROM demo_table WHERE id % 3 = 0;
DELETE 2334
postgres=#

-- 2. Run standard VACUUM
postgres=# VACUUM demo_table;
VACUUM
postgres=#

-- Table size remains the same (space is kept for Postgres reuse, not given back to OS)
postgres=# SELECT pg_size_pretty(pg_relation_size('demo_table')) AS size_after_vacuum;
 size_after_vacuum
-------------------
 2504 kB
(1 row)

postgres=#
postgres=# SELECT sum(avail) AS total_free_after_vacuum FROM pg_freespace('demo_table');
 total_free_after_vacuum
-------------------------
                 1351904
(1 row)

postgres=#

-- 3. Run VACUUM FULL (Rewrites and compacts entire table)
postgres=# VACUUM FULL demo_table;
VACUUM
postgres=#

-- Physical table size shrinks on disk
postgres=# SELECT pg_size_pretty(pg_relation_size('demo_table')) AS size_after_vacuum_full;
 size_after_vacuum_full
------------------------
 1168 kB
(1 row)

postgres=#

-- The FSM free space is reset because the table has been reduced to fewer pages. As a result, PostgreSQL has fewer pages to track for free space.
Note: VACUUM FULL rewrites the entire table into a new file on disk.

postgres=# SELECT coalesce(sum(avail), 0) AS total_free_after_vacuum_full FROM pg_freespace('demo_table');
 total_free_after_vacuum_full
------------------------------
                            0
(1 row)

postgres=#
postgres=# SELECT pg_relation_filepath('demo_table');
 pg_relation_filepath
----------------------
 base/5/65859
(1 row)

postgres=#
[postgres@pgdb01 ~]$ ls -la /pgData/pgsql17/data/base/5/65859*
-rw-------. 1 postgres postgres 1196032 Sep 23 18:18 /pgData/pgsql17/data/base/5/65859
[postgres@pgdb01 ~]$

Warning: VACUUM FULL rewrites the entire table into a new file on disk. It places an EXCLUSIVE LOCK on the table, blocking all reads and writes until it finishes. Use with caution in production!

9. Quick Cheatsheet

ConceptDescription
What is FSM?A binary tree tracking free space across all 8 KB pages in a table.
PurposeSpeeds up INSERT/UPDATE by finding pages with room instantly.
Who updates it?VACUUM / autovacuum updates the FSM after cleaning dead rows.
Inspection ToolSELECT * FROM pg_freespace('table_name');
Disk FileStored alongside the main table file with the _fsm suffix.
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

VACUUM in PostgreSQL

VACUUM in PostgreSQL

Table of Contents



1. Why Do We Need VACUUM? (The Problem)

Because of  MVCC, PostgreSQL does not directly overwrite a row when you run an UPDATE, and it does not immediately remove the old row data when you run a DELETE.

  • An UPDATE creates a brand-new row and marks the old one as “dead”.
  • A DELETE simply marks the existing row as “dead”.

Over time, these “dead rows” (also called dead tuples) accumulate. This is called table bloat. It wastes disk space and slows down your queries.

VACUUM is PostgreSQL’s garbage collector. It cleans up dead rows so your database stays fast and lean.

2. Setting Up a Test Table

Let’s create a playground table and insert 1,00,00,000 rows to see VACUUM in action.

-- Create a sample items table
postgres=# DROP TABLE IF EXISTS items;
DROP TABLE
postgres=# CREATE TABLE items (
    id SERIAL PRIMARY KEY,
    name TEXT,
    price INT
);
CREATE TABLE
postgres=#

-- Disable autovacuum for one test table, just for DEMO

postgres=# ALTER TABLE items SET (autovacuum_enabled = false);
ALTER TABLE
postgres=#

postgres=# SELECT relname, reloptions FROM pg_class WHERE relname = 'items';
 relname |         reloptions
---------+----------------------------
 items   | {autovacuum_enabled=false}
(1 row)

postgres=#

-- Insert 1,00,00,000 rows

postgres=# INSERT INTO items (name, price)
SELECT
    'Item ' || generate_series,
    (random() * 100)::INT
FROM generate_series(1, 10000000);
INSERT 0 10000000
postgres=#

-- Table physical path
postgres=# SELECT pg_relation_filepath('public.items');
 pg_relation_filepath
----------------------
 base/5/65690
(1 row)

postgres=#

[postgres@pgdb01 5]$ ls -lrth /pgData/pgsql17/data/base/5/65690*
-rw-------. 1 postgres postgres 144K Sep 22 19:09 /pgData/pgsql17/data/base/5/65690_fsm
-rw-------. 1 postgres postgres 498M Sep 22 19:13 /pgData/pgsql17/data/base/5/65690
[postgres@pgdb01 5]$

3. Creating Table Bloat (Generating Dead Rows)

Now let’s update all 1,00,00,000 rows. This will generate 1,00,00,000 dead rows alongside the 1,00,00,000 new rows.

-- Update every row
postgres=# UPDATE items SET price = price + 10;
UPDATE 10000000
postgres=#

4. Inspecting Dead Rows & Table Size

Let’s check how many dead rows exist and how large the table has become.

-- Check dead row count

postgres=# SELECT relname, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'items';
 relname | n_live_tup | n_dead_tup
---------+------------+------------
 items   |   10000147 |   10000000
(1 row)

postgres=# 

-- Check physical disk size of the table

postgres=# SELECT pg_size_pretty(pg_total_relation_size('items')) AS total_size;
 total_size
------------
 1424 MB
(1 row)

postgres=#

[postgres@pgdb01 5]$ ls -lrth /pgData/pgsql17/data/base/5/65690*
-rw-------. 1 postgres postgres 272K Sep 22 19:16 /pgData/pgsql17/data/base/5/65690_fsm
-rw-------. 1 postgres postgres 996M Sep 22 19:16 /pgData/pgsql17/data/base/5/65690
[postgres@pgdb01 5]$

5. Standard VACUUM (Reclaiming Space for Reuse)

A standard VACUUM marks the dead rows as free space so future INSERT or UPDATE operations can reuse it, but it won’t release free space to the OS

-- Run standard vacuum
postgres=# VACUUM VERBOSE items;
INFO:  vacuuming "postgres.public.items"
INFO:  finished vacuuming "postgres.public.items": index scans: 1
pages: 0 removed, 127389 remain, 127389 scanned (100.00% of total)
tuples: 10000000 removed, 10000000 remain, 0 are dead but not yet removable
removable cutoff: 2178, which was 0 XIDs old when operation ended
new relfrozenxid: 2176, which is 4 XIDs ahead of previous value
frozen: 48685 pages from table (38.22% of total) had 7643430 tuples frozen
index scan needed: 63695 pages from table (50.00% of total) had 10000000 dead item identifiers removed
index "items_pkey": pages: 54839 in total, 0 newly deleted, 0 currently deleted, 0 reusable
avg read rate: 64.788 MB/s, avg write rate: 69.883 MB/s
buffer usage: 146023 hits, 227350 misses, 245229 dirtied
WAL usage: 455518 records, 245755 full page images, 1191493291 bytes
system usage: CPU: user: 10.29 s, system: 2.81 s, elapsed: 27.41 s
INFO:  vacuuming "postgres.pg_toast.pg_toast_65690"
INFO:  finished vacuuming "postgres.pg_toast.pg_toast_65690": index scans: 0
pages: 0 removed, 0 remain, 0 scanned (100.00% of total)
tuples: 0 removed, 0 remain, 0 are dead but not yet removable
removable cutoff: 2178, which was 0 XIDs old when operation ended
new relfrozenxid: 2178, which is 6 XIDs ahead of previous value
frozen: 0 pages from table (100.00% of total) had 0 tuples frozen
index scan not needed: 0 pages from table (100.00% of total) had 0 dead item identifiers removed
avg read rate: 1.178 MB/s, avg write rate: 1.178 MB/s
buffer usage: 9 hits, 1 misses, 1 dirtied
WAL usage: 1 records, 1 full page images, 7245 bytes
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
VACUUM
postgres=#


-- Check dead rows again

postgres=# SELECT relname, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'items';
 relname | n_live_tup | n_dead_tup
---------+------------+------------
 items   |   10000000 |          0  <---- all dead rows deleted.
(1 row)

postgres=# SELECT pg_size_pretty(pg_total_relation_size('items')) AS total_size;
 total_size
------------
 1424 MB      <--------- free space won't release to the OS, We can only reuse the unused space.
(1 row)

postgres=#

Important Note on Standard VACUUM:

  • n_dead_tup dropped to 0 (the garbage was cleaned).
  • However, if you check pg_total_relation_size('items'), it will still show 1424 MB.
  • Standard VACUUM does not shrink the file size back to the operating system. Instead, it keeps the empty slots ready inside PostgreSQL so new inserts won’t require growing the file again.
  • Non-blocking: Standard VACUUM allows normal SELECT, INSERT, UPDATE, and DELETE queries to continue running while it works.

6. VACUUM FULL (Shrinking Disk Space to the OS)

If you deleted millions of rows and you must return the unused disk space back to your operating system, you need VACUUM FULL.

-- Check table size
postgres=# SELECT pg_size_pretty(pg_total_relation_size('items')) AS total_size;
 total_size
------------
 1424 MB
(1 row)

postgres=#

-- Run VACUUM FULL
postgres=# VACUUM FULL items;
VACUUM
postgres=#

-- Check table size again
postgres=# SELECT pg_size_pretty(pg_total_relation_size('items')) AS total_size;
 total_size
------------
 712 MB  <--- shrunk back down to original size!, free space released to the OS. 
(1 row)

postgres=#

-- VACUUM FULL rewrites the entire table into a new file on disk.

[postgres@pgdb01 5]$ ls -lrth /pgData/pgsql17/data/base/5/65690*
-rw-------. 1 postgres postgres 0 Sep 22 19:23 /pgData/pgsql17/data/base/5/65690
[postgres@pgdb01 5]$

postgres=# SELECT pg_relation_filepath('public.items');
 pg_relation_filepath
----------------------
 base/5/65698
(1 row)

postgres=#

[postgres@pgdb01 5]$ ls -lrth /pgData/pgsql17/data/base/5/65698*
-rw-------. 1 postgres postgres 498M Sep 22 19:23 /pgData/pgsql17/data/base/5/65698
[postgres@pgdb01 5]$

65690 -- This file no more availble.

Warning: VACUUM FULL rewrites the entire table into a new file on disk. It places an EXCLUSIVE LOCK on the table, blocking all reads and writes until it finishes. Use with caution in production!

7. VACUUM ANALYZE (Optimizing Query Performance)

Whenever data changes significantly, PostgreSQL’s query optimizer needs updated statistics to choose the fastest query plan.

VACUUM ANALYZE cleans up dead rows and refreshes the planner’s statistics in a single command:

-- Clean dead rows + update query planner statistics
postgres=# VACUUM ANALYZE items;
VACUUM
postgres=#

Pro Tip: Always run VACUUM ANALYZE after large bulk imports or mass data updates.

8. Autovacuum: The Background Worker

You don’t need to manually run VACUUM all day. PostgreSQL includes a daemon called Autovacuum that triggers automatically when a table exceeds a dead-tuple threshold (by default, when ~20% of rows are modified).

-- It was disabled for DEMO, enable back to ture.
postgres=# ALTER TABLE items SET (autovacuum_enabled = true);
ALTER TABLE
postgres=#

--- Confirming default autovacuum settings

postgres=# SHOW autovacuum;
 autovacuum
------------
 on
(1 row)

postgres=# SHOW autovacuum_naptime;
 autovacuum_naptime
--------------------
 1min
(1 row)

postgres=# SHOW autovacuum_vacuum_threshold;
 autovacuum_vacuum_threshold
-----------------------------
 50
(1 row)

postgres=# SHOW autovacuum_vacuum_scale_factor;
 autovacuum_vacuum_scale_factor
--------------------------------
 0.2
(1 row)

postgres=#

-- Check when autovacuum last cleaned your tables
postgres=# SELECT
    relname,
    last_vacuum,
    last_autovacuum,
    last_analyze,
    last_autoanalyze
FROM pg_stat_user_tables
WHERE relname = 'items';
 relname |         last_vacuum          | last_autovacuum |         last_analyze          | last_autoanalyze
---------+------------------------------+-----------------+-------------------------------+------------------
 items   | 2026-09-22 19:28:22.29634+08 |                 | 2026-09-22 19:28:22.992613+08 |
(1 row)

postgres=#

-- LOAD some data and check again autovacuum last cleaned status

postgres=# UPDATE items SET price = price + 20;
UPDATE 10000000
postgres=#

postgres=# SELECT
    relname,
    last_vacuum,
    last_autovacuum,
    last_analyze,
    last_autoanalyze
FROM pg_stat_user_tables
WHERE relname = 'items';
 relname |         last_vacuum          |        last_autovacuum        |         last_analyze          |       last_autoanalyze
---------+------------------------------+-------------------------------+-------------------------------+-------------------------------
 items   | 2026-09-22 19:28:22.29634+08 | 2026-09-22 19:40:56.685873+08 | 2026-09-22 19:28:22.992613+08 | 2026-09-22 19:40:58.096743+08
(1 row)

postgres=#

Note: Autovacuum automatically performs VACUUM to clean up obsolete row versions and ANALYZE to maintain optimizer statistics. It does NOT performs VACUUM FULL.

9. Quick Summary Cheatsheet

CommandWhat it DoesReclaims OS Disk?Locks Table?
VACUUMCleans dead rows; marks space reusable for PostgresNoNo (Safe)
VACUUM ANALYZECleans dead rows + updates optimizer statisticsNoNo (Safe)
VACUUM FULLRewrites table completely, shrinks file sizeYesYes (Exclusive lock)
AutovacuumRuns standard vacuum automatically in backgroundNoNo (Safe)
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

MVCC in PostgreSQL

MVCC in PostgreSQL

Table of Contents



1. The Core Concept (Why MVCC?)

MVCC stands for Multi-Version Concurrency Control.

Its primary goal is simple: Readers should never block Writers, and Writers should never block Readers.

Instead of locking a row when someone is updating it (which makes everyone else wait), PostgreSQL keeps multiple versions of the same row active at the same time. Different transactions see different versions depending on when they started.

2. Hidden Columns (xmin & xmax)

Every table you create in PostgreSQL has hidden system columns. The two most important ones for MVCC are:

  • xmin: The Transaction ID that created/inserted this row version.
  • xmax: The Transaction ID that deleted or updated this row version. (If it is 0, the row is still actively alive).
-- To view these columns by explicitly querying them:
SELECT xmin, xmax, * FROM table_name;

3. Create a Test Table

-- Create a simple products table and  insert our first record and see how Postgres assigns the transaction ID.

[postgres@pgdb01 ~]$ psql -c "CREATE TABLE products (id SERIAL PRIMARY KEY,name TEXT,price INT);"
CREATE TABLE
[postgres@pgdb01 ~]$

-- Insert a row
[postgres@pgdb01 ~]$ psql -c "INSERT INTO products (name, price) VALUES ('Laptop', 1000);"
INSERT 0 1
[postgres@pgdb01 ~]$ 

-- Query the row with xmin and xmax
[postgres@pgdb01 ~]$ psql -c "SELECT xmin, xmax, id, name, price FROM products;"
 xmin | xmax | id |  name  | price
------+------+----+--------+-------
 2156 |    0 |  1 | Laptop |  1000
(1 row)

[postgres@pgdb01 ~]$
  • xmin = 2156: xmin represents the transaction ID that created the row version. xmin = 2156
  • xmax = 0: xmax represents the transaction ID that deleted or updated. xmax = 0 means No one has deleted/updated it yet . It is actively visible to everyone.

4. UPDATE Creates a New Version

Now, let’s see MVCC in action. Open two separate database sessions/terminal windows.

Terminal 1 (The Writer): Start a transaction and update the price, but do not commit yet.

postgres=# BEGIN;
BEGIN
postgres=*# SELECT txid_current();
 txid_current
--------------
         2157
(1 row)

postgres=*# UPDATE products SET price = 1200 WHERE id = 1;
UPDATE 1
postgres=*#

Terminal 2 (The Reader): Check what Terminal 2 sees:

[postgres@pgdb01 ~]$ psql -c "SELECT xmin, xmax, id, name, price FROM products;"
 xmin | xmax | id |  name  | price
------+------+----+--------+-------
 2156 | 2157 |  1 | Laptop |  1000
(1 row)

[postgres@pgdb01 ~]$
Internally, PostgreSQL creates a new row version.

Before UPDATE

Row Version A
+----------------+
| id = 1         |
| price = 1000   |
+----------------+


After UPDATE

Old Version                New Version
+----------------+          +----------------+
| id = 1         |          | id = 1         |
| price = 1000   |          | price = 1200   |
| xmax = 2157    |          | xmin = 2157    |
+----------------+          +----------------+
        |                           |
        | old version               | new version
        v                           v
      xmax                         xmin

xmax = old version (xmax refers Dead rows)
xmin = new version
The old row version is not immediately physically removed.
  • The reader still sees the original price of 1000. They are not blocked!
  • The row now has an xmax of 2157. This tells Postgres: “Transaction 2157 is currently changing or deleting this row. If you aren’t Transaction 2157, keep looking at the old data.”
  • Under the hood, Postgres has already written a new row version with a price of 1200 and xmin = 2157, but it is completely invisible to Terminal 2 because transaction 2157 hasn’t committed yet.

5. After COMMIT

Let’s finalize the transaction and see the transition.

Terminal 1 (The Writer): Commit the change.

postgres=# BEGIN;
BEGIN
postgres=*# SELECT txid_current();
 txid_current
--------------
         2157
(1 row)

postgres=*# UPDATE products SET price = 1200 WHERE id = 1;
UPDATE 1
postgres=*# COMMIT;   <------------------ (only run COMMIT)
COMMIT
postgres=#

Terminal 2 (The Reader): Query the table again. What Terminal 2 sees now

[postgres@pgdb01 ~]$ psql -c "SELECT xmin, xmax, id, name, price FROM products;"
 xmin | xmax | id |  name  | price
------+------+----+--------+-------
 2157 |    0 |  1 | Laptop |  1200
(1 row)

[postgres@pgdb01 ~]$
  • The old row version (price 1000) is now marked as dead(invisible).
  • The new row version (price 1200) is now alive(current row) with xmin = 2157 and xmax = 0.

6. DELETE Doesn’t Actually Delete

In Postgres, a DELETE statement does not physically erase data from the hard drive immediately. It just changes a flag.

postgres=# BEGIN;
BEGIN
postgres=*# DELETE FROM products WHERE id = 1;
DELETE 1
postgres=*# SELECT txid_current();
 txid_current
--------------
         2158
(1 row)

postgres=*# COMMIT;
COMMIT
postgres=#


A DELETE Internally, simply marks the existing row as dead.

Before DELETE

+----------------+
| id=1           |
| pirce = 1200   |
+----------------+

After DELETE

+----------------+
| id=1           |
| price = 1200   |
| xmax = 2158    |
+----------------+

Later, VACUUM can remove dead tuples when they are no longer needed by active transactions.

Under the hood, Postgres simply updated the existing row’s xmax value to 2158. Because transaction 2158 is committed, Postgres now knows that this row is dead and shouldn’t be shown to any new queries. The actual space on the disk won’t be freed until we vacuum.

7. Dead tuples and VACUUM

Because UPDATE creates new rows and DELETE just marks them as dead, database will accumulate “dead tuples” (bloat) over time.
In simple way, Since UPDATE and DELETE don’t remove old rows, dead rows pile up. This is called bloat.

Postgres uses a process called VACUUM to find these dead rows, clear them out, and mark that space as reusable for future inserts.

-- 1. Check how many dead rows are lurking a table

[postgres@pgdb01 ~]$ psql -c "SELECT relname, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'products';"
 relname  | n_live_tup | n_dead_tup
----------+------------+------------
 products |          0 |          2   <--------------------
(1 row)

[postgres@pgdb01 ~]$


-- 2. Run a manual vacuum to clean up

[postgres@pgdb01 ~]$ psql -c "VACUUM VERBOSE products;"
INFO:  vacuuming "postgres.public.products"
INFO:  table "products": truncated 1 to 0 pages
INFO:  finished vacuuming "postgres.public.products": index scans: 1
pages: 1 removed, 0 remain, 1 scanned (100.00% of total)
tuples: 2 removed, 0 remain, 0 are dead but not yet removable
removable cutoff: 2159, which was 1 XIDs old when operation ended
new relfrozenxid: 2159, which is 4 XIDs ahead of previous value
frozen: 0 pages from table (0.00% of total) had 0 tuples frozen
index scan needed: 1 pages from table (100.00% of total) had 1 dead item identifiers removed
index "products_pkey": pages: 2 in total, 0 newly deleted, 0 currently deleted, 0 reusable
avg read rate: 0.000 MB/s, avg write rate: 11.905 MB/s
buffer usage: 61 hits, 0 misses, 8 dirtied
WAL usage: 11 records, 8 full page images, 47529 bytes
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
INFO:  vacuuming "postgres.pg_toast.pg_toast_65663"
INFO:  finished vacuuming "postgres.pg_toast.pg_toast_65663": index scans: 0
pages: 0 removed, 0 remain, 0 scanned (100.00% of total)
tuples: 0 removed, 0 remain, 0 are dead but not yet removable
removable cutoff: 2160, which was 0 XIDs old when operation ended
new relfrozenxid: 2160, which is 5 XIDs ahead of previous value
frozen: 0 pages from table (100.00% of total) had 0 tuples frozen
index scan not needed: 0 pages from table (100.00% of total) had 0 dead item identifiers removed
avg read rate: 22.978 MB/s, avg write rate: 22.978 MB/s
buffer usage: 27 hits, 1 misses, 1 dirtied
WAL usage: 1 records, 1 full page images, 8097 bytes
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
VACUUM
[postgres@pgdb01 ~]$


-- 3. Check again; dead rows should now be 0

[postgres@pgdb01 ~]$ psql -c "SELECT relname, n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 'products';"
 relname  | n_live_tup | n_dead_tup
----------+------------+------------
 products |          0 |          0  
(1 row)

[postgres@pgdb01 ~]$

Note: PostgreSQL runs an Autovacuum background process automatically, so you rarely need to run manual vacuums in production.

8. Quick Summary Cheatsheet

To keep MVCC simple, remember these four rules:

OperationWhat Postgres Actually Does
INSERTCreates a row with xmin = Current TXID.
DELETEMark row as dead, does not physically erase it. Sets xmax = Current TXID
UPDATE INSERT new version + DELETE (mark old version as dead).
VACUUMSweeps up the dead rows to reclaim storage space.
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

Install EDB Postgres Advanced Server 17 on Linux 8

Install EDB Postgres Advanced Server 17 on Linux 8

Table of Contents



1. Setup EDB repository

--Logon https://www.enterprisedb.com/repos and select --->  RHEL8 x86_64 then ----> EDB Postgres Advanced Server  ---> 17

[root@lxtrdedbpsgv01 ~]# curl -1sSLf 'https://downloads.enterprisedb.com/PMuvhbPsvEt9Rd4ORs5gnUT6z1L4P85R/enterprise/setup.rpm.sh' | sudo -E bash
Executing the  setup script for the 'enterprisedb/enterprise' repository ...

   OK: Checking for required executable 'curl' ...
   OK: Checking for required executable 'rpm' ...
   OK: Detecting your OS distribution and release using system methods ...
 ^^^^: ... Detected/provided for your OS/distribution, version and architecture:
 >>>>:
 >>>>: ... distro=ol  version=8.7  codename=8.7  arch=x86_64
 >>>>:
   OK: Importing 'enterprisedb/enterprise' repository GPG keys into rpm ...
   OK: Checking for available package manager (DNF/Microdnf/YUM/Zypper) ...
 ^^^^: ... Detected package manager as 'dnf'
   OK: Checking for dnf dependency 'dnf-plugins-core' ...
   OK: Checking if upstream install config is OK ...
   OK: Fetching 'enterprisedb/enterprise' repository configuration ...
   OK: Installing 'enterprisedb/enterprise' repository via dnf ...
  RUN: Updating the dnf cache to fetch the new repository metadata ...Importing GPG key 0x9F1EF813:
 Userid     : "Cloudsmith Package (enterprisedb/enterprise) <support@cloudsmith.io>"
 Fingerprint: 31A4 CF09 0B3A E265 F131 58DE E71E B082 9F1E F813
 From       : https://downloads.enterprisedb.com/PMuvhbPsvEt9Rd4ORs5gnUT6z1L4P85R/enterprise/gpg.E71EB0829F1EF813.key
Importing GPG key 0x9F1EF813:
 Userid     : "Cloudsmith Package (enterprisedb/enterprise) <support@cloudsmith.io>"
 Fingerprint: 31A4 CF09 0B3A E265 F131 58DE E71E B082 9F1E F813
 From       : https://downloads.enterprisedb.com/PMuvhbPsvEt9Rd4ORs5gnUT6z1L4P85R/enterprise/gpg.E71EB0829F1EF813.key
Importing GPG key 0x9F1EF813:
 Userid     : "Cloudsmith Package (enterprisedb/enterprise) <support@cloudsmith.io>"
 Fingerprint: 31A4 CF09 0B3A E265 F131 58DE E71E B082 9F1E F813
 From       : https://downloads.enterprisedb.com/PMuvhbPsvEt9Rd4ORs5gnUT6z1L4P85R/enterprise/gpg.E71EB0829F1EF813.key
   OK: Updating the dnf cache to fetch the new repository metadata ...
   OK: The repository has been installed successfully - You're ready to rock!

[root@lxtrdedbpsgv01 ~]#

[root@lxtrdedbpsgv01 ~]# dnf repolist | grep -i enterprisedb
enterprisedb-enterprise        enterprisedb-enterprise
enterprisedb-enterprise-noarch enterprisedb-enterprise-noarch
enterprisedb-enterprise-source enterprisedb-enterprise-source
[root@lxtrdedbpsgv01 ~]#

2. Install EDB Postgres Advanced Server 17

[root@lxtrdedbpsgv01 ~]# dnf -y install edb-as17-server
..
Dependencies resolved.
========================================================================================================================================================================================================================================
 Package                                                         Architecture                                 Version                                               Repository                                                     Size
========================================================================================================================================================================================================================================
Installing:
 edb-as17-server                                                 x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                        12 k
Installing dependencies:
 edb-as17-server-client                                          x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                       2.2 M
 edb-as17-server-contrib                                         x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                       831 k
 edb-as17-server-core                                            x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                       7.4 M
 edb-as17-server-docs                                            x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                        21 k
 edb-as17-server-ecpg                                            x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                       416 k
 edb-as17-server-libs                                            x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                       652 k
 edb-as17-server-plperl                                          x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                        76 k
 edb-as17-server-plpython3                                       x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                       110 k
 edb-as17-server-pltcl                                           x86_64                                       17.11.0-1.el8                                         enterprisedb-enterprise                                        49 k
 edb-libicu66                                                    x86_64                                       66.1-3.el8                                            enterprisedb-enterprise                                       9.2 M
 tcl                                                             x86_64                                       1:8.6.8-2.el8                                         ol8_baseos_latest                                             1.1 M
Installing weak dependencies:
 beacon-agent                                                    x86_64                                       1.401.5-1                                             enterprisedb-enterprise                                       115 M

Transaction Summary
========================================================================================================================================================================================================================================
Install  13 Packages

Total download size: 137 M
Installed size: 412 M
Downloading Packages:
(1/13): edb-as17-server-17.11.0-1.el8.x86_64.rpm                                                                                                                                                        9.1 kB/s |  12 kB     00:01
(2/13): edb-as17-server-client-17.11.0-1.el8.x86_64.rpm                                                                                                                                                 1.0 MB/s | 2.2 MB     00:02
(3/13): edb-as17-server-contrib-17.11.0-1.el8.x86_64.rpm                                                                                                                                                448 kB/s | 831 kB     00:01
(4/13): edb-as17-server-docs-17.11.0-1.el8.x86_64.rpm                                                                                                                                                    17 kB/s |  21 kB     00:01
(5/13): edb-as17-server-core-17.11.0-1.el8.x86_64.rpm                                                                                                                                                   2.8 MB/s | 7.4 MB     00:02
(6/13): edb-as17-server-ecpg-17.11.0-1.el8.x86_64.rpm                                                                                                                                                   273 kB/s | 416 kB     00:01
(7/13): edb-as17-server-libs-17.11.0-1.el8.x86_64.rpm                                                                                                                                                   433 kB/s | 652 kB     00:01
(8/13): edb-as17-server-plperl-17.11.0-1.el8.x86_64.rpm                                                                                                                                                  54 kB/s |  76 kB     00:01
(9/13): edb-as17-server-plpython3-17.11.0-1.el8.x86_64.rpm                                                                                                                                               72 kB/s | 110 kB     00:01
(10/13): edb-as17-server-pltcl-17.11.0-1.el8.x86_64.rpm                                                                                                                                                  50 kB/s |  49 kB     00:00
(11/13): tcl-8.6.8-2.el8.x86_64.rpm                                                                                                                                                                     1.3 MB/s | 1.1 MB     00:00
(12/13): beacon-agent-1.401.5_linux_amd64.rpm                                                                                                                                                           6.9 MB/s | 115 MB     00:16
(13/13): edb-libicu66-66.1-3.el8.x86_64.rpm                                                                                                                                                             1.1 MB/s | 9.2 MB     00:08
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                                                                                                   8.2 MB/s | 137 MB     00:16
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                                                                                                                                                                                1/1
  Installing       : edb-libicu66-66.1-3.el8.x86_64                                                                                                                                                                                1/13
  Running scriptlet: edb-libicu66-66.1-3.el8.x86_64                                                                                                                                                                                1/13
  Installing       : edb-as17-server-libs-17.11.0-1.el8.x86_64                                                                                                                                                                     2/13
  Running scriptlet: edb-as17-server-libs-17.11.0-1.el8.x86_64                                                                                                                                                                     2/13
  Installing       : edb-as17-server-ecpg-17.11.0-1.el8.x86_64                                                                                                                                                                     3/13
  Installing       : edb-as17-server-client-17.11.0-1.el8.x86_64                                                                                                                                                                   4/13
  Running scriptlet: edb-as17-server-client-17.11.0-1.el8.x86_64                                                                                                                                                                   4/13
  Installing       : edb-as17-server-contrib-17.11.0-1.el8.x86_64                                                                                                                                                                  5/13
  Installing       : tcl-1:8.6.8-2.el8.x86_64                                                                                                                                                                                      6/13
  Running scriptlet: tcl-1:8.6.8-2.el8.x86_64                                                                                                                                                                                      6/13
  Installing       : edb-as17-server-docs-17.11.0-1.el8.x86_64                                                                                                                                                                     7/13
  Installing       : beacon-agent-1.401.5-1.x86_64                                                                                                                                                                                 8/13
  Running scriptlet: edb-as17-server-core-17.11.0-1.el8.x86_64                                                                                                                                                                     9/13
  Installing       : edb-as17-server-core-17.11.0-1.el8.x86_64                                                                                                                                                                     9/13
  Running scriptlet: edb-as17-server-core-17.11.0-1.el8.x86_64                                                                                                                                                                     9/13
  Installing       : edb-as17-server-plperl-17.11.0-1.el8.x86_64                                                                                                                                                                  10/13
  Installing       : edb-as17-server-plpython3-17.11.0-1.el8.x86_64                                                                                                                                                               11/13
  Installing       : edb-as17-server-pltcl-17.11.0-1.el8.x86_64                                                                                                                                                                   12/13
  Installing       : edb-as17-server-17.11.0-1.el8.x86_64                                                                                                                                                                         13/13
  Running scriptlet: edb-as17-server-17.11.0-1.el8.x86_64                                                                                                                                                                         13/13
  Verifying        : beacon-agent-1.401.5-1.x86_64                                                                                                                                                                                 1/13
  Verifying        : edb-as17-server-17.11.0-1.el8.x86_64                                                                                                                                                                          2/13
  Verifying        : edb-as17-server-client-17.11.0-1.el8.x86_64                                                                                                                                                                   3/13
  Verifying        : edb-as17-server-contrib-17.11.0-1.el8.x86_64                                                                                                                                                                  4/13
  Verifying        : edb-as17-server-core-17.11.0-1.el8.x86_64                                                                                                                                                                     5/13
  Verifying        : edb-as17-server-docs-17.11.0-1.el8.x86_64                                                                                                                                                                     6/13
  Verifying        : edb-as17-server-ecpg-17.11.0-1.el8.x86_64                                                                                                                                                                     7/13
  Verifying        : edb-as17-server-libs-17.11.0-1.el8.x86_64                                                                                                                                                                     8/13
  Verifying        : edb-as17-server-plperl-17.11.0-1.el8.x86_64                                                                                                                                                                   9/13
  Verifying        : edb-as17-server-plpython3-17.11.0-1.el8.x86_64                                                                                                                                                               10/13
  Verifying        : edb-as17-server-pltcl-17.11.0-1.el8.x86_64                                                                                                                                                                   11/13
  Verifying        : edb-libicu66-66.1-3.el8.x86_64                                                                                                                                                                               12/13
  Verifying        : tcl-1:8.6.8-2.el8.x86_64                                                                                                                                                                                     13/13

Installed:
  beacon-agent-1.401.5-1.x86_64                edb-as17-server-17.11.0-1.el8.x86_64        edb-as17-server-client-17.11.0-1.el8.x86_64   edb-as17-server-contrib-17.11.0-1.el8.x86_64   edb-as17-server-core-17.11.0-1.el8.x86_64
  edb-as17-server-docs-17.11.0-1.el8.x86_64    edb-as17-server-ecpg-17.11.0-1.el8.x86_64   edb-as17-server-libs-17.11.0-1.el8.x86_64     edb-as17-server-plperl-17.11.0-1.el8.x86_64    edb-as17-server-plpython3-17.11.0-1.el8.x86_64
  edb-as17-server-pltcl-17.11.0-1.el8.x86_64   edb-libicu66-66.1-3.el8.x86_64              tcl-1:8.6.8-2.el8.x86_64

Complete!
[root@lxtrdedbpsgv01 ~]#

[root@lxtrdedbpsgv01 ~]# rpm -qa | grep edb-as17
edb-as17-server-libs-17.11.0-1.el8.x86_64
edb-as17-server-plperl-17.11.0-1.el8.x86_64
edb-as17-server-docs-17.11.0-1.el8.x86_64
edb-as17-server-client-17.11.0-1.el8.x86_64
edb-as17-server-pltcl-17.11.0-1.el8.x86_64
edb-as17-server-contrib-17.11.0-1.el8.x86_64
edb-as17-server-core-17.11.0-1.el8.x86_64
edb-as17-server-17.11.0-1.el8.x86_64
edb-as17-server-ecpg-17.11.0-1.el8.x86_64
edb-as17-server-plpython3-17.11.0-1.el8.x86_64
[root@lxtrdedbpsgv01 ~]#
[root@lxtrdedbpsgv01 ~]# /usr/edb/as17/bin/psql --version
psql (EnterpriseDB) 17.11.0
[root@lxtrdedbpsgv01 ~]#

3. Check and reset EDB user password

[root@lxtrdedbpsgv01 ~]# id enterprisedb
uid=972(enterprisedb) gid=972(enterprisedb) groups=972(enterprisedb)
[root@lxtrdedbpsgv01 ~]# getent passwd enterprisedb
enterprisedb:x:972:972:EDBAS User:/var/lib/edb:/bin/bash
[root@lxtrdedbpsgv01 ~]#
[root@lxtrdedbpsgv01 ~]# passwd enterprisedb
Changing password for user enterprisedb.
New password:
BAD PASSWORD: The password is shorter than 8 characters
Retype new password:
passwd: all authentication tokens updated successfully.
[root@lxtrdedbpsgv01 ~]#

4. Change ownership for mount points

[root@lxtrdedbpsgv01 ~]# chown -R enterprisedb:enterprisedb /edb*
[root@lxtrdedbpsgv01 ~]#
[root@lxtrdedbpsgv01 ~]# df -h /edb/*
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdf1       100G  746M  100G   1% /edb/pgArch
/dev/sdd1       100G  746M  100G   1% /edb/pgBackup
/dev/sdb1       100G  746M  100G   1% /edb/pgData
/dev/sde1       100G  746M  100G   1% /edb/pgBin
/dev/sdc1       100G  746M  100G   1% /edb/tsdata
[root@lxtrdedbpsgv01 ~]#

5. Setup custom Bin directory (Optional)

[root@lxtrdedbpsgv01 ~]# ls -l /usr/edb/as17/
total 36
drwxr-xr-x. 2 root root  4096 Aug 22 03:49 bin
-r--r--r--. 1 root root 15716 Aug 12 12:26 edb-as17-server_license.txt
drwxr-xr-x. 3 root root  8192 Aug 22 03:49 lib
drwxr-xr-x. 9 root root  4096 Aug 22 03:49 share
[root@lxtrdedbpsgv01 ~]#
[root@lxtrdedbpsgv01 ~]# cp -rp /usr/edb/as17 /edb/pgBin
[root@lxtrdedbpsgv01 ~]# 
[root@lxtrdedbpsgv01 ~]# ls -l /edb/pgBin/as17/
total 32
drwxr-xr-x. 2 root root  4096 Aug 22 03:49 bin
-r--r--r--. 1 root root 15716 Aug 12 12:26 edb-as17-server_license.txt
drwxr-xr-x. 3 root root  4096 Aug 22 03:49 lib
drwxr-xr-x. 9 root root  4096 Aug 22 03:49 share
[root@lxtrdedbpsgv01 ~]#
[root@lxtrdedbpsgv01 ~]# chown -R enterprisedb:enterprisedb /edb/pgBin/*
[root@lxtrdedbpsgv01 ~]# ls -l /edb/pgBin/as17/
total 32
drwxr-xr-x. 2 enterprisedb enterprisedb  4096 Aug 22 03:49 bin
-r--r--r--. 1 enterprisedb enterprisedb 15716 Aug 12 12:26 edb-as17-server_license.txt
drwxr-xr-x. 3 enterprisedb enterprisedb  4096 Aug 22 03:49 lib
drwxr-xr-x. 9 enterprisedb enterprisedb  4096 Aug 22 03:49 share
[root@lxtrdedbpsgv01 ~]#


One caution: simply copying /usr/edb/as17 can miss RPM-managed symlinks, configuration files, or dependencies outside that directory. Also, future dnf update operations will update /usr/edb/as17, but will not automatically update /edb/pgBase. So after an EDB RPM upgrade, you would need to copy the new binaries again. 
rsync -av /usr/edb/as17/ /edb/pgBin/as17

6. Configure environment variables

[root@lxtrdedbpsgv01 ~]# su - enterprisedb
[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ cat .bash_profile

export PGHOME=/edb/pgBin/as17
export PGDATA=/edb/pgData/as17/data
export PATH=$PGHOME/bin:$PATH

[enterprisedb@lxtrdedbpsgv01 ~]$ . .bash_profile
[enterprisedb@lxtrdedbpsgv01 ~]$ which pg_ctl
/edb/pgBin/as17/bin/pg_ctl
[enterprisedb@lxtrdedbpsgv01 ~]$ which psql
/edb/pgBin/as17/bin/psql
[enterprisedb@lxtrdedbpsgv01 ~]$ which pg_basebackup
/edb/pgBin/as17/bin/pg_basebackup
[enterprisedb@lxtrdedbpsgv01 ~]$ which pg_rewind
/edb/pgBin/as17/bin/pg_rewind
[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ pg_ctl --version
pg_ctl (EnterpriseDB) 17.11.0
[enterprisedb@lxtrdedbpsgv01 ~]$ psql --version
psql (EnterpriseDB) 17.11.0
[enterprisedb@lxtrdedbpsgv01 ~]$

7. Initialize the database

[enterprisedb@lxtrdedbpsgv01 ~]$ which initdb
/edb/pgBin/as17/bin/initdb
[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ initdb --version
initdb (EnterpriseDB) 17.11.0
[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ which edb-as-17-setup
/edb/pgBin/as17/bin/edb-as-17-setup
[enterprisedb@lxtrdedbpsgv01 ~]$

[enterprisedb@lxtrdedbpsgv01 ~]$ ls -ld /edb/pgData/as17/data
ls: cannot access '/edb/pgData/as17/data': No such file or directory
[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ mkdir -p /edb/pgData/as17/data
[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ chmod 700 /edb/pgData/as17/data
[enterprisedb@lxtrdedbpsgv01 ~]$ ls -ld /edb/pgData/as17/data
drwx------. 2 enterprisedb enterprisedb 6 Aug 22 06:16 /edb/pgData/as17/data
[enterprisedb@lxtrdedbpsgv01 ~]$

[enterprisedb@lxtrdedbpsgv01 ~]$ /edb/pgBin/as17/bin/initdb -D /edb/pgData/as17/data
The files belonging to this database system will be owned by user "enterprisedb".
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.
Transparent data encryption is disabled.

fixing permissions on existing directory /edb/pgData/as17/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... America/New_York
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
creating edb sys ... ok
loading edb contrib modules ...
edb_redwood_bytea.sql
edb_redwood_date.sql
dbms_alert_public.sql
dbms_alert.plb
dbms_assert_public.sql
dbms_assert.plb
dbms_job_public.sql
dbms_job.plb
dbms_lob_public.sql
dbms_lob.plb
dbms_output_public.sql
dbms_output.plb
dbms_pipe_public.sql
dbms_pipe.plb
dbms_rls_public.sql
dbms_rls.plb
dbms_sql_public.sql
dbms_sql.plb
dbms_utility_public.sql
dbms_utility.plb
dbms_aqadm_public.sql
dbms_aqadm.plb
dbms_aq_public.sql
dbms_aq.plb
dbms_profiler_public.sql
dbms_profiler.plb
dbms_random_public.sql
dbms_random.plb
dbms_redact_public.sql
dbms_redact.plb
dbms_lock_public.sql
dbms_lock.plb
dbms_scheduler_public.sql
dbms_scheduler.plb
dbms_crypto_public.sql
dbms_crypto.plb
dbms_mview_public.sql
dbms_mview.plb
dbms_session_public.sql
dbms_session.plb
xmltype_public.sql
xmltype.plb
dbms_xmldom_public.sql
dbms_xmldom.plb
dbms_privilege_capture_public.sql
dbms_privilege_capture.plb
edb_bulkload.sql
edb_gen.sql
edb_objects.sql
edb_redwood_casts.sql
edb_redwood_nls.sql
edb_redwood_strings.sql
edb_redwood_views.sql
utl_encode_public.sql
utl_encode.plb
utl_http_public.sql
utl_http.plb
utl_file.plb
edb_ht_public.sql
edb_ht.plb
utl_tcp_public.sql
utl_tcp.plb
utl_smtp_public.sql
utl_smtp.plb
utl_mail_public.sql
utl_mail.plb
utl_url_public.sql
utl_url.plb
utl_raw_public.sql
utl_raw.plb
edb_gen_redwood.sql
waitstates.sql
installing extension edb_dblink_libpq ... ok
installing extension edb_dblink_oci ... ok
snap_tables.sql
snap_functions.sql
dblink_ora.sql
sys_stats.sql
ok
finalizing initial databases ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
initdb: hint: 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:

    /edb/pgBin/as17/bin/pg_ctl -D /edb/pgData/as17/data -l logfile start

[enterprisedb@lxtrdedbpsgv01 ~]$

8. Start PostgreSQL Service

[enterprisedb@lxtrdedbpsgv01 ~]$ /edb/pgBin/as17/bin/pg_ctl -D /edb/pgData/as17/data -l /tmp/postgresql.log start
waiting for server to start.... done
server started
[enterprisedb@lxtrdedbpsgv01 ~]$

[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ ps -ef | grep postgres
enterpr+   42936       1  0 06:25 ?        00:00:00 /edb/pgBin/as17/bin/edb-postgres -D /edb/pgData/as17/data
enterpr+   42937   42936  0 06:25 ?        00:00:00 postgres: logger
enterpr+   42938   42936  0 06:25 ?        00:00:00 postgres: checkpointer
enterpr+   42939   42936  0 06:25 ?        00:00:00 postgres: background writer
enterpr+   42941   42936  0 06:25 ?        00:00:00 postgres: walwriter
enterpr+   42942   42936  0 06:25 ?        00:00:00 postgres: autovacuum launcher
enterpr+   42943   42936  0 06:25 ?        00:00:00 postgres: dbms_aq launcher
enterpr+   42944   42936  0 06:25 ?        00:00:00 postgres: logical replication launcher
enterpr+   43022   41905  0 06:28 pts/0    00:00:00 grep --color=auto postgres
[enterprisedb@lxtrdedbpsgv01 ~]$

[enterprisedb@lxtrdedbpsgv01 ~]$ pstree -p 42936
edb-postgres(42936)─┬─edb-postgres(42937)
                    ├─edb-postgres(42938)
                    ├─edb-postgres(42939)
                    ├─edb-postgres(42941)
                    ├─edb-postgres(42942)
                    ├─edb-postgres(42943)
                    └─edb-postgres(42944)
[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ ls -ltr /edb/pgData/as17/data
total 76
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_twophase
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_tblspc
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_stat_tmp
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_snapshots
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_serial
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_replslot
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_notify
drwx------. 4 enterprisedb enterprisedb    36 Aug 22 06:24 pg_multixact
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_dynshmem
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:24 pg_commit_ts
-rw-------. 1 enterprisedb enterprisedb     3 Aug 22 06:24 PG_VERSION
-rw-------. 1 enterprisedb enterprisedb 38410 Aug 22 06:24 postgresql.conf
-rw-------. 1 enterprisedb enterprisedb    88 Aug 22 06:24 postgresql.auto.conf
-rw-------. 1 enterprisedb enterprisedb  2640 Aug 22 06:24 pg_ident.conf
-rw-------. 1 enterprisedb enterprisedb  5711 Aug 22 06:24 pg_hba.conf
drwx------. 2 enterprisedb enterprisedb    18 Aug 22 06:24 pg_xact
drwx------. 2 enterprisedb enterprisedb    18 Aug 22 06:24 pg_subtrans
drwx------. 4 enterprisedb enterprisedb   109 Aug 22 06:24 pg_wal
drwx------. 6 enterprisedb enterprisedb    46 Aug 22 06:24 base
drwx------. 4 enterprisedb enterprisedb    68 Aug 22 06:24 pg_logical
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:25 dbms_pipe
drwx------. 2 enterprisedb enterprisedb    48 Aug 22 06:25 log
-rw-------. 1 enterprisedb enterprisedb    46 Aug 22 06:25 current_logfiles
-rw-------. 1 enterprisedb enterprisedb    62 Aug 22 06:25 postmaster.opts
drwx------. 2 enterprisedb enterprisedb     6 Aug 22 06:25 pg_stat
-rw-------. 1 enterprisedb enterprisedb    88 Aug 22 06:25 postmaster.pid
drwx------. 2 enterprisedb enterprisedb  4096 Aug 22 06:27 global
[enterprisedb@lxtrdedbpsgv01 ~]$


[enterprisedb@lxtrdedbpsgv01 ~]$ psql -d postgres -c "SELECT version();"
                                                                     version
-------------------------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 17.11 (EnterpriseDB Advanced Server 17.11.0) on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-28), 64-bit
(1 row)

[enterprisedb@lxtrdedbpsgv01 ~]$
[enterprisedb@lxtrdedbpsgv01 ~]$ psql -d postgres
psql (17.11.0)
Type "help" for help.

postgres=#
postgres=# \l
                                                              List of databases
   Name    |    Owner     | Encoding | Locale Provider |   Collate   |    Ctype    | ICU | Locale | ICU Rules |       Access privileges
-----------+--------------+----------+-----------------+-------------+-------------+-----+--------+-----------+-------------------------------
 edb       | enterprisedb | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |     |        |           |
 postgres  | enterprisedb | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |     |        |           |
 template0 | enterprisedb | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |     |        |           | =c/enterprisedb              +
           |              |          |                 |             |             |     |        |           | enterprisedb=CTc/enterprisedb
 template1 | enterprisedb | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |     |        |           | =c/enterprisedb              +
           |              |          |                 |             |             |     |        |           | enterprisedb=CTc/enterprisedb
(4 rows)

postgres=#
postgres=# \conninfo
You are connected to database "postgres" as user "enterprisedb" via socket in "/tmp" at port "5444".
postgres=#
postgres=# \du
                                     List of roles
         Role name         |                         Attributes
---------------------------+------------------------------------------------------------
 aq_administrator_role     | No inheritance, Cannot login                              +
                           | Profile default
 capture_admin             | No inheritance, Cannot login                              +
                           | Profile default
 edb_createdirectory_admin | No inheritance, Cannot login                              +
                           | Profile default
 edb_dropdirectory_admin   | No inheritance, Cannot login                              +
                           | Profile default
 enterprisedb              | Superuser, Create role, Create DB, Replication, Bypass RLS+
                           | Profile default

postgres=# ALTER USER enterprisedb PASSWORD 'oracle';
ALTER ROLE
postgres=# 
postgres=# \duS
                                      List of roles
          Role name          |                         Attributes
-----------------------------+------------------------------------------------------------
 aq_administrator_role       | No inheritance, Cannot login                              +
                             | Profile default
 capture_admin               | No inheritance, Cannot login                              +
                             | Profile default
 edb_createdirectory_admin   | No inheritance, Cannot login                              +
                             | Profile default
 edb_dropdirectory_admin     | No inheritance, Cannot login                              +
                             | Profile default
 enterprisedb                | Superuser, Create role, Create DB, Replication, Bypass RLS+
                             | Profile default
 pg_checkpoint               | Cannot login                                              +
                             | Profile default
 pg_create_subscription      | Cannot login                                              +
                             | Profile default
 pg_database_owner           | Cannot login                                              +
                             | Profile default
 pg_execute_server_program   | Cannot login                                              +
                             | Profile default
 pg_maintain                 | Cannot login                                              +
                             | Profile default
 pg_monitor                  | Cannot login                                              +
                             | Profile default
 pg_read_all_data            | Cannot login                                              +
                             | Profile default
 pg_read_all_settings        | Cannot login                                              +
                             | Profile default
 pg_read_all_stats           | Cannot login                                              +
                             | Profile default
 pg_read_server_files        | Cannot login                                              +
                             | Profile default
 pg_signal_backend           | Cannot login                                              +
                             | Profile default
 pg_stat_scan_tables         | Cannot login                                              +
                             | Profile default
 pg_use_reserved_connections | Cannot login                                              +
                             | Profile default
 pg_write_all_data           | Cannot login                                              +
                             | Profile default
 pg_write_server_files       | Cannot login                                              +
                             | Profile default

postgres=#

postgres=# \dn
      List of schemas
  Name  |       Owner
--------+-------------------
 public | pg_database_owner
(1 row)

postgres=# \dnS
            List of schemas
        Name        |       Owner
--------------------+-------------------
 information_schema | enterprisedb
 pg_catalog         | enterprisedb
 pg_temp_0          | enterprisedb
 pg_toast           | enterprisedb
 pg_toast_temp_0    | enterprisedb
 public             | pg_database_owner
 sys                | enterprisedb
(7 rows)

postgres=# \dnS+
                                                  List of schemas
        Name        |       Owner       |           Access privileges            |           Description
--------------------+-------------------+----------------------------------------+----------------------------------
 information_schema | enterprisedb      | enterprisedb=UC/enterprisedb          +|
                    |                   | =U/enterprisedb                        |
 pg_catalog         | enterprisedb      | enterprisedb=UC/enterprisedb          +| system catalog schema
                    |                   | =U/enterprisedb                        |
 pg_temp_0          | enterprisedb      |                                        |
 pg_toast           | enterprisedb      |                                        | reserved schema for TOAST tables
 pg_toast_temp_0    | enterprisedb      |                                        |
 public             | pg_database_owner | pg_database_owner=UC/pg_database_owner+| standard public schema
                    |                   | =U/pg_database_owner                   |
 sys                | enterprisedb      | enterprisedb=UC/enterprisedb          +| sys schema
                    |                   | =U/enterprisedb                        |
(7 rows)

postgres=#

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

Types of Shutdown in PostgreSQL

Types of Shutdown in PostgreSQL

PostgreSQL Shutdown TypeOracleBehavior
Smart (SIGTERM)SHUTDOWN NORMALThe server stops accepting new connections but allows existing sessions to continue running normally. PostgreSQL shuts down only after all active sessions disconnect and their transactions complete successfully.
Fast (SIGINT – Default)SHUTDOWN IMMEDIATEThe server stops accepting new connections and immediately terminates all active sessions. Any uncommitted transactions are rolled back, and PostgreSQL performs a clean shutdown gracefully.
Immediate (SIGQUIT)SHUTDOWN ABORTThe server terminates all processes instantly without performing a proper shutdown. No cleanup or checkpoint occurs, so PostgreSQL performs crash recovery by replaying WAL files during the next startup.
Shutdown TypeSignalCommand ExampleBehavior
SmartSIGTERMpg_ctl stop -D /pgData -m smartStops new connections, waits for existing sessions to finish normally, then shuts down.
Fast (Default)SIGINTpg_ctl stop -D /pgdata -m fastStops new connections, terminates active sessions, rolls back uncommitted transactions, and shuts down cleanly.
ImmediateSIGQUITpg_ctl stop -D /pgdata -m immediateStops immediately without proper shutdown. Recovery from WAL files happens during next startup.

Difference Between Reload and Restart

  • When we change server configuration parameters, PostgreSQL needs to read those changes before they become active.
  • Reload applies the new configuration without stopping or restarting the database service. Existing connections and database activity continue normally.
  • Some configuration parameter changes cannot be applied through reload and will only take effect after a restart.
  • Restart stops the PostgreSQL server gracefully, ends all running activities, releases resources, closes open files, and then starts the server again with the new configuration.

 

Smart Shutdown (SIGTERM):

[postgres@pgdb02 ~]$ pg_ctl stop -D /pgData/pgsql17/data -m smart
waiting for server to shut down.... done
server stopped
[postgres@pgdb02 ~]$

2026-07-03 14:26:59.741 +08 [3476] LOG:  received smart shutdown request
2026-07-03 14:26:59.752 +08 [3476] LOG:  background worker "logical replication launcher" (PID 3483) exited with exit code 1
2026-07-03 14:26:59.753 +08 [3478] LOG:  shutting down
2026-07-03 14:26:59.755 +08 [3478] LOG:  checkpoint starting: shutdown immediate
2026-07-03 14:26:59.765 +08 [3478] LOG:  checkpoint complete: wrote 3 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.002 s, sync=0.003 s, total=0.012 s; sync files=2, longest=0.003 s, average=0.002 s; distance=0 kB, estimate=0 kB; lsn=1/105465F0, redo lsn=1/105465F0
2026-07-03 14:26:59.771 +08 [3476] LOG:  database system is shut down


[postgres@pgdb02 ~]$ pg_ctl start -D /pgData/pgsql17/data
waiting for server to start....2026-07-03 14:27:18.394 +08 [3487] LOG:  redirecting log output to logging collector process
2026-07-03 14:27:18.394 +08 [3487] HINT:  Future log output will appear in directory "log".
 done
server started
[postgres@pgdb02 ~]$

2026-07-03 14:27:18.394 +08 [3487] LOG:  starting PostgreSQL 17.10 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14), 64-bit
2026-07-03 14:27:18.396 +08 [3487] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2026-07-03 14:27:18.396 +08 [3487] LOG:  listening on IPv6 address "::", port 5432
2026-07-03 14:27:18.399 +08 [3487] LOG:  listening on Unix socket "/run/postgresql/.s.PGSQL.5432"
2026-07-03 14:27:18.404 +08 [3487] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"
2026-07-03 14:27:18.413 +08 [3491] LOG:  database system was shut down at 2026-07-03 14:26:59 +08
2026-07-03 14:27:18.425 +08 [3487] LOG:  database system is ready to accept connections

Fast Shutdown (SIGINT – Default):

[postgres@pgdb02 ~]$ pg_ctl stop -D /pgData/pgsql17/data -m fast
waiting for server to shut down.... done
server stopped
[postgres@pgdb02 ~]$

2026-07-03 14:28:26.839 +08 [3487] LOG:  received fast shutdown request
2026-07-03 14:28:26.844 +08 [3487] LOG:  aborting any active transactions
2026-07-03 14:28:26.854 +08 [3487] LOG:  background worker "logical replication launcher" (PID 3494) exited with exit code 1
2026-07-03 14:28:26.860 +08 [3489] LOG:  shutting down
2026-07-03 14:28:26.862 +08 [3489] LOG:  checkpoint starting: shutdown immediate
2026-07-03 14:28:26.874 +08 [3489] LOG:  checkpoint complete: wrote 3 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.002 s, sync=0.005 s, total=0.015 s; sync files=2, longest=0.004 s, average=0.003 s; distance=0 kB, estimate=0 kB; lsn=1/105466A0, redo lsn=1/105466A0
2026-07-03 14:28:26.882 +08 [3487] LOG:  database system is shut down

[postgres@pgdb02 ~]$ pg_ctl start -D /pgData/pgsql17/data
waiting for server to start....2026-07-03 14:29:00.574 +08 [3505] LOG:  redirecting log output to logging collector process
2026-07-03 14:29:00.574 +08 [3505] HINT:  Future log output will appear in directory "log".
 done
server started
[postgres@pgdb02 ~]$

2026-07-03 14:29:00.575 +08 [3505] LOG:  starting PostgreSQL 17.10 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14), 64-bit
2026-07-03 14:29:00.579 +08 [3505] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2026-07-03 14:29:00.580 +08 [3505] LOG:  listening on IPv6 address "::", port 5432
2026-07-03 14:29:00.582 +08 [3505] LOG:  listening on Unix socket "/run/postgresql/.s.PGSQL.5432"
2026-07-03 14:29:00.587 +08 [3505] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"
2026-07-03 14:29:00.595 +08 [3509] LOG:  database system was shut down at 2026-07-03 14:28:26 +08
2026-07-03 14:29:00.607 +08 [3505] LOG:  database system is ready to accept connections

Immediate Shutdown (SIGQUIT):

[postgres@pgdb02 ~]$ pg_ctl stop -D /pgData/pgsql17/data -m immediate
waiting for server to shut down.... done
server stopped
[postgres@pgdb02 ~]$

2026-07-03 14:29:47.471 +08 [3505] LOG:  received immediate shutdown request
2026-07-03 14:29:47.485 +08 [3505] LOG:  database system is shut down


[postgres@pgdb02 ~]$ pg_ctl start -D /pgData/pgsql17/data
waiting for server to start....2026-07-03 14:30:17.965 +08 [3520] LOG:  redirecting log output to logging collector process
2026-07-03 14:30:17.965 +08 [3520] HINT:  Future log output will appear in directory "log".
. done
server started
[postgres@pgdb02 ~]$

2026-07-03 14:30:17.966 +08 [3520] LOG:  starting PostgreSQL 17.10 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14), 64-bit
2026-07-03 14:30:17.966 +08 [3520] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2026-07-03 14:30:17.966 +08 [3520] LOG:  listening on IPv6 address "::", port 5432
2026-07-03 14:30:17.970 +08 [3520] LOG:  listening on Unix socket "/run/postgresql/.s.PGSQL.5432"
2026-07-03 14:30:17.974 +08 [3520] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"
2026-07-03 14:30:17.983 +08 [3524] LOG:  database system was interrupted; last known up at 2026-07-03 14:29:00 +08
2026-07-03 14:30:19.211 +08 [3524] LOG:  database system was not properly shut down; automatic recovery in progress
2026-07-03 14:30:19.216 +08 [3524] LOG:  redo starts at 1/10546718
2026-07-03 14:30:19.216 +08 [3524] LOG:  invalid record length at 1/10546750: expected at least 24, got 0
2026-07-03 14:30:19.217 +08 [3524] LOG:  redo done at 1/10546718 system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
2026-07-03 14:30:19.221 +08 [3522] LOG:  checkpoint starting: end-of-recovery immediate wait
2026-07-03 14:30:19.231 +08 [3522] LOG:  checkpoint complete: wrote 3 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.002 s, sync=0.003 s, total=0.012 s; sync files=2, longest=0.002 s, average=0.002 s; distance=0 kB, estimate=0 kB; lsn=1/10546750, redo lsn=1/10546750
2026-07-03 14:30:19.239 +08 [3520] LOG:  database system is ready to accept connections

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/

PostgreSQL 17 DBA Free Training Roadmap

PostgreSQL DBA Free Coaching

Free PostgreSQL DBA Training in Telugu for Beginners | Live Ongoing Batch | June 15 - July 31, 2026.
తెలుగులో నేర్చుకోండి · 5 Weeks · Online · Beginner Friendly · 100% Free

DBA Training Program
// 5-week structured training · 19 sessions · hands-on labs
5
Weeks
19
Sessions
100+
Topics
∞
Future Free
Week 01 Lab Setup, Linux & PostgreSQL Fundamentals
Session 01 Lab Setup & Storage Configuration ▾
Pre-requisites
  • Install VirtualBox on Windows (Do your self)
  • Install PgAdmin4 on Windows (Do your self)
  • Configure Oracle Linux 9.6 VM (will share link)
Linux VM Setup
  • Create Oracle Linux 9.6 VM
  • VM Hardware Sizing for PostgreSQL
  • Add Multiple Virtual Disks
Storage Configuration
  • Create Partitions
  • Create Filesystems
  • Mount Filesystems
  • Configure /etc/fstab
PostgreSQL Directory Layout
  • Create Mount Points: /pgData, /pgWal, /pgArch, /pgBackup, /pgLog
Session 02 Linux Administration for DBAs ▾
  • Basic Linux Commands
  • File and Directory Management
  • User and Group Management
  • Permissions and Ownership
  • Process Management
  • Service Management using systemctl
  • Network Commands
  • Disk and Memory Monitoring
Session 03 PostgreSQL 17 Installation ▾
Overview
  • PostgreSQL Overview and Versions
Installation Methods
  • RPM Installation
  • DNF/YUM Installation
  • Source Code Installation
Database Initialization
  • initdb
  • Custom WAL Location
  • PostgreSQL Service Configuration
Validation
  • Start PostgreSQL
  • Connect using psql
  • Connect using PgAdmin4
Session 04 PostgreSQL Architecture ▾
Memory Architecture
  • Shared Buffers
  • WAL Buffers
  • Work Memory
  • Maintenance Work Memory
Process Architecture
  • Postmaster
  • Backend Processes
  • Checkpointer
  • Background Writer
  • WAL Writer
  • Autovacuum
Physical Storage Layout
  • Base Directory
  • Global Directory
  • WAL Directory
  • Tablespaces
Session 05 PostgreSQL Configuration Files ▾
  • postgresql.conf
  • postgresql.auto.conf
  • pg_hba.conf
  • pg_ident.conf
  • Reload vs Restart
Week 02 Administration & Security
Session 06 Startup, Shutdown ▾
  • PostgreSQL Startup Process
  • Smart Shutdown
  • Fast Shutdown (default)
  • Immediate Shutdown
Session 07 Database Administration ▾
  • Creating Databases
  • Creating Schemas
  • Schema Search Path
  • Roles and Users
  • Access Control
Session 08 Tablespaces Management ▾
  • pg_default
  • pg_global
  • Custom Tablespaces
  • Move Objects Between Tablespaces
  • Tablespace Monitoring
Session 09 Security ▾
  • pg_hba.conf
  • Authentication Methods
  • pg_ident.conf
  • .pgpass
  • SSL/TLS Setup
Session 10 Vacuum and Analyze ▾
  • MVCC Concepts
  • Dead Tuples
  • VACUUM
  • VACUUM FULL
  • ANALYZE
  • Autovacuum
Week 03 Backup & Recovery
Session 11 WAL Archiving ▾
  • WAL Fundamentals
  • Archive Mode
  • Archive Command
Session 12 Logical Backup and Restore ▾
  • pg_dump
  • pg_dumpall
  • Backup Formats
  • pg_restore
  • Restore using psql
Session 13 Physical Backup and Restore ▾
  • pg_basebackup
  • Full Cluster Backup
  • Database Refresh on New Server
Session 14 PITR — Point-in-Time Recovery ▾
  • PITR Concepts and Architecture
  • PITR Demonstration (hands-on)
Session 15 Full Recovery & Disaster Recovery ▾
  • Full Database Recovery
  • Disaster Recovery Scenarios
Week 04 Replication, Failover
Session 16 Streaming Replication ▾
  • Primary Configuration
  • Replica Configuration
  • Base Backup for Replica
  • Verify Replication
Session 17 Replication Administration ▾
  • Replication Slots
  • Monitoring Replication
  • Async Replication
  • Sync Replication
  • Convert from ASYNC to SYNC
Session 18 Failover and pg_rewind ▾
  • Manual Failover
  • Promote Replica
  • pg_rewind
  • Rejoin Old Primary as Replica
Week 05 Upgrades
Session 19 PostgreSQL Upgrades & Final Lab ▾
  • Minor Version Upgrade
  • Major Version Upgrades
  • pg_upgrade
  • Upgrade using pg_dump/pg_restore
  • Upgrade Validation
  • Final End-to-End Lab
Future Batch Advanced Topics — Free Training
// Topics reserved for the next advanced batch · access is free
EXPLAIN / EXPLAIN ANALYZE Index Types & Tuning Table Partitioning Performance Tuning PostgreSQL Extensions PgBouncer pgmetrics pgcollector pgbadger Repmgr Patroni Maintenance Operations Oracle → PostgreSQL (Ora2Pg) Advanced Data Migration Logical Replication Multi-Node HA Architectures

PostgreSQL DBA Free Training Roadmap · Click any session card to expand topics

PostgreSQL

PostgreSQL DBA Step by Step Learning

#PostgreSQL DBA Topics
1How to Install PostgreSQL ON Linux?
2How to Install PostgreSQL on Linux 7 using source code?
3How to START/STOP PostgreSQL ON Linux?
4How to Create Database in PostgreSQL?
5PostgreSQL User Management
6PostgreSQL pg_hba.conf Guide
7PostgreSQL Change Data Directory
8Understanding WAL Files in PostgreSQL – For Oracle DBAs
9Change PostgreSQL WAL Directory Path (pg_wal)
10Enable Archive Mode in PostgreSQL 17
11How to Disable ARCHIVELOG Mode
12PostgreSQL Tablespace Management
13PostgreSQL pg_dump and pg_restore Guide
14PostgreSQL Backup and Restore Using pg_dumpall and psql
15pg_basebackup – Backup, Restore, and Recovery
16Backup & Restore PostgreSQL DB Cluster to Another Host (No Archive Mode)
17Backup & Restore PostgreSQL DB Cluster on Same Host
18Restore PostgreSQL to New Host using pg_basebackup + WAL Archives
19PostgreSQL PITR – Point in Time Recovery
20Configure Streaming Replication in PostgreSQL
21Manual Failover in PostgreSQL Streaming Replication
22Convert Asynchronous Replication to Synchronous Replication

 

Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/

Change Data Directory

PostgreSQL 17 – Change Data Directory


0. Overview
1. Stop PostgreSQL Service
2. Create the New Directory
3. Copy Existing Data to New Location
4. Move existing data directory
5. Update PostgreSQL Configuration to Use New Data Directory
6. Reload systemd and start PostgreSQL
7. Edit postgres .bash_profile
8. Verify New Data Directory is in Use
9. Remove Old Data Directory (Optional – A week after)


0. Overview

To change the PostgreSQL 17 data directory from its default location

From: /var/lib/pgsql/17/data To: /pgData/pgsql17/data

 

1. Stop PostgreSQL Service

[postgres@lxicbpgdsgv01 ~]$ psql -c "SHOW data_directory;"
     data_directory
------------------------
 /var/lib/pgsql/17/data <---- existing data directory
(1 row)

[postgres@lxicbpgdsgv01 ~]$

[root@lxicbpgdsgv01 ~]# systemctl stop postgresql-17
[root@lxicbpgdsgv01 ~]# 
[root@lxicbpgdsgv01 ~]# ps -ef | grep postgres
root        5759    4079  0 18:09 pts/1    00:00:00 grep --color=auto postgres
[root@lxicbpgdsgv01 ~]#

2. Create the New Directory

[root@lxicbpgdsgv01 ~]# mkdir -p /pgData/pgsql17/data
[root@lxicbpgdsgv01 ~]# chown postgres:postgres /pgData/pgsql17/data
[root@lxicbpgdsgv01 ~]# chmod 700 /pgData/pgsql17/data
[root@lxicbpgdsgv01 ~]#

3. Copy Existing Data to New Location

[postgres@lxicbpgdsgv01 ~]$ nohup rsync -av --progress /var/lib/pgsql/17/data/ /pgData/pgsql17/data/ > rsync_pgdata.log 2>&1 &
[1] 5863
[postgres@lxicbpgdsgv01 ~]$

[postgres@lxicbpgdsgv01 ~]$ tail -f rsync_pgdata.log
pg_wal/000000010000000000000002
     16,777,216 100%   17.68MB/s    0:00:00 (xfr#1621, to-chk=3/1652)
pg_wal/archive_status/
pg_wal/summaries/
pg_xact/
pg_xact/0000
          8,192 100%    8.84kB/s    0:00:00 (xfr#1622, to-chk=0/1652)

sent 73,827,441 bytes  received 31,005 bytes  49,238,964.00 bytes/sec
total size is 73,717,583  speedup is 1.00
^C[1]+  Done                    nohup rsync -av --progress /var/lib/pgsql/17/data/ /pgData/pgsql17/data/ > rsync_pgdata.log 2>&1

[postgres@lxicbpgdsgv01 ~]$

4. Move existing data directory

[postgres@lxicbpgdsgv01 ~]$ mv /var/lib/pgsql/17/data /var/lib/pgsql/17/data_bkp_10072025
[postgres@lxicbpgdsgv01 ~]$

5. Update PostgreSQL Configuration to Use New Data Directory

[postgres@lxicbpgdsgv01 ~]$ cat /usr/lib/systemd/system/postgresql-17.service | grep -i "Environment=PGDATA"
Environment=PGDATA=/var/lib/pgsql/17/data/  <-----
[postgres@lxicbpgdsgv01 ~]$

[root@lxicbpgdsgv01 ~]# ls -ltr /usr/lib/systemd/system/postgresql-17.service
-rw-r--r--. 1 root root 1788 Aug 13 17:39 /usr/lib/systemd/system/postgresql-17.service
[root@lxicbpgdsgv01 ~]#

[root@lxicbpgdsgv01 ~]# sed -i 's|^Environment=PGDATA=.*|Environment=PGDATA=/pgData/pgsql17/data/|' /usr/lib/systemd/system/postgresql-17.service
[root@lxicbpgdsgv01 ~]#
[root@lxicbpgdsgv01 ~]# cat /usr/lib/systemd/system/postgresql-17.service | grep -i "Environment=PGDATA"
Environment=PGDATA=/pgData/pgsql17/data/ <------
[root@lxicbpgdsgv01 ~]#

6. Reload systemd and start PostgreSQL

[root@lxicbpgdsgv01 ~]# systemctl daemon-reload
[root@lxicbpgdsgv01 ~]#

[root@lxicbpgdsgv01 ~]# systemctl start postgresql-17
[root@lxicbpgdsgv01 ~]#

[root@lxicbpgdsgv01 ~]# systemctl cat postgresql-17 | grep -i Environment=PGDATA
Environment=PGDATA=/pgData/pgsql17/data/ <-----
[root@lxicbpgdsgv01 ~]#

[root@lxicbpgdsgv01 ~]# systemctl status postgresql-17
● postgresql-17.service - PostgreSQL 17 database server
     Loaded: loaded (/usr/lib/systemd/system/postgresql-17.service; enabled; preset: disabled)
     Active: active (running) since Tue 2025-10-07 18:33:05 +08; 2min 26s ago
       Docs: https://www.postgresql.org/docs/17/static/
    Process: 5911 ExecStartPre=/usr/pgsql-17/bin/postgresql-17-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
   Main PID: 5916 (postgres)
      Tasks: 7 (limit: 15700)
     Memory: 21.3M
        CPU: 177ms
     CGroup: /system.slice/postgresql-17.service
             ├─5916 /usr/pgsql-17/bin/postgres -D /pgData/pgsql17/data/
             ├─5917 "postgres: logger "
             ├─5918 "postgres: checkpointer "
             ├─5919 "postgres: background writer "
             ├─5921 "postgres: walwriter "
             ├─5922 "postgres: autovacuum launcher "
             └─5923 "postgres: logical replication launcher "

Oct 07 18:33:05 lxicbpgdsgv01.rajasekhar.com systemd[1]: Starting PostgreSQL 17 database server...
Oct 07 18:33:05 lxicbpgdsgv01.rajasekhar.com postgres[5916]: 2025-10-07 18:33:05.392 +08 [5916] LOG:  redirecting log output to logging collector process
Oct 07 18:33:05 lxicbpgdsgv01.rajasekhar.com postgres[5916]: 2025-10-07 18:33:05.392 +08 [5916] HINT:  Future log output will appear in directory "log".
Oct 07 18:33:05 lxicbpgdsgv01.rajasekhar.com systemd[1]: Started PostgreSQL 17 database server.

7. Edit postgres .bash_profile

[postgres@lxicbpgdsgv01 ~]$ cat .bash_profile | grep -i PGDATA=
PGDATA=/var/lib/pgsql/17/data
[postgres@lxicbpgdsgv01 ~]$

[postgres@lxicbpgdsgv01 ~]$ sed -i 's|^PGDATA=.*|PGDATA=/pgData/pgsql17/data|' ~/.bash_profile
[postgres@lxicbpgdsgv01 ~]$
[postgres@lxicbpgdsgv01 ~]$ cat .bash_profile | grep -i PGDATA=
PGDATA=/pgData/pgsql17/data <-----
[postgres@lxicbpgdsgv01 ~]$ . .bash_profile <-- reload the profile
[postgres@lxicbpgdsgv01 ~]$

8. Verify New Data Directory is in Use

[postgres@lxicbpgdsgv01 ~]$ psql -c "SHOW data_directory;"
    data_directory
----------------------
 /pgData/pgsql17/data  <---- new data directory
(1 row)

[postgres@lxicbpgdsgv01 ~]$

9. Remove Old Data Directory (Optional – A week after)

[postgres@lxicbpgdsgv01 ~]$ ls -ld /var/lib/pgsql/17/data_bkp_10072025
drwx------. 20 postgres postgres 4096 Oct  7 18:20 /var/lib/pgsql/17/data_bkp_10072025
[postgres@lxicbpgdsgv01 ~]$ rm -rf /var/lib/pgsql/17/data_bkp_10072025
[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/

PostgreSQL Tablespace

PostgreSQL Tablespace Management

Table of Contents

___________________________________________________________________________________________________

0. Introduction
1. How big can a PostgreSQL tablespace grow?
2. Create a Tablespace
3. Create a Database with default tablespace
4. Create Tables on Data tablspace
5. Crate Index on Index tablespaces
6. Create a Dedicated Tablespace for Temp
7. Assign Temp Tablespace to a Database
8. Check Tablespace OIDs
9. Move Table Between Tablespaces
10. Verify Database Directory (Filesystem)
11. Rename tablespace
12. Drop Tablespace
13. Change ownership for Tablespace
14. Verify Symbolic Links
15. How to find tablespace size
___________________________________________________________________________________________________


0. Introduction

What is a Tablespace?

--> Tablespace is a logical structure in which objects are stored.

--> A tablespace is simply a physical storage location on disk where PostgreSQL stores database objects (tables, indexes, etc.). 

--> By default database objects are stored in the current default tablespace of the database.

List tablespaces:

\db+ or select * from pg_tablespace;

 

Types of Tablespaces: Default Tablespaces and Non-Default Tablespaces
I. Default Tablespaces

These come built-in with PostgreSQL:

pg_global – for shared system data
pg_default – for regular user data (tables, indexes, etc.)
Featurepg_defaultpg_global
PurposeStores regular user dataStores global system data
Used by default?Yes, for tables/indexesNo, only used internally
Can store user tables?YesNo
Location$PGDATA/base/$PGDATA/global/
Droppable?NoNo
II. Non-Default Tablespaces :

--> These are created by users to store data in custom locations.
--> Useful for managing storage better (e.g., putting large tables on faster disks).


1. How big can a PostgreSQL tablespace grow?

There is no fixed maximum size for a tablespace in PostgreSQL itself.
The limit depends on your operating system and filesystem where the tablespace directory is located.

For example:

On modern filesystems like ext4 or XFS, a single file can be several terabytes (TB) or even petabytes (PB).
PostgreSQL breaks large tables into multiple files (each typically up to 1 GB) internally, so extremely large tablespaces are supported.
Your disk/storage capacity is the main practical limit.


2. Create a Tablespace

[root@pg17 ~]# mkdir -p /pgData/pgsql15/edbua_tbs01
[root@pg17 ~]# mkdir -p /pgData/pgsql15/edbua_tbs02
[root@pg17 ~]# mkdir -p /pgData/pgsql15/edbua_ixtbs01
[root@pg17 ~]# mkdir -p /pgData/pgsql15/edbua_temptbs01
[root@pg17 ~]# chown postgres:postgres /pgData/pgsql15/edbua*
[root@pg17 ~]# chmod 700 /pgData/pgsql15/edbua*
[root@pg17 ~]#

[root@pg17 ~]# su - postgres
[postgres@pg17 ~]$ psql
psql (15.13)
Type "help" for help.

postgres=# CREATE TABLESPACE edbua_tbs01 LOCATION '/pgData/pgsql15/edbua_tbs01';
CREATE TABLESPACE
postgres=# CREATE TABLESPACE edbua_tbs02 LOCATION '/pgData/pgsql15/edbua_tbs02';
CREATE TABLESPACE
postgres=# CREATE TABLESPACE edbua_ixtbs01 LOCATION '/pgData/pgsql15/edbua_ixtbs01';
CREATE TABLESPACE
postgres=# 


3. Create a Database with default tablespace

postgres=# CREATE DATABASE edbua TABLESPACE edbua_tbs01;
CREATE DATABASE
postgres=# 
postgres=# SELECT oid, datname, dattablespace, datistemplate, datdba FROM pg_database WHERE datname = 'edbua';
  oid  | datname | dattablespace | datistemplate | datdba
-------+---------+---------------+---------------+--------
 92666 | edbua   |         92662 | f             |     10
(1 row)

postgres=#


4. Create Tables on Data tablspace

postgres=# \c edbua
You are now connected to database "edbua" as user "postgres".
edbua=# CREATE TABLE emp01 (id int, name text) TABLESPACE edbua_tbs01;
CREATE TABLE
edbua=# CREATE TABLE emp02 (id int, name text) TABLESPACE edbua_tbs01;
CREATE TABLE
edbua=# CREATE TABLE emp03 (id int, name text) TABLESPACE edbua_tbs01;
CREATE TABLE
edbua=#


5. Create Index on Index tablespaces

edbua=# CREATE INDEX emp01_ix ON emp01(name) TABLESPACE edbua_ixtbs01;
CREATE INDEX
edbua=# CREATE INDEX emp02_ix ON emp02(name) TABLESPACE edbua_ixtbs01;
CREATE INDEX
edbua=# CREATE INDEX emp03_ix ON emp03(name) TABLESPACE edbua_ixtbs01;
CREATE INDEX
edbua=# SELECT c.oid, c.relname, c.relkind, t.spcname AS tablespace, c.relfilenode
edbua-# FROM pg_class c
edbua-# LEFT JOIN pg_tablespace t ON c.reltablespace = t.oid
edbua-# WHERE c.relname IN ('emp01','emp02','emp03','emp01_ix','emp02_ix','emp03_ix');
  oid  | relname  | relkind |  tablespace   | relfilenode
-------+----------+---------+---------------+-------------
 92685 | emp01    | r       |               |       92685
 92690 | emp02    | r       |               |       92690
 92695 | emp03    | r       |               |       92695
 92700 | emp01_ix | i       | edbua_ixtbs01 |       92700
 92701 | emp02_ix | i       | edbua_ixtbs01 |       92701
 92702 | emp03_ix | i       | edbua_ixtbs01 |       92702
(6 rows)

edbua=#

If a table or index is created in the database’s default tablespace, PostgreSQL does not explicitly record the tablespace in pg_class.reltablespace.

But when it’s created in a non-default tablespace, PostgreSQL records that tablespace OID in pg_class.reltablespace.

If you want to always see the tablespace name (even when it's the default)

edbua=# SELECT
edbua-#     c.oid,
edbua-#     c.relname,
edbua-#     c.relkind,
edbua-#     COALESCE(t1.spcname, t2.spcname) AS tablespace,
edbua-#     c.relfilenode
edbua-# FROM pg_class c
edbua-# LEFT JOIN pg_tablespace t1 ON c.reltablespace = t1.oid
edbua-# LEFT JOIN pg_database d ON d.datname = current_database()
edbua-# LEFT JOIN pg_tablespace t2 ON d.dattablespace = t2.oid
edbua-# WHERE c.relname IN ('emp01','emp02','emp03','emp01_ix','emp02_ix','emp03_ix');
  oid  | relname  | relkind |  tablespace   | relfilenode
-------+----------+---------+---------------+-------------
 92685 | emp01    | r       | edbua_tbs01   |       92685
 92690 | emp02    | r       | edbua_tbs01   |       92690
 92695 | emp03    | r       | edbua_tbs01   |       92695
 92700 | emp01_ix | i       | edbua_ixtbs01 |       92700
 92701 | emp02_ix | i       | edbua_ixtbs01 |       92701
 92702 | emp03_ix | i       | edbua_ixtbs01 |       92702
(6 rows)

edbua=#

pg_tblspc/<tbs_oid>/PG_15_<catalog_version>/<db_oid>/<relfilenode>

edbua=# SELECT relname, pg_relation_filepath(oid) AS filepath
edbua-# FROM pg_class
edbua-# WHERE relname IN ('emp01','emp02','emp03','emp01_ix','emp02_ix','emp03_ix');
 relname  |                  filepath
----------+---------------------------------------------
 emp01    | pg_tblspc/92662/PG_15_202209061/92666/92685
 emp02    | pg_tblspc/92662/PG_15_202209061/92666/92690
 emp03    | pg_tblspc/92662/PG_15_202209061/92666/92695
 emp01_ix | pg_tblspc/92664/PG_15_202209061/92666/92700
 emp02_ix | pg_tblspc/92664/PG_15_202209061/92666/92701
 emp03_ix | pg_tblspc/92664/PG_15_202209061/92666/92702
(6 rows)

edbua=#


6. Create a Dedicated Tablespace for Temp

postgres=# CREATE TABLESPACE edbua_temptbs01 LOCATION '/pgData/pgsql15/edbua_temptbs01';
CREATE TABLESPACE
postgres=#


7. Assign Temp Tablespace to a Database

postgres=# ALTER DATABASE edbua SET temp_tablespaces = edbua_temptbs01;
ALTER DATABASE
postgres=#

postgres=# \c edbua
You are now connected to database "edbua" as user "postgres".
edbua=# 

edbua=# CREATE TEMP TABLE emp_tmp (id int, name text);
CREATE TABLE
edbua=#
edbua=# SELECT c.oid, c.relname, t.spcname, pg_relation_filepath(c.oid)
edbua-# FROM pg_class c
edbua-# LEFT JOIN pg_tablespace t ON c.reltablespace = t.oid
edbua-# WHERE c.relname = 'emp_tmp';
  oid  | relname |     spcname     |              pg_relation_filepath
-------+---------+-----------------+------------------------------------------------
 92705 | emp_tmp | edbua_temptbs01 | pg_tblspc/92665/PG_15_202209061/92666/t4_92705
(1 row)

edbua=#

pg_tblspc/<tbs_oid>/PG_15_<catalog_version>/<db_oid>/<relfilenode>


8. Check Tablespace OIDs

edbua=# SELECT oid, spcname AS tablespace_name, pg_tablespace_location(oid) AS location FROM pg_tablespace ORDER BY oid;
  oid  | tablespace_name |            location
-------+-----------------+---------------------------------
  1663 | pg_default      |
  1664 | pg_global       |
 24581 | dell_ts_data    | /u01/data/dell_ts_data
 24582 | dell_ts_index   | /u01/data/dell_ts_index
 24594 | dell_tbs        | /pgData/pgsql15/data
 24597 | trd_tb01        | /pgData/pgsql15/data/trd_tb01
 24598 | trd_ix01        | /pgData/pgsql15/data/trd_ix01
 32787 | geb_tb          | /pgData/pgsql15/pgtb/geb_tb
 32788 | geb_ix          | /pgData/pgsql15/pgix/geb_ix
 92662 | edbua_tbs01     | /pgData/pgsql15/edbua_tbs01
 92663 | edbua_tbs02     | /pgData/pgsql15/edbua_tbs02
 92664 | edbua_ixtbs01   | /pgData/pgsql15/edbua_ixtbs01
 92665 | edbua_temptbs01 | /pgData/pgsql15/edbua_temptbs01
(13 rows)

edbua=#


9. Move Table Between Tablespaces

edbua=# ALTER TABLE emp01 SET TABLESPACE edbua_tbs02;
ALTER TABLE
edbua=# ALTER TABLE emp02 SET TABLESPACE edbua_tbs02;
ALTER TABLE
edbua=# ALTER TABLE emp03 SET TABLESPACE edbua_tbs02;
ALTER TABLE
edbua=#

pg_tblspc/<tbs_oid>/PG_15_<catalog_version>/<db_oid>/<relfilenode>

edbua=# select pg_relation_filepath('emp01');
            pg_relation_filepath
---------------------------------------------
 pg_tblspc/92663/PG_15_202209061/92666/92710
(1 row)

edbua=# 

edbua=# SELECT relname, pg_relation_filepath(oid) AS filepath
FROM pg_class
WHERE relname IN ('emp01','emp02','emp03','emp01_ix','emp02_ix','emp03_ix');
 relname  |                  filepath
----------+---------------------------------------------
 emp01    | pg_tblspc/92663/PG_15_202209061/92666/92710
 emp02    | pg_tblspc/92663/PG_15_202209061/92666/92713
 emp03    | pg_tblspc/92663/PG_15_202209061/92666/92716
 emp01_ix | pg_tblspc/92664/PG_15_202209061/92666/92700
 emp02_ix | pg_tblspc/92664/PG_15_202209061/92666/92701
 emp03_ix | pg_tblspc/92664/PG_15_202209061/92666/92702
(6 rows)

edbua=#

Important: Indexes do not move automatically when you move a table.
They remain in their original tablespace (edbua_ixtbs01 in this case).
If you want indexes in edbua_tbs02, you must explicitly run:

edbua=# ALTER INDEX emp01_ix SET TABLESPACE edbua_tbs02;
ALTER INDEX
edbua=# ALTER INDEX emp02_ix SET TABLESPACE edbua_tbs02;
ALTER INDEX
edbua=# ALTER INDEX emp03_ix SET TABLESPACE edbua_tbs02;
ALTER INDEX
edbua=#
edbua=# SELECT c.relname, t.spcname AS tablespace, pg_relation_filepath(c.oid)
edbua-# FROM pg_class c
edbua-# LEFT JOIN pg_tablespace t ON c.reltablespace = t.oid
edbua-# WHERE c.relname IN ('emp01','emp02','emp03','emp01_ix','emp02_ix','emp03_ix');
 relname  | tablespace  |            pg_relation_filepath
----------+-------------+---------------------------------------------
 emp01_ix | edbua_tbs02 | pg_tblspc/92663/PG_15_202209061/92666/92719
 emp02_ix | edbua_tbs02 | pg_tblspc/92663/PG_15_202209061/92666/92720
 emp03_ix | edbua_tbs02 | pg_tblspc/92663/PG_15_202209061/92666/92721
 emp01    | edbua_tbs02 | pg_tblspc/92663/PG_15_202209061/92666/92710
 emp02    | edbua_tbs02 | pg_tblspc/92663/PG_15_202209061/92666/92713
 emp03    | edbua_tbs02 | pg_tblspc/92663/PG_15_202209061/92666/92716
(6 rows)

edbua=#

edbua=# SELECT
edbua-#     c.relname,
edbua-#     COALESCE(t1.spcname, t2.spcname) AS tablespace,
edbua-#     pg_catalog.pg_tablespace_location(COALESCE(c.reltablespace, d.dattablespace)) AS tablespace_path,
edbua-#     current_setting('data_directory') AS data_directory,
edbua-#     CASE
edbua-#         WHEN c.reltablespace = 0 THEN
edbua-#             current_setting('data_directory') || '/base/' || d.oid || '/' || c.relfilenode
edbua-#         ELSE
edbua-#             pg_catalog.pg_tablespace_location(c.reltablespace) || '/PG_' ||
edbua-#             split_part(split_part(version(), ' ', 2), '.', 1) || '_' ||
edbua-#             '202209061/' || d.oid || '/' || c.relfilenode
edbua-#     END AS full_path_guess
edbua-# FROM pg_class c
edbua-# LEFT JOIN pg_tablespace t1 ON c.reltablespace = t1.oid
edbua-# LEFT JOIN pg_database d ON d.datname = current_database()
edbua-# LEFT JOIN pg_tablespace t2 ON d.dattablespace = t2.oid
edbua-# WHERE c.relname IN ('emp01','emp02','emp03','emp01_ix','emp02_ix','emp03_ix');
 relname  | tablespace  |       tablespace_path       |    data_directory    |                     full_path_guess
----------+-------------+-----------------------------+----------------------+---------------------------------------------------------
 emp01_ix | edbua_tbs02 | /pgData/pgsql15/edbua_tbs02 | /pgData/pgsql15/data | /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92719
 emp02_ix | edbua_tbs02 | /pgData/pgsql15/edbua_tbs02 | /pgData/pgsql15/data | /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92720
 emp03_ix | edbua_tbs02 | /pgData/pgsql15/edbua_tbs02 | /pgData/pgsql15/data | /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92721
 emp01    | edbua_tbs02 | /pgData/pgsql15/edbua_tbs02 | /pgData/pgsql15/data | /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92710
 emp02    | edbua_tbs02 | /pgData/pgsql15/edbua_tbs02 | /pgData/pgsql15/data | /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92713
 emp03    | edbua_tbs02 | /pgData/pgsql15/edbua_tbs02 | /pgData/pgsql15/data | /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92716
(6 rows)

edbua=#


10. Verify Database Directory (Filesystem)

edbua=# SELECT oid, datname FROM pg_database WHERE datname = current_database();
  oid  | datname
-------+---------
 92666 | edbua
(1 row)

edbua=#

[root@pg17 ~]# ls -ltr /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/*
-rw-------. 1 postgres postgres    0 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92710
-rw-------. 1 postgres postgres    0 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92711
-rw-------. 1 postgres postgres 8192 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92712
-rw-------. 1 postgres postgres    0 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92713
-rw-------. 1 postgres postgres    0 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92714
-rw-------. 1 postgres postgres 8192 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92715
-rw-------. 1 postgres postgres    0 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92716
-rw-------. 1 postgres postgres    0 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92717
-rw-------. 1 postgres postgres 8192 Sep 23 00:14 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92718
-rw-------. 1 postgres postgres 8192 Sep 23 00:52 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92719
-rw-------. 1 postgres postgres 8192 Sep 23 00:52 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92720
-rw-------. 1 postgres postgres 8192 Sep 23 00:52 /pgData/pgsql15/edbua_tbs02/PG_15_202209061/92666/92721
[root@pg17 ~]#


Issue: Why are there 12 files when I have only 6 objects?

created only: 3 tables: emp01, emp02, emp03 & 3 indexes: emp01_ix, emp02_ix, emp03_ix

But in the tablespace directory, we're seeing 12 files: 3 tables + 3 indexes + 3 TOAST tables + 3 TOAST indexes

edbua=# SELECT
edbua-#     c.relname,
edbua-#     c.relfilenode,
edbua-#     c.relkind,
edbua-#     n.nspname AS schema,
edbua-#     t.spcname AS tablespace
edbua-# FROM
edbua-#     pg_class c
edbua-# JOIN
edbua-#     pg_namespace n ON n.oid = c.relnamespace
edbua-# LEFT JOIN
edbua-#     pg_tablespace t ON c.reltablespace = t.oid
edbua-# WHERE
edbua-#     c.reltablespace = (SELECT oid FROM pg_tablespace WHERE spcname = 'edbua_tbs02')
edbua-# ORDER BY
edbua-#     c.relfilenode;
       relname        | relfilenode | relkind |  schema  | tablespace
----------------------+-------------+---------+----------+-------------
 emp01                |       92710 | r       | public   | edbua_tbs02
 pg_toast_92685       |       92711 | t       | pg_toast | edbua_tbs02
 pg_toast_92685_index |       92712 | i       | pg_toast | edbua_tbs02
 emp02                |       92713 | r       | public   | edbua_tbs02
 pg_toast_92690       |       92714 | t       | pg_toast | edbua_tbs02
 pg_toast_92690_index |       92715 | i       | pg_toast | edbua_tbs02
 emp03                |       92716 | r       | public   | edbua_tbs02
 pg_toast_92695       |       92717 | t       | pg_toast | edbua_tbs02
 pg_toast_92695_index |       92718 | i       | pg_toast | edbua_tbs02
 emp01_ix             |       92719 | i       | public   | edbua_tbs02
 emp02_ix             |       92720 | i       | public   | edbua_tbs02
 emp03_ix             |       92721 | i       | public   | edbua_tbs02
(12 rows)

edbua=#


11. Rename tablespace

edbua=# ALTER TABLESPACE edbua_tbs02 RENAME TO edbua_tbs03;
ALTER TABLESPACE
edbua=#

edbua=# \db+ edbua*
                                                List of tablespaces
      Name       |  Owner   |            Location             | Access privileges | Options |  Size   | Description
-----------------+----------+---------------------------------+-------------------+---------+---------+-------------
 edbua_ixtbs01   | postgres | /pgData/pgsql15/edbua_ixtbs01   |                   |         | 6 bytes |
 edbua_tbs01     | postgres | /pgData/pgsql15/edbua_tbs01     |                   |         | 7789 kB |
 edbua_tbs03     | postgres | /pgData/pgsql15/edbua_tbs02     |                   |         | 48 kB   |
 edbua_temptbs01 | postgres | /pgData/pgsql15/edbua_temptbs01 |                   |         | 6 bytes |
(4 rows)

edbua=#


12. Drop Tablespace

edbua=# drop tablespace edbua_tbs03;
ERROR:  tablespace "edbua_tbs03" is not empty
edbua=#

edbua=# SELECT
edbua-#   CASE
edbua-#     WHEN c.relkind = 'r' THEN
edbua-#       format('ALTER TABLE %I.%I SET TABLESPACE edbua_tbs01;', n.nspname, c.relname)
edbua-#     WHEN c.relkind = 'i' THEN
edbua-#       format('ALTER INDEX %I.%I SET TABLESPACE edbua_tbs01;', n.nspname, c.relname)
edbua-#     WHEN c.relkind = 'S' THEN
edbua-#       format('ALTER SEQUENCE %I.%I SET TABLESPACE edbua_tbs01;', n.nspname, c.relname)
edbua-#   END AS alter_statement
edbua-# FROM pg_class c
edbua-# JOIN pg_namespace n ON n.oid = c.relnamespace
edbua-# WHERE c.reltablespace = (SELECT oid FROM pg_tablespace WHERE spcname = 'edbua_tbs03')
edbua-#   AND c.relkind IN ('r', 'i', 'S')
edbua-#   AND n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')
edbua-# ORDER BY n.nspname, c.relname;
                     alter_statement
---------------------------------------------------------
 ALTER TABLE public.emp01 SET TABLESPACE edbua_tbs01;
 ALTER INDEX public.emp01_ix SET TABLESPACE edbua_tbs01;
 ALTER TABLE public.emp02 SET TABLESPACE edbua_tbs01;
 ALTER INDEX public.emp02_ix SET TABLESPACE edbua_tbs01;
 ALTER TABLE public.emp03 SET TABLESPACE edbua_tbs01;
 ALTER INDEX public.emp03_ix SET TABLESPACE edbua_tbs01;
(6 rows)

edbua=#

edbua=# ALTER TABLE public.emp01 SET TABLESPACE edbua_tbs01;
 ALTER INDEX public.emp02_ix SET TABLESPACE edbua_tbs01;
 ALTER TABLE public.emp03 SET TABLESPACE edbua_tbs01;
 ALTER INDEX public.emp03_ix SET TABLESPACE edbua_tbs01;ALTER TABLE
edbua=#  ALTER INDEX public.emp01_ix SET TABLESPACE edbua_tbs01;
ALTER INDEX
edbua=#  ALTER TABLE public.emp02 SET TABLESPACE edbua_tbs01;
ALTER TABLE
edbua=#  ALTER INDEX public.emp02_ix SET TABLESPACE edbua_tbs01;
ALTER INDEX
edbua=#  ALTER TABLE public.emp03 SET TABLESPACE edbua_tbs01;
ALTER TABLE
edbua=#  ALTER INDEX public.emp03_ix SET TABLESPACE edbua_tbs01;
ALTER INDEX
edbua=#

edbua=# drop tablespace edbua_tbs03;
DROP TABLESPACE
edbua=# \db+ edbua*
                                                List of tablespaces
      Name       |  Owner   |            Location             | Access privileges | Options |  Size   | Description
-----------------+----------+---------------------------------+-------------------+---------+---------+-------------
 edbua_ixtbs01   | postgres | /pgData/pgsql15/edbua_ixtbs01   |                   |         | 6 bytes |
 edbua_tbs01     | postgres | /pgData/pgsql15/edbua_tbs01     |                   |         | 7837 kB |
 edbua_temptbs01 | postgres | /pgData/pgsql15/edbua_temptbs01 |                   |         | 6 bytes |
(3 rows)

edbua=#


13. Change ownership for Tablespace

edbua=# ALTER TABLESPACE edbua_tbs01 OWNER TO teja;
ALTER TABLESPACE
edbua=# \db+ edbua*
                                                List of tablespaces
      Name       |  Owner   |            Location             | Access privileges | Options |  Size   | Description
-----------------+----------+---------------------------------+-------------------+---------+---------+-------------
 edbua_ixtbs01   | postgres | /pgData/pgsql15/edbua_ixtbs01   |                   |         | 6 bytes |
 edbua_tbs01     | teja     | /pgData/pgsql15/edbua_tbs01     |                   |         | 7837 kB |
 edbua_temptbs01 | postgres | /pgData/pgsql15/edbua_temptbs01 |                   |         | 6 bytes |
(3 rows)

edbua=# ALTER TABLESPACE edbua_tbs01 OWNER TO postgres;
ALTER TABLESPACE
edbua=# \db+ edbua*
                                                List of tablespaces
      Name       |  Owner   |            Location             | Access privileges | Options |  Size   | Description
-----------------+----------+---------------------------------+-------------------+---------+---------+-------------
 edbua_ixtbs01   | postgres | /pgData/pgsql15/edbua_ixtbs01   |                   |         | 6 bytes |
 edbua_tbs01     | postgres | /pgData/pgsql15/edbua_tbs01     |                   |         | 7837 kB |
 edbua_temptbs01 | postgres | /pgData/pgsql15/edbua_temptbs01 |                   |         | 6 bytes |
(3 rows)

edbua=#


14. Verify Symbolic Links

edbua=# show data_directory;
    data_directory
----------------------
 /pgData/pgsql15/data  <----
(1 row)

edbua=#

[root@pg17 pg_tblspc]# ls -ltr /pgData/pgsql15/data/pg_tblspc/*
lrwxrwxrwx. 1 postgres postgres 22 Aug 18 06:36 /pgData/pgsql15/data/pg_tblspc/24581 -> /u01/data/dell_ts_data
lrwxrwxrwx. 1 postgres postgres 23 Aug 18 06:36 /pgData/pgsql15/data/pg_tblspc/24582 -> /u01/data/dell_ts_index
lrwxrwxrwx. 1 postgres postgres 20 Sep  3 09:52 /pgData/pgsql15/data/pg_tblspc/24594 -> /pgData/pgsql15/data
lrwxrwxrwx. 1 postgres postgres 29 Sep  3 11:55 /pgData/pgsql15/data/pg_tblspc/24597 -> /pgData/pgsql15/data/trd_tb01
lrwxrwxrwx. 1 postgres postgres 29 Sep  3 11:55 /pgData/pgsql15/data/pg_tblspc/24598 -> /pgData/pgsql15/data/trd_ix01
lrwxrwxrwx. 1 postgres postgres 27 Sep  4 05:59 /pgData/pgsql15/data/pg_tblspc/32787 -> /pgData/pgsql15/pgtb/geb_tb
lrwxrwxrwx. 1 postgres postgres 27 Sep  4 06:03 /pgData/pgsql15/data/pg_tblspc/32788 -> /pgData/pgsql15/pgix/geb_ix
lrwxrwxrwx. 1 postgres postgres 27 Sep 20 07:20 /pgData/pgsql15/data/pg_tblspc/92662 -> /pgData/pgsql15/edbua_tbs01
lrwxrwxrwx. 1 postgres postgres 29 Sep 20 07:21 /pgData/pgsql15/data/pg_tblspc/92664 -> /pgData/pgsql15/edbua_ixtbs01
lrwxrwxrwx. 1 postgres postgres 31 Sep 20 07:21 /pgData/pgsql15/data/pg_tblspc/92665 -> /pgData/pgsql15/edbua_temptbs01
[root@pg17 pg_tblspc]#


15. How to find tablespace size

edbua=# SELECT spcname, pg_size_pretty(pg_tablespace_size(spcname)) AS size FROM pg_tablespace;
     spcname     |  size
-----------------+---------
 pg_default      | 1660 MB
 pg_global       | 571 kB
 dell_ts_data    | 7761 kB
 dell_ts_index   | 16 kB
 dell_tbs        | 0 bytes
 trd_tb01        | 1615 MB
 trd_ix01        | 2648 kB
 geb_tb          | 13 GB
 geb_ix          | 8950 MB
 edbua_ixtbs01   | 6 bytes
 edbua_temptbs01 | 6 bytes
 edbua_tbs01     | 7837 kB
(12 rows)

edbua=#
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

PostgreSQL pg_hba.conf

Host-Based Authentication (pg_hba.conf)

Table of Contents


1. What is pg_hba.conf?
2. Authentication Methods in pg_hba.conf
3. Create Database and User
4. Contents of pg_hba.conf
5. Play with peer
6. Play with trust
7. Play with reject
8. Restrict by User
9. Restrict by Network
10. Verify logs



1. What is pg_hba.conf?

HBA = Host-Based Authentication.
Location: usually inside PostgreSQL data directory (/var/lib/pgsql/<version>/data/pg_hba.conf or /pgData/pgsql15/data/pg_hba.conf depending on your setup).



Format:

# TYPE  DATABASE  USER  ADDRESS        METHOD
TYPE: local, host, hostssl, hostnossl
DATABASE: which DBs (e.g. all, postgres, mydb)
USER: which roles (e.g. all, myuser)
ADDRESS: client IP/CIDR (127.0.0.1/32, 192.168.2.0/24)
METHOD: authentication method (trust, md5, scram-sha-256, peer, reject, cert)

OPTIONS	: optional settings (e.g., clientcert=1)


2. Authentication Methods in pg_hba.conf

trust:
--- > No password needed. Anyone who can connect to the server is trusted.
--- > Use: testing only.
--- > Not secure in production.

Example:

host    all    all    127.0.0.1/32    trust

md5
--- > Password required, stored as MD5 hash.
--- > Legacy support. Weaker security, avoid if possible.

Example:

host    all    all    127.0.0.1/32    md5

scram-sha-256
--- > Password required, stored as salted SCRAM-SHA-256 hash.
--- > Stronger than MD5.
--- > Recommended method for production.

Example:

host    all    all    127.0.0.1/32    scram-sha-256


peer
--- > Works for local connections only.
--- > The OS user name must match the PostgreSQL role.
--- > Good for local scripts/services under same user.

Example:

local   all    all                     peer


reject
--- > Explicitly denies access.
----> Useful for blocking unwanted connections (like a firewall rule).

Example:

host    all    all    0.0.0.0/0       reject


cert
---> Requires SSL/TLS client certificate.
---> PostgreSQL role must match certificate username.
---> Very secure for enterprise / production with SSL.

Example:
hostssl all   all    192.168.1.0/24   cert clientcert=1


3. Create Database and User

postgres=# CREATE USER teja WITH PASSWORD 'teja123';
CREATE ROLE
postgres=# CREATE DATABASE orcl OWNER teja;
CREATE DATABASE
postgres=#


4. Contents of pg_hba.conf

[postgres@pg17 ~]$ cat /pgData/pgsql15/data/pg_hba.conf
# TYPE  DATABASE         USER            ADDRESS                METHOD

# Allow local peer access for postgres
local   all             postgres                                peer

# Allow local password access for all
local   all             all                                     scram-sha-256

[postgres@pg17 ~]$


5. Play with peer

Works for local connections only. The OS user name must match the PostgreSQL role.

[postgres@pg17 ~]$ cat /pgData/pgsql15/data/pg_hba.conf
# TYPE  DATABASE         USER            ADDRESS                METHOD

# Allow local peer access for postgres

local   all             postgres                                peer


# Allow local password access for all
local   all             all                                     scram-sha-256

[postgres@pg17 ~]$

[postgres@pg17 ~]$ psql -U teja -d orcl
 Password for user teja: <---- it's asking password 

psql (15.13)
Type "help" for help.

orcl=> exit
[postgres@pg17 ~]$

 Password Not asking for user postgres 
[postgres@pg17 ~]$ psql -U postgres -d orcl
psql (15.13)
Type "help" for help.

orcl=#


6. Play with trust

No password needed. Anyone who can connect to the server is trusted.
Use: testing only. Not secure in production.

Change pg_hba.conf:

[postgres@pg17 ~]$ cat /pgData/pgsql15/data/pg_hba.conf
# TYPE  DATABASE         USER            ADDRESS                METHOD

# Allow local peer access for postgres
local   all             postgres                                peer

# Allow local password access for all

local   all             all                                     trust

[postgres@pg17 ~]$

[postgres@pg17 ~]$ /usr/pgsql-15/bin/pg_ctl reload -D /pgData/pgsql15/data/
server signaled
[postgres@pg17 ~]$ psql -U teja -d orcl  <--- It won’t ask for a password. 
psql (15.13)
Type "help" for help.

orcl=>


7. Play with reject

[postgres@pg17 ~]$ cat /pgData/pgsql15/data/pg_hba.conf
# TYPE  DATABASE         USER            ADDRESS                METHOD

# Allow local peer access for postgres
local   all             postgres                                peer

# Allow local password access for all

local   all             all                                     reject

[postgres@pg17 ~]$

[postgres@pg17 ~]$ /usr/pgsql-15/bin/pg_ctl reload -D /pgData/pgsql15/data/
server signaled
[postgres@pg17 ~]$ psql -U teja -d orcl

psql: error: connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: FATAL:  pg_hba.conf rejects connection for host "[local]", user "teja", database "orcl", no encryption

[postgres@pg17 ~]$


8. Restrict by User

Allow only user teja, block everyone else:

[postgres@pg17 ~]$ cat /pgData/pgsql15/data/pg_hba.conf
# TYPE  DATABASE         USER            ADDRESS                METHOD

# Allow local peer access for postgres
local   all             postgres                                peer

# Allow local password access for all
local   all             all                                     trust

# Allow IPv4 localhost

host    orcl            teja             192.168.2.31/32         scram-sha-256
host    orcl            all              192.168.2.31/32         reject


[postgres@pg17 ~]$

[postgres@pg17 ~]$ /usr/pgsql-15/bin/pg_ctl reload -D /pgData/pgsql15/data/
server signaled
[postgres@pg17 ~]$ psql -U teja -d orcl -h 192.168.2.31
Password for user teja:
psql (15.13)
Type "help" for help.

orcl=> \conninfo
You are connected to database "orcl" as user "teja" on host "192.168.2.31" at port "5432".
orcl=>
orcl=> exit
[postgres@pg17 ~]$


[postgres@pg17 ~]$ psql -U postgres -d orcl -h 192.168.2.31

psql: error: connection to server at "192.168.2.31", port 5432 failed: FATAL:  pg_hba.conf rejects connection for host "192.168.2.31", user "postgres", database "orcl", no encryption

[postgres@pg17 ~]$


Notice: Only user Teja able to connect where as user postgres not able to connect


9. Restrict by Network

We want to block connections from IP 192.168.2.0/24 

[postgres@pg17 ~]$ cat /pgData/pgsql15/data/pg_hba.conf
# TYPE  DATABASE         USER            ADDRESS                METHOD

# Allow local peer access for postgres
local   all             postgres                                peer

# Allow local password access for all
local   all             all                                     trust

# Allow IPv4 localhost

host    all             all             192.168.2.0/24          reject

[postgres@pg17 ~]$

[postgres@pg17 ~]$ /usr/pgsql-15/bin/pg_ctl reload -D /pgData/pgsql15/data/
server signaled
[postgres@pg17 ~]$ 
[postgres@pg17 ~]$ psql -U teja -d orcl -h 192.168.2.31

psql: error: connection to server at "192.168.2.31", port 5432 failed: FATAL:  pg_hba.conf rejects connection for host "192.168.2.31", user "teja", database "orcl", no encryption

[postgres@pg17 ~]$


10. Verify logs

postgres=# SELECT * FROM pg_hba_file_rules;
 line_number | type  | database | user_name  |   address    |     netmask     |  auth_method  | options | error
-------------+-------+----------+------------+--------------+-----------------+---------------+---------+-------
           4 | local | {all}    | {postgres} |              |                 | peer          |         |
           7 | local | {all}    | {all}      |              |                 | trust         |         |
          11 | host  | {orcl}   | {teja}     | 192.168.2.31 | 255.255.255.255 | scram-sha-256 |         |
          12 | host  | {orcl}   | {all}      | 192.168.2.31 | 255.255.255.255 | reject        |         |
(4 rows)

postgres=#

[postgres@pg17 ~]$ tail -f /pgData/pgsql15/data/log/postgresql-Tue.log
2025-09-16 04:08:37.244 EDT [8294] LOG:  database system was shut down at 2025-09-16 04:08:32 EDT
2025-09-16 04:08:37.251 EDT [8289] LOG:  database system is ready to accept connections
2025-09-16 04:11:27.459 EDT [8289] LOG:  received SIGHUP, reloading configuration files
2025-09-16 04:11:31.842 EDT [8401] FATAL:  pg_hba.conf rejects connection for host "[local]", user "teja", database "orcl", no encryption
2025-09-16 04:13:37.568 EDT [8292] LOG:  checkpoint starting: time
2025-09-16 04:13:37.573 EDT [8292] LOG:  checkpoint complete: wrote 3 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.002 s, sync=0.002 s, total=0.006 s; sync files=2, longest=0.001 s, average=0.001 s; distance=0 kB, estimate=0 kB
2025-09-16 04:16:15.863 EDT [8289] LOG:  received SIGHUP, reloading configuration files
2025-09-16 04:16:52.318 EDT [8474] FATAL:  pg_hba.conf rejects connection for host "192.168.2.31", user "teja", database "orcl", no encryption
2025-09-16 04:27:15.932 EDT [8289] LOG:  received SIGHUP, reloading configuration files
2025-09-16 04:27:50.593 EDT [8613] FATAL:  pg_hba.conf rejects connection for host "192.168.2.31", user "postgres", database "orcl", no encryption

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/

Training

BRIGHT DBA Training

Train your team.
Or train yourself.

Become a production-ready DBA

Instructor-led, hands-on DBA training for enterprise teams and individuals — Oracle, RAC, Data Guard, GoldenGate, Exadata, PostgreSQL and SQL Server.

🏢 For Teams

Corporate Training

  • Customised to your technology stack
  • Real production scenarios and labs
  • Schedule that suits your team
Request a Proposal →
💻 For Individuals

Online Training

  • Live, instructor-led classes
  • Telugu or English
  • Weekend, weekday and fast-track batches
Enquire Now →
Training Programs

Choose your DBA track

Career-focused programs across Oracle, PostgreSQL and SQL Server, each built around real production scenarios — not just theory.

🔴

Oracle DBA

From installation to backup, Data Guard, patching and tuning.

ArchitectureRMANData GuardPatchingTuning
₹12,000₹15,00020% OFF
Duration: 45 days
Language: Telugu / English
Batches: Weekend · Weekday · Fast Track
Enquire Now
🧩

Oracle RAC DBA

Clusterware, ASM, RAC administration and production troubleshooting.

Grid InfraASMRAC AdminAdd/Delete NodeExadata Intro
₹12,000₹15,00020% OFF
Duration: 30 days
Language: Telugu / English
Batches: Weekend · Weekday · Fast Track
Enquire Now
🚀

Oracle Performance Tuning

Find and fix slow SQL with statistics, AWR/ASH, explain plans and tracing.

StatisticsAWR / ASHExplain PlanSQL Tuning AdvisorSQLT & Tracing
₹6,000₹12,00050% OFF
Duration: 2 days
Language: Telugu / English
Batches: Weekend only
Enquire Now
🔁

Oracle GoldenGate

Real-time data replication and zero-downtime migration, from install to troubleshooting.

InstallationDML & DDL ReplicationIntegrated CaptureZero Downtime Migration
Coming soon
Language: Telugu / English
Batches: To be announced
Notify Me
🛡️

Oracle Data Guard

A dedicated course on configuring Data Guard for standalone and RAC systems, with DGMGRL, switchover and failover.

Standalone & RACDGMGRL BrokerSwitchoverFailover
₹3,000₹5,00040% OFF
Duration: 2 days
Language: Telugu / English
Batches: Weekend only
Enquire Now
🗄️

Oracle Exadata Setup on VirtualBox

Build your own Exadata simulation — 2 DB nodes + 3 cell nodes — then take disks all the way to ASM disk groups and set up Oracle RAC on Exadata. Practise CellCLI and DB node patching (simulated cells can’t be patched).

₹3,000₹6,00050% OFF
Duration: 2 days
Language: Telugu / English
Batches: Weekend only
Enquire Now
🐘

PostgreSQL DBA

Install to HA: backups, PITR, replication, Patroni, HAProxy and upgrades.

Backup & PITRReplicationrepmgrPatronipgBackRest
₹12,000₹15,00020% OFF
Duration: 45 days
Language: Telugu / English
Batches: Weekend · Weekday · Fast Track
Enquire Now
🪟

SQL Server DBA

Administration, security, backup/restore and Always On Availability Groups.

InstallationSecurityBackup/RestoreLog ShippingAlways On
Coming soon
Language: Telugu / English
Batches: To be announced
Notify Me
Ways to Learn

Online & Corporate Training

Whether you are upskilling yourself or your whole team, we have a format for you.

💻

Online Training

Live, instructor-led DBA classes for individuals and career switchers, with hands-on labs.

Telugu / EnglishWeekendWeekdayFast Track
Enquire Now
🏢

Corporate Training

Customised programs for your team, matched to your technology stack and skill level.

OracleRACPostgreSQLSQL Server
Request a Proposal
Get in Touch

Questions about a course or batch?

Call or message us on WhatsApp, or send an email. We usually reply quickly.

Free Knowledge Base

Looking for tutorials?

Step-by-step Oracle and PostgreSQL articles, grouped by topic, in our Knowledge Base.

Visit Knowledge Base →
Rajasekhar Amudala - Senior DBA
Your Trainer

Rajasekhar Amudala

Senior Database Professional, Trainer and Consultant with hands-on experience in Oracle, SQL Server and PostgreSQL across production-critical environments. Founder of BrightDBA — hands-on experience first, theory next.

💬

YouTube

1. How to Install Oracle 19c on Linux 7

2. How to create Oracle Database using DBCA?

3. How to Drop Database Manually?

4. How to start database?

5. How to Shutdown Database?

6. How to connect as SYSDBA?

7. How to connect to database remotely in SQL Developer?

8.Install Oracle 19c database software in silent mode

9. Create database in silent mode.

PSQL

PostgreSQL DBA Step by Step Learning

#PostgreSQL DBA Topics
1How to Install PostgreSQL ON Linux?
2How to Install PostgreSQL on Linux 7 using source code?
3How to START/STOP PostgreSQL ON Linux?
4How to Create Database in PostgreSQL?
5PostgreSQL User Management
6PostgreSQL pg_hba.conf Guide
7PostgreSQL Change Data Directory
8Understanding WAL Files in PostgreSQL – For Oracle DBAs
9Change PostgreSQL WAL Directory Path (pg_wal)
10Enable Archive Mode in PostgreSQL 17
11How to Disable ARCHIVELOG Mode
12PostgreSQL Tablespace Management
13PostgreSQL pg_dump and pg_restore Guide
14PostgreSQL Backup and Restore Using pg_dumpall and psql
15pg_basebackup – Backup, Restore, and Recovery
16Backup & Restore PostgreSQL DB Cluster to Another Host (No Archive Mode)
17Backup & Restore PostgreSQL DB Cluster on Same Host
18Restore PostgreSQL to New Host using pg_basebackup + WAL Archives
19PostgreSQL PITR – Point in Time Recovery
20Configure Streaming Replication in PostgreSQL
21Manual Failover in PostgreSQL Streaming Replication
22Convert Asynchronous Replication to Synchronous Replication

 

Thank you,
Rajasekhar Amudala
Email: br8dba@gmail.com
Linkedin: https://www.linkedin.com/in/rajasekhar-amudala/