Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Move a PostgreSQL Data Directory to a New Location Safely

Safely relocate a PostgreSQL cluster to a new mounted filesystem with a consistent copy, correct service configuration, security-policy updates, verification, and rollback steps.
Length12 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Move a PostgreSQL cluster only while it is stopped, copy the complete directory with its ownership and metadata intact, point the service that actually starts the cluster at the new path, then verify SQL access, tablespaces, security policy, and application behavior before deleting the original. A backup or storage snapshot is your recovery point; a live file copy is not a consistent migration.

This guide covers package-managed Debian and Ubuntu clusters as well as manually managed installations. It assumes you are relocating an existing cluster to another mounted filesystem, not creating an empty cluster.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What PostgreSQL’s data directory contains

PGDATA is the cluster directory initialized by initdb. It contains the databases and the state required to start the server, including PG_VERSION, base, global, pg_wal, pg_tblspc, configuration files (unless your packaging keeps them elsewhere), and other control, statistics, and temporary-state directories. PostgreSQL documentation describes this as the traditional location for a cluster’s data and configuration files.

Moving PGDATA is therefore different from moving one database or changing a client connection string. The server must be stopped, every file must be copied consistently, and the service supervisor must be changed to use the new directory. If the cluster uses tablespaces, some data is outside PGDATA and needs a separate plan.

Choose a relocation method before touching the old directory

Method Downtime Rollback When it fits
Direct directory relocation Time to stop, copy, configure, and validate Simple while the untouched old copy remains available Same PostgreSQL major version and a destination filesystem that can preserve the cluster’s files and metadata
New cluster plus backup/restore Restore duration, with a possible logical cutover plan Old cluster remains an independent source Changing major versions, redesigning tablespaces, changing ownership or authentication deliberately, or moving between incompatible filesystems
Storage-level volume move or snapshot Usually the stop and attach time, subject to the storage platform Snapshot or detached original volume A dedicated local, virtual-machine, or managed block-storage volume is being replaced or reattached

A direct move preserves the cluster exactly, but it also preserves problems: an incorrect owner, an overlooked tablespace link, or an unsupported mount option comes across unchanged. A backup/restore takes longer and requires more planning, yet it is often safer when the destination changes the storage design or the PostgreSQL major version.

Pre-move inventory and safety checks

Do this inventory while the old cluster is healthy. Record the PostgreSQL major version, cluster name, service unit, current data directory, operating-system owner, mount point, free space, replication role, and backup status. Confirm that the destination volume will be mounted automatically before PostgreSQL starts after a reboot.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Find the active cluster and data path

Connect as a role with permission to inspect the server and save the results:

psql -d postgres -c 'SELECT version();'
psql -d postgres -c 'SHOW data_directory;'
psql -d postgres -c 'SHOW config_file;'
psql -d postgres -c 'SELECT pg_is_in_recovery();'
psql -d postgres -c 'l'
psql -d postgres -c 'du'
psql -d postgres -c 'db+'

SHOW data_directory reports the directory used by the running server. The database and role listings provide a baseline for post-move checks. The tablespace listing is essential because user-defined tablespaces are represented by symbolic links under PGDATA/pg_tblspc but store relation files elsewhere.

Inspect the operating-system and filesystem state

