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.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Move a PostgreSQL data directory as a controlled cluster migration: stop the correct cluster, copy its files with ownership and metadata intact, point the service at the new path, fix AppArmor or SELinux policy, then validate with SQL and an application check before deleting the original. A copy made while PostgreSQL is running is not a consistent migration.

What you are moving

PostgreSQL’s data directory is the initialized cluster directory (the value commonly called PGDATA). It contains the cluster’s databases and control files, including PG_VERSION, base, global, pg_wal, pg_tblspc, and configuration files or links used by the installation. The directory must already have been initialized with initdb; creating an empty directory and changing a setting does not move an existing database.

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

The operation has two separate parts: physically copying the cluster and changing the mechanism that starts this particular server. PostgreSQL will continue using the old location if an active service unit, cluster registration, data_directory setting, PGDATA variable, or -D argument still points there.

Choose a migration approach

Approach Downtime Rollback Operational work Best fit
Stop, copy, and repoint the existing cluster From shutdown through validation; copy time is downtime Simple while the original tree is preserved Filesystem metadata, service configuration, and security policy must all be correct Most single-server moves to a new local disk, virtual-machine volume, or dedicated database disk
Create a new cluster and restore a backup Depends on backup size and restore process; can support a separately planned cutover Keep the old cluster untouched until the restored cluster is accepted Requires a complete backup, restore, configuration recreation, and extension/role validation When you want a clean layout, a major-version change, or a migration method independent of the original files
Storage snapshot or volume clone followed by cutover Snapshot consistency and cutover still require a controlled stop unless the storage platform provides a PostgreSQL-aware method Usually strong if the original volume is retained Depends on the storage platform; mount identity, boot ordering, and security labels still need checking Hosts with reliable block-storage snapshots and an established recovery procedure

Regardless of method, take a backup or storage snapshot first. A raw file copy taken while PostgreSQL is changing pages and WAL is not a substitute for a consistent backup.

Pre-move inventory and destination checks

Record the exact cluster you will move

Before stopping anything, record the PostgreSQL major version, cluster name, service unit, current data directory, operating-system owner, directory modes, mount options, replication role, and backup status. On a running server, SQL can show the active path:

SHOW data_directory;

Also record the databases, roles, extensions, and tablespaces. A host can contain several PostgreSQL major versions or several clusters with different names; moving one while starting another is a common source of confusion.

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

Prepare the new filesystem

  • Mount the new PostgreSQL storage volume at the intended path and configure it to be mounted before the database service at boot.
  • Confirm sufficient free space for the entire source tree, growth headroom, temporary files, and WAL.
  • Check filesystem mount options and local policy. Options that prevent execution, locking, or access by the PostgreSQL owner can cause startup failures.
  • Create the destination with the same restrictive ownership and mode policy used by the source. The owner is normally postgres, but verify the actual service account.
  • Decide whether the destination is local storage, a virtual-machine volume, or managed block storage; latency, durability, snapshots, and boot ordering remain storage-platform responsibilities.

Inventory tablespaces and external paths

User-defined tablespaces are not necessarily inside the main directory. PostgreSQL stores symbolic links for them under PGDATA/pg_tblspc. Capture each link target and verify that its target directory will remain mounted and accessible after the move. Moving only the main directory does not move those external tablespace files.

Move the existing cluster step by step

1. Stop the intended cluster cleanly

Use the control path supported by the host rather than killing processes. On Debian or Ubuntu, identify the cluster and use pg_ctlcluster or the corresponding systemd unit. A typical systemd form is:

sudo systemctl stop postgresql@16-main

Replace the unit with the version and cluster name actually installed. On a manually managed installation, use the installation’s pg_ctl -D command. Wait for a clean shutdown, then verify that no PostgreSQL process still has files open in the old directory. For example, inspect the service status and use the host’s process or open-file tools according to local policy.

Do not copy a live data directory and assume that a later restart will repair inconsistencies. The safe file-copy procedure begins only after the cluster has stopped.

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

2. Mount and secure the destination

