Encrypting the PostgreSQL data directory at rest protects table, index, and write-ahead log files when a disk, virtual machine image, or storage snapshot is taken offline. It is a storage-layer control for stolen or retired media, not a replacement for database permissions, host hardening, or encrypted client connections.
PostgreSQL stores its cluster files under data_directory, and the upstream encryption options describe file-system or block-level encryption for data partition protection. On Linux, dm-crypt with LUKS opens an encrypted device as a normal mapper path such as /dev/mapper/pgdata_crypt, then PostgreSQL continues to use the same data directory after the encrypted filesystem is mounted there.
Plan a maintenance window before moving a production cluster. Use a new empty block device, copy the stopped cluster onto it, add boot-time unlock and mount entries, and verify PostgreSQL after the encrypted volume is active. Use the actual data_directory value from the server, keep an unencrypted rollback copy only until the reboot test passes, and remember that a mounted encrypted filesystem is readable to accounts that already have operating-system access.
Related: How to secure a PostgreSQL server
Related: How to create a PostgreSQL database backup
$ sudo -u postgres psql -tAc "SHOW data_directory;" /var/lib/postgresql/18/main
Examples use the Debian and Ubuntu packaged layout for PostgreSQL 18. Replace /var/lib/postgresql/18/main with the path returned by the command on the target server.
Formatting the wrong device, copying an incomplete data directory, or losing the LUKS header can make the cluster unrecoverable. Keep a database backup or storage snapshot that is separate from the device being encrypted.
$ lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS NAME SIZE TYPE FSTYPE MOUNTPOINTS sda 100G disk ├─sda1 1G part vfat /boot/efi └─sda2 99G part ext4 / sdb 200G disk └─sdb1 200G part
Use a dedicated partition, logical volume, or virtual disk for the PostgreSQL data volume.
$ sudo cryptsetup luksFormat --type luks2 /dev/sdb1 WARNING! ======== This will overwrite data on /dev/sdb1 irrevocably. Are you sure? (Type 'YES' in capital letters): YES Enter passphrase for /dev/sdb1: Verify passphrase:
Selecting the wrong device permanently erases data on that device.
$ sudo cryptsetup luksHeaderBackup /dev/sdb1 --header-backup-file /root/pgdata_crypt.luks-header
Store the header backup and a recovery passphrase somewhere protected but separate from the encrypted disk. A damaged LUKS header can make the encrypted data permanently inaccessible.
$ sudo cryptsetup open /dev/sdb1 pgdata_crypt Enter passphrase for /dev/sdb1:
The mapper name pgdata_crypt is referenced later by /etc/crypttab and appears as /dev/mapper/pgdata_crypt after unlock.
$ sudo cryptsetup status pgdata_crypt /dev/mapper/pgdata_crypt is active. type: LUKS2 cipher: aes-xts-plain64 keysize: 512 [bits] key location: keyring device: /dev/sdb1 sector size: 4096 [bytes] mode: read/write
$ sudo mkfs.ext4 -L pgdata /dev/mapper/pgdata_crypt mke2fs 1.47.2 (1-Jan-2025) Creating filesystem with 52424832 4k blocks and 13107200 inodes Filesystem UUID: 2a3b4c5d-6e7f-8901-2345-6789abcdef01 ##### snipped #####
$ sudo systemctl stop postgresql
$ sudo systemctl status postgresql
● postgresql.service - PostgreSQL RDBMS
Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled)
Active: inactive (dead)
##### snipped #####
$ sudo mv /var/lib/postgresql/18/main /var/lib/postgresql/18/main.unencrypted
Use the data_directory path confirmed in the first step.
$ sudo mkdir -p /var/lib/postgresql/18/main
$ sudo mount /dev/mapper/pgdata_crypt /var/lib/postgresql/18/main
$ sudo chown postgres:postgres /var/lib/postgresql/18/main
$ sudo chmod 0700 /var/lib/postgresql/18/main
$ sudo rsync -aHAX --numeric-ids /var/lib/postgresql/18/main.unencrypted/ /var/lib/postgresql/18/main/
SELinux systems may require restorecon -RF on the data directory after copying.
$ sudo blkid /dev/sdb1 /dev/sdb1: UUID="11111111-2222-3333-4444-555555555555" TYPE="crypto_LUKS" PARTUUID="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
/etc/crypttab pgdata_crypt UUID=11111111-2222-3333-4444-555555555555 none luks
The none key field prompts for a passphrase at boot. Use a protected key file, TPM-backed unlock, or another approved key-management method only when unattended startup is required and the key is protected separately from the data volume.
$ sudo blkid /dev/mapper/pgdata_crypt /dev/mapper/pgdata_crypt: LABEL="pgdata" UUID="2a3b4c5d-6e7f-8901-2345-6789abcdef01" BLOCK_SIZE="4096" TYPE="ext4"
/etc/fstab UUID=2a3b4c5d-6e7f-8901-2345-6789abcdef01 /var/lib/postgresql/18/main ext4 defaults 0 2
An invalid /etc/fstab entry can stop the system in emergency mode during boot.
$ sudo systemctl daemon-reload
$ sudo systemctl start postgresql
$ findmnt -no SOURCE,TARGET /var/lib/postgresql/18/main /dev/mapper/pgdata_crypt /var/lib/postgresql/18/main
$ sudo -u postgres psql -c "SELECT now();"
now
-------------------------------
2026-06-07 05:12:34.12345+00
(1 row)
$ sudo reboot
A passphrase-based /etc/crypttab entry prompts on the system console during startup.
$ findmnt -no SOURCE,TARGET /var/lib/postgresql/18/main /dev/mapper/pgdata_crypt /var/lib/postgresql/18/main
$ sudo -u postgres psql -c "SELECT now();"
now
-------------------------------
2026-06-07 05:18:20.67890+00
(1 row)
$ sudo rm -rf /var/lib/postgresql/18/main.unencrypted
Removing the rollback copy is irreversible, but keeping it leaves an unencrypted copy of the database on disk.