stat -c '%U %G %a %n' /path/to/current/PGDATA
findmnt -T /path/to/current/PGDATA
findmnt -T /path/to/new/storage
df -h /path/to/current/PGDATA /path/to/new/storage
ps -ef | grep '[p]ostgres'
readlink -f /path/to/current/PGDATA/pg_tblspc/*

Use the real paths returned by your server; the paths above are examples, not directories to copy literally. Check that the destination has enough capacity for the entire cluster plus expected growth, supports the required file ownership and permissions, and allows symbolic links. If your policy relies on ACLs or extended attributes, confirm that the filesystem and mount options support them.

Check replication and backup implications

For a primary with replicas, document how the replica will learn the new storage location and whether replication monitoring assumes a particular path. For a standby, use the same controlled stop and copy procedure and verify its recovery configuration. Do not treat a directory copy as a backup: retain a tested backup or snapshot that predates the move.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Stop PostgreSQL cleanly

Stop only the intended cluster. On Debian and Ubuntu, multiple major versions or cluster names can coexist, so stopping a generic service may affect more than the instance you mean to move.

Debian or Ubuntu with a registered cluster

sudo pg_ctlcluster <version> <cluster> stop
sudo systemctl status postgresql@<version>-<cluster>

The pg_ctlcluster wrapper selects the matching cluster and its data path. When run as root, it can redirect control to systemd supervision. Substitute the actual values shown by your host’s cluster listing; do not stop another production cluster by guessing its name.

Manual installation or explicit service unit

sudo -u postgres pg_ctl -D /path/to/current/PGDATA stop -m fast
ps -ef | grep '[p]ostgres'

Use the installation’s supported control path. A fast shutdown lets active sessions finish and then stops the server; an immediate shutdown should be reserved for an emergency because it causes crash recovery at the next start. Continue only when no PostgreSQL server processes still have files open in the old directory. If your host uses a custom systemd unit, stop that unit rather than a similarly named package service.

Prepare and mount the destination

Mount the new PostgreSQL storage volume at a stable path before copying. Configure the mount in the host’s normal boot mechanism and test it after a controlled remount or reboot window. PostgreSQL should not start against an empty mount-point directory if the volume fails to mount: that can create a confusing second cluster or produce a startup failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create the destination with the cluster owner, normally the operating-system account postgres, and restrictive permissions. The exact mode can vary with packaging, but the data directory must not be writable by untrusted users.

sudo mkdir -p /path/to/new/PGDATA
sudo chown postgres:postgres /path/to/new/PGDATA
sudo chmod 700 /path/to/new/PGDATA
sudo findmnt -T /path/to/new/PGDATA

If the destination directory is the root of a mounted filesystem, create it only after the mount is active. Verify available space with df -h and inode capacity with df -i. Also check whether local security policy requires a particular mount label or whether the path is beneath a directory that denies traversal to the postgres user.

Copy the stopped cluster with metadata intact

Copy the complete old data directory, including hidden files, symbolic links, hard links where present, ACLs, and extended attributes required by your environment. A commonly suitable tool on Linux is:

sudo rsync -aHAX --info=progress2 /path/to/current/PGDATA/ /path/to/new/PGDATA/

The trailing slash on the source means “copy the contents into the destination.” The -a option preserves ordinary ownership, permissions, timestamps, and links; -H preserves hard links; -A preserves POSIX ACLs; and -X preserves extended attributes. Use those options only when supported by both filesystems and allowed by local policy. If your platform uses a different metadata-preserving copy tool, configure its equivalent behavior.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Verify the copy before changing configuration

sudo rsync -aHAXn --delete /path/to/current/PGDATA/ /path/to/new/PGDATA/
sudo stat -c '%U %G %a %n' /path/to/new/PGDATA
sudo ls -la /path/to/new/PGDATA/pg_tblspc
sudo readlink -f /path/to/new/PGDATA/pg_tblspc/*

A dry-run with --delete reports differences without modifying either tree; use it only after checking both source and destination paths carefully. Compare file counts and total sizes as additional sanity checks. A non-empty difference report means the copy needs investigation before cutover.

Do not flatten symbolic links with an archive extraction or a copy option that follows links. A link in pg_tblspc must remain a link, and its target directory must still exist at the path PostgreSQL recorded. Moving a tablespace target is a separate operation.

Point the real startup mechanism at the new path

Changing an environment variable in your shell is not enough if systemd or a distribution wrapper starts PostgreSQL. Change the configuration that owns this instance’s startup path, then inspect the effective unit before starting.

Debian and Ubuntu cluster configuration

Package-managed clusters commonly keep per-cluster configuration under:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
/etc/postgresql/<version>/<cluster>/

Inspect postgresql.conf and its data_directory setting. If the setting is absent, the wrapper may be using the package’s default data path. You can register an already initialized directory with pg_createcluster and set its data_directory, but do not create a new empty cluster by accident. The documented default layout is under /var/lib/postgresql/%v/%c; an existing cluster moved elsewhere must be configured explicitly.

sudo grep -n '^[[:space:]]*data_directory' /etc/postgresql/<version>/<cluster>/postgresql.conf
sudo systemctl cat postgresql@<version>-<cluster>
sudo systemctl daemon-reload

Do not edit a generated unit blindly. Package upgrades can regenerate it, and the durable setting may belong in the cluster configuration or a package-supported override. Confirm with systemctl cat which command and environment the unit actually uses.

Manual PostgreSQL service

A manually managed instance may obtain its path from the PGDATA environment variable, a pg_ctl -D argument, an initdb -D path used when the cluster was created, or an explicit systemd ExecStart. Update the one that starts this server:

sudo systemctl cat postgresql-custom.service
sudo systemctl edit postgresql-custom.service

If you use a systemd drop-in, set the new path in the appropriate Environment=PGDATA=... or ExecStart=... directive, following systemd’s requirement to clear an existing ExecStart before replacing it. Keep a record of the old value for rollback.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle tablespaces, WAL, and external paths

Tablespaces

PostgreSQL stores user-defined tablespace locations as symbolic links below PGDATA/pg_tblspc. Copying PGDATA does not copy the directories those links target. Before cutover, list every tablespace and verify that each target path is mounted and readable by postgres. If a tablespace is also being moved, move its directory while the cluster is stopped, preserve its ownership and metadata, and recreate the link or update the tablespace location using a PostgreSQL-supported procedure rather than leaving a dangling link.

After startup, compare db+ output with the pre-move inventory and query representative tables stored in each non-default tablespace. A cluster can start successfully while a missing tablespace remains unnoticed until an application touches a relation there.

WAL and other separately mounted directories

The pg_wal directory is normally inside PGDATA, but some installations place it on another filesystem through a symbolic link or mount arrangement. Inventory it before copying and verify the destination retains the same layout. Do not delete an old WAL location until the new instance has started and completed its normal recovery or replication checks.

Apply AppArmor or SELinux policy

Ubuntu AppArmor

Ubuntu’s PostgreSQL AppArmor profile can deny access even when Unix ownership and modes are correct. Add a local profile rule covering the new directory tree with the read, write, and lock access required by PostgreSQL, then reload AppArmor. Keep the rule narrow enough to cover only the intended data path.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sudo aa-status
sudo journalctl -k | grep -i apparmor
sudo systemctl reload apparmor

Use the kernel or AppArmor logs to confirm whether a denial names the new path. Reloading the profile is required after editing a local rule; restarting PostgreSQL alone does not load the change.

SELinux

SELinux requires the PostgreSQL database file context on the new tree. This is especially important when the destination is beneath /home, where parent-directory search permissions can block access even after the files are labeled.

ls -Zd /path/to/new/PGDATA
sudo semanage fcontext -a -t postgresql_db_t '/path/to/new/PGDATA(/.*)?'
sudo restorecon -Rv /path/to/new/PGDATA
ls -Z /path/to/new/PGDATA

If semanage is unavailable, install the host’s policy-management package according to its normal administration procedure. Check audit logs for denials and verify every parent directory allows the postgres process to search it.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Start the cluster and validate it at three levels

Start through the same supervisor you will use in production. Do not start the copied directory manually if systemd or pg_ctlcluster will later start a different path.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
sudo systemctl start postgresql@<version>-<cluster>
sudo systemctl status postgresql@<version>-<cluster> --no-pager
sudo journalctl -u postgresql@<version>-<cluster> -b --no-pager

For a manual instance, use its configured service or the matching pg_ctl -D /path/to/new/PGDATA start command. A clean service status is only the first check.

Server and filesystem checks

psql -d postgres -c 'SHOW data_directory;'
psql -d postgres -c 'SELECT current_database(), current_user;'
psql -d postgres -c 'l'
psql -d postgres -c 'du'
psql -d postgres -c 'db+'
stat -c '%U %G %a %n' /path/to/new/PGDATA

SHOW data_directory must report the new path. Confirm expected databases, roles, extensions, and tablespaces. Check the server log for permission, missing-file, recovery, or tablespace errors rather than assuming a successful process start proves the move worked.

Read/write and application checks

Run a representative transaction in a safe test table or an application-approved health-check path. Verify reads from important databases, a controlled write and rollback, extension availability, scheduled jobs, connection pooling, replication status, and monitoring. If the application uses a Unix socket path or a service discovery setting tied to the old host configuration, validate that separately; moving PGDATA does not automatically change client settings.

Keep a rollback path

Do not remove the old directory immediately. Protect it from accidental writes, but retain it intact until SQL checks, logs, application checks, replication checks, and a fresh backup or snapshot have passed.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Stop the new cluster using its normal supervisor.
  2. Restore the original data_directory, PGDATA, -D, or ExecStart setting.
  3. Undo or preserve the security-policy rules needed for the old path.
  4. Start the original cluster and verify its reported data directory and database state.
  5. Document any writes accepted after the attempted cutover. Reconcile them before treating the rollback as a complete recovery.

Once the new location has remained healthy for the retention period required by your operations policy, take a fresh backup or snapshot and then remove the old copy according to your data-disposal rules. Never delete it merely because the service started once.

Troubleshooting by symptom

The service starts the old location

  • Run SHOW data_directory; and inspect the active unit with systemctl cat.
  • Check the exact cluster name and major version; package-managed hosts can have several clusters.
  • Inspect postgresql.conf for an overriding data_directory value.
  • Confirm that the mount containing the new path was present before the service started.

Permission denied on Ubuntu

  • Verify Unix owner and mode on every parent directory and on the copied tree.
  • Check mount options and ACLs.
  • Review AppArmor denial messages, add a local rule for the new subtree, and reload AppArmor.
  • Retry only after confirming the process is using the intended path.

Permission denied with SELinux enabled

  • Inspect labels with ls -Z.
  • Apply the PostgreSQL database context using semanage fcontext and restorecon.
  • Check audit logs and parent-directory search permission, particularly for paths below /home.

Databases or tablespaces appear to be missing

  • Compare l, du, and db+ with the pre-move inventory.
  • Inspect PGDATA/pg_tblspc and resolve each symbolic link.
  • Verify that external tablespace and WAL mounts are present at boot and readable by postgres.
  • Check logs for a relation or directory error that may not appear until a particular database is accessed.

The new disk was not mounted

Stop PostgreSQL immediately if it started against an unintended empty directory. Do not initialize that path. Mount the correct volume, restore the intended configuration, and compare the directory contents with your backup or preserved copy before restarting.

Operational checklist

  • Record version, cluster name, service unit, current path, owner, mounts, tablespaces, replication, and backup status.
  • Confirm the destination volume is mounted at boot and has adequate space, inodes, and metadata support.
  • Stop the intended cluster cleanly and confirm no PostgreSQL processes use the old path.
  • Copy with metadata-preserving options and verify symbolic links, ACLs, and extended attributes as required.
  • Change the path in the actual startup mechanism, not only in an interactive shell.
  • Apply AppArmor or SELinux policy before starting.
  • Validate service logs, SHOW data_directory, databases, roles, extensions, tablespaces, read/write behavior, and application health.
  • Keep the old copy and a recovery point until the rollback window and retention requirements are satisfied.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.