Mount the new filesystem at its permanent path, then create the target directory if the mount does not create it automatically. Set ownership to the cluster owner and carry over the source’s restrictive modes. A directory that is writable by unrelated users can both prevent PostgreSQL from starting and expose database files.

3. Copy all files while preserving metadata

Use a metadata-preserving copy tool appropriate for both filesystems. A common Linux example is:

sudo rsync -aHAX --info=progress2 /var/lib/postgresql/16/main/ /srv/postgresql/16-main/

The trailing slash on the source copies the contents into the destination. The -a option preserves ordinary attributes and symbolic links; -H preserves hard links; -A preserves POSIX ACLs; and -X preserves extended attributes. Use these options only when supported by the destination filesystem and permitted by local policy. If ACLs or extended attributes are not used on your host, verify what the filesystem supports rather than silently discarding metadata.

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.

After the copy, compare file counts or a checksum-based listing, inspect symbolic links, and verify that pg_tblspc links still resolve to the intended external directories. Reapply the cluster owner and modes if the copy tool or mount altered them.

4. Change the startup mechanism that actually controls this server

Debian and Ubuntu package-managed clusters

These systems commonly keep cluster configuration in /etc/postgresql/<version>/<cluster>. Inspect that cluster’s postgresql.conf and its data_directory setting. Set it to the new directory, then start the same cluster through pg_ctlcluster or systemd. The pg_ctlcluster wrapper determines the data path and delegates to the matching pg_ctl; when invoked as root, it can redirect control to systemd supervision.

If you are registering an existing initialized directory rather than changing an already registered cluster, pg_createcluster can create the registration and set data_directory. Its documented default layout is /var/lib/postgresql/%v/%c, so check the generated configuration instead of assuming the default applies to your host. Review package upgrade behavior before editing generated files or unit definitions.

Manually managed installations

A manually managed server follows whichever path its administrator configured. Update the service’s PGDATA environment variable, the -D argument passed to pg_ctl or the server, the directory supplied to initdb for a newly initialized cluster, or the explicit ExecStart path in the systemd unit. Inspect the active unit with systemd tools so that you modify the source of truth rather than an unused override. After changing it, run systemctl daemon-reload only when a unit file was changed, then start the service.

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

Do not rely on an incidental symbolic link as the only configuration change. A link can work, but it obscures the real mount, complicates security policy, and makes multiple clusters harder to distinguish. A direct, documented data-directory setting is easier to audit and roll back.

5. Apply mandatory access-control policy

Ubuntu AppArmor

AppArmor can deny access even when Unix ownership and modes are correct. Add a local rule covering the complete new PostgreSQL tree, including read, write, and lock access required by the profile. Reload the affected AppArmor profile after saving the rule, then inspect AppArmor denial logs if startup still reports permission errors. Keep the local customization separate from the distribution profile so package updates do not erase it.

SELinux-enabled systems

Inspect labels on the new tree with ls -Z. Assign the PostgreSQL database file context persistently with semanage fcontext, then apply it with restorecon. For a destination such as /srv/postgresql/16-main, the commands are:

sudo semanage fcontext -a -t postgresql_db_t '/srv/postgresql/16-main(/.*)?'
sudo restorecon -Rv /srv/postgresql/16-main

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

Use the context appropriate to the PostgreSQL policy installed on your distribution. If the path is beneath /home, every parent directory must also grant the PostgreSQL service account search permission; correct labels on the final directory do not override a parent-directory denial.

6. Start and validate the new location

Start the same cluster you stopped, then inspect service status and PostgreSQL logs before testing applications. Immediately connect with psql and run:

SHOW data_directory;

The result must be the new path. Check that expected databases, roles, extensions, and tablespaces exist. For tablespaces, a useful inventory query is:

SELECT spcname, pg_tablespace_location(oid) FROM pg_tablespace;

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

Run a representative read and write transaction in a test or controlled application session, then perform the application’s health check. Confirm that replication, scheduled jobs, monitoring, and backup tools also reference the new location or the service endpoint rather than a hard-coded old path.

7. Preserve a rollback copy

Do not delete the old directory immediately. Make it read-only or otherwise protected from accidental writes while you keep it as the rollback source. Take a fresh backup or snapshot after validation, and retain the old copy for the period required by your recovery plan. Document exactly which writes occurred after cutover; those writes must be reconciled if you roll back.

Tablespaces, WAL, and related paths

Inspect pg_tblspc before and after the move. Each symbolic link points to a separately managed tablespace directory, which may be on another disk. Keep those targets mounted at the same paths or migrate them in a separately planned operation. A successful start of the main cluster does not prove that every external tablespace is available; queries that touch an unavailable tablespace can fail later.

The pg_wal directory is part of the cluster copy unless your installation deliberately places WAL elsewhere. Check configuration and mount units for separate WAL locations, archive directories, replication slots, and backup destinations. These paths must remain reachable with the same permissions after cutover.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting after the move

The service starts the old location

Inspect the active systemd unit, the version and cluster name, postgresql.conf, and the effective data_directory. Package-managed hosts often contain several major versions or clusters. Confirm that you stopped and started the same unit, not a default aggregate service that selected another cluster.

Permission denied on Ubuntu

Check the destination owner and modes first, then mount options. If they are correct, inspect AppArmor profile logs for a denial involving the new path. Add the path to the profile’s local rules and reload AppArmor; restarting PostgreSQL alone does not reload a changed policy.

Permission denied with SELinux

Compare source and destination labels with ls -Z. Apply the PostgreSQL database context using semanage fcontext and restorecon, and verify search permission on every parent directory. SELinux audit messages identify the denied path and operation.

Databases or tablespaces are missing

Compare the pre-move database, role, extension, and tablespace inventory with the running cluster. Inspect pg_tblspc links and their targets. If only the main directory was copied, external tablespace data may still be on an unmounted or inaccessible filesystem.

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

The new cluster will not start

Read the PostgreSQL log and service status before changing files again. Common causes are an incorrect path, wrong owner, incomplete copy, missing mount, invalid security label, or a port conflict with another cluster. Stop the new instance before attempting a rollback so two processes cannot use related files.

Rollback procedure

  1. Stop the new cluster through its normal service or pg_ctl path.
  2. Record any application writes made after the cutover; the preserved old copy will not contain them.
  3. Restore the original data_directory, PGDATA, -D, or systemd configuration.
  4. Confirm that the original mount and tablespace targets are available, then start the original cluster.
  5. Verify SHOW data_directory;, logs, application connectivity, and replication state before reopening writes.

After a rollback, reconcile transactions performed against the new location before treating the old cluster as authoritative. Keep the failed destination available for diagnosis until the incident is closed.

When a direct move is not the right choice

Use a new cluster plus backup restore when you need a major-version upgrade, a clean initialization, a change in database encoding or locale, or a migration that avoids trusting a raw filesystem copy. Use a storage snapshot workflow when your platform provides tested, crash-consistent snapshots and you understand how to attach the volume, preserve identifiers, and apply security labels. In both cases, the same validation applies: confirm the reported data directory, objects, tablespaces, logs, and application behavior before retiring the original.

Frequently Asked Questions

Can I copy the PostgreSQL data directory while the server is running?

No. Stop the intended cluster cleanly first, or use a PostgreSQL-aware backup or storage procedure that explicitly provides consistency. An ordinary live file copy can capture mutually inconsistent data and WAL files.

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

Why did changing PGDATA have no effect?

The running service may obtain its path from a Debian or Ubuntu cluster configuration, a systemd unit, a data_directory setting, or an explicit -D argument instead of your shell’s PGDATA variable. Inspect the active startup mechanism and verify with SHOW data_directory;.

Do I have to move tablespaces separately?

Only if their target directories are changing. Tablespaces are represented by links under pg_tblspc; moving the main directory does not copy external tablespace targets. Keep those targets mounted and accessible, or migrate them as a separate operation.

How long should I keep the old directory?

Keep it until SQL checks, application checks, backups, and your defined rollback-retention period have passed. Protect it from accidental writes and document any post-cutover writes in case rollback is required.

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.

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