惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
S
Securelist
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
A
About on SuperTechFans
Martin Fowler
Martin Fowler
量子位
D
Docker
G
Google Developers Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
U
Unit 42
S
Secure Thoughts
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
K
Kaspersky official blog
NISL@THU
NISL@THU
T
Tailwind CSS Blog
I
Intezer
T
Threat Research - Cisco Blogs
P
Privacy & Cybersecurity Law Blog
D
DataBreaches.Net
Engineering at Meta
Engineering at Meta
C
Cyber Attacks, Cyber Crime and Cyber Security
F
Full Disclosure
Microsoft Security Blog
Microsoft Security Blog
Cisco Talos Blog
Cisco Talos Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
Vercel News
Vercel News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Fortinet All Blogs
W
WeLiveSecurity
C
Cisco Blogs
Security Latest
Security Latest
PCI Perspectives
PCI Perspectives
L
LangChain Blog
P
Palo Alto Networks Blog
IT之家
IT之家
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale
Help Net Security
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
Project Zero
Project Zero
G
GRAHAM CLULEY
Recent Commits to openclaw:main
Recent Commits to openclaw:main
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
Lohrmann on Cybersecurity
Cloudbric
Cloudbric
D
Darknet – Hacking Tools, Hacker News & Cyber Security

DigitalOcean Community Tutorials

It's Time to Break Up with Your Cloud: Why AI Teams are Switching We Built a Private-Document AI App to Test Platform Security. Here Is What We Could Actually Verify. PostgreSQL Explained: A Complete Beginner-to-Advanced Guide How To Install and Configure Postfix on Ubuntu How To Build a Web Application Using Flask in Python 3 Build AI Reading List with DigitalOcean Functions and Mistral How To Concatenate Strings in Python How to Allow MySQL Remote Access Securely How To Install and Use Docker on Rocky Linux How To Build a Multi-Agent AI System with Docker Agent DSPy Use Cases: Build Optimized LLM Pipelines How To Submit AJAX Forms with jQuery Build an AI-Powered GPU Fleet Optimizer with the DigitalOcean AI Platform ADK Monitor GPU Utilization in Real Time: A Complete Guide Reduce File Size of Images in Linux - CLI and GUI methods Reduce PDF File Size in Linux: Tools and Methods How To Set Up a Private Docker Registry on Ubuntu How To Troubleshoot Terraform: Errors and Fixes How to Use Go Modules Python Multiprocessing Example: Process, Pool & Queue Convert Class Components to Functional Components with React Hooks How To Install and Configure Ansible on Ubuntu LLM Tokenizers Simplified: BPE, SentencePiece, and More How To Monitor System Authentication Logs on Ubuntu How to Use Traceroute and MTR to Diagnose Network Issues How to Deploy Postgres to Kubernetes Cluster Importing Packages in Go: A Complete Guide Create RAID Arrays with mdadm on Ubuntu How To Make an HTTP Server in Go How To Set Up Time Synchronization on Ubuntu How To Use Struct Tags in Go apt-key Deprecation: Add Repositories with GPG on Ubuntu Linux ps Command: 20 Real-World Examples Python struct.pack and struct.unpack for Binary Data Deadlock in Java: Examples, Detection, and Prevention How To Use Find and Locate to Search for Files on Linux Structured Resume Skill Extraction Using Mistral-7B Inference How to Use the Python Main Function How to Set Up NemoClaw on a DigitalOcean Droplet with 1-Click Build an End-to-End RAG Pipeline for LLM Applications From Single to Multi-Agent Systems: Key Infrastructure Needs Back Up Data to Object Storage Using Restic How to Generate Videos with LTX-2.3 on DigitalOcean GPU Droplets How To Install LAMP Stack (Apache, MySQL, PHP) on Ubuntu How to Download Files with cURL How To Use Variadic Functions in Go Generate UUIDs with uuidgen on Linux How To Use EJS to Template Your Node Application How to Install Node.js on Ubuntu (Step-by-Step Guide) MongoDB Indexes: Improve Query Performance with Node.js LLM Tool Calling with DigitalOcean AI Platform and Databases What are Text Diffusion Models? - An Overview Crafting a Game from Scratch with GPT-5.4 Building Long-Term Memory in AI Agents with LangGraph and Mem0 How To Install PHP 7.4 and Set Up a Local Development Environment on Ubuntu 20.04 Build a GraphQL API in Go to Upload Files to Spaces How To Lint and Format Code with ESLint in Visual Studio Code Train YOLO26 for Retail Object Detection on DigitalOcean GPUs How To Work with JSON in MySQL How to Use the JavaScript .map() Method Building a Scalable App with MongoDB Using DigitalOcean's MCP Server How to Create an SSH Key in Linux: Easy Step-by-Step Guide Measure MySQL Query Performance with mysqlslap How To Use *args and **kwargs in Python 3 Nemotron 3 helped me find the perfect dish rack? A2A vs MCP - How These AI Agent Protocols Actually Differ How To Install and Manage Supervisor Docker Container Images with Watchtower on Ubuntu Getting Started with Qwen3.5 Vision-Language Models How To Create a New Sudo-Enabled User on Ubuntu How to Use Ansible to Install and Set Up Docker on Ubuntu How To Enable Remote Desktop Protocol Using xrdp on Ubuntu 22.04 How To Convert a String to a List in Python How To Check If a String Contains Another String in Python How to Read a Properties File in Python Python Command Line Arguments: sys.argv, argparse, getopt Mastering Grep command in Linux/Unix: A Beginner's Tutorial Understanding Python Data Types How to Implement a Stack in C With Code Examples Python os.system() vs subprocess: Run System Commands How To Install and Use Docker Compose on Ubuntu How to Add and Delete Users on Ubuntu How To Order Query Results in Laravel Eloquent How To Define and Use Handlers in Ansible Playbooks How To Install and Use SQLite on Ubuntu How To Install and Use Homebrew on macOS How To Manage DateTime with Carbon in Laravel and PHP How To Install Git on Ubuntu How To Install and Secure Redis on Ubuntu How To Build and Install Go Programs on Linux Using ldflags to Set Version Information for Go Applications How To Build a Node.js Application with Docker How To Add JavaScript to HTML How To Reset Your MySQL or MariaDB Root Password How To Add Images in Markdown How To Set Up a Production Elasticsearch Cluster with Ansible How To Set Up a Firewall Using firewalld on CentOS Understanding Systemd Units and Unit Files How To Set Up Replication in MySQL How To Use the .htaccess File
Move PostgreSQL Data Directory on Ubuntu
Melissa Anderson, Mark Drake, Jeanelle Horcasitas · 2022-07-27 · via DigitalOcean Community Tutorials

Introduction

PostgreSQL stores each cluster’s databases and system files inside its data directory. When that directory lives on a disk that also hosts the operating system, the cluster can run out of space or start competing for I/O with other processes.

This guide shows how to move the PostgreSQL data directory to a new mount point by copying the files with rsync, updating postgresql.conf, and adjusting the Ubuntu AppArmor profile so PostgreSQL can access the new path. You will also use systemctl to stop and start PostgreSQL and psql to verify the migration.

By the end, PostgreSQL should start cleanly from the new data directory, and you will have a .bak rollback path if you need to revert.

Key Takeaways

  • The default PostgreSQL data directory is /var/lib/postgresql/<version>/main, and you can confirm it with SHOW data_directory; in psql.
  • rsync is preferred over cp because it preserves permissions, timestamps, symlinks, and special file metadata, and it can resume by copying only differences.
  • AppArmor restricts PostgreSQL to specific filesystem paths, so a non-default data directory path often needs a profile override on Ubuntu.
  • After restart, you can verify the migration succeeded by running SHOW data_directory; in psql.
  • Keep the original directory as /var/lib/postgresql/<version>/main.bak to rollback safely by renaming it back and reverting data_directory.

Understanding the PostgreSQL Data Directory Structure

Before you move the data directory, understand what each component inside it does. An incomplete or corrupted copy of any of these files will prevent PostgreSQL from starting or cause silent data loss.

Configuration files are also part of this picture. Even though postgresql.conf is commonly a symlink into /etc/postgresql/<version>/main/postgresql.conf, PostgreSQL reads the active data_directory value from that file.

The default layout looks like this (replace 14 with your installed PostgreSQL major version):

/var/lib/postgresql/
└── 14/
    └── main/
        ├── base/
        ├── global/
        ├── pg_wal/
        ├── pg_hba.conf
        ├── postgresql.conf -> /etc/postgresql/14/main/postgresql.conf
        └── PG_VERSION

Each entry in that tree has a specific role:

  • base/ holds the actual on-disk database files. Each subdirectory inside base/ corresponds to a database in the cluster, identified by its object ID (OID). This is the largest part of the data directory on most systems.
  • global/ holds cluster-wide system catalog tables such as pg_database and pg_authid. These tables track which databases exist and which roles can access them.
  • pg_wal/ holds write-ahead log (WAL) segments. PostgreSQL writes changes here before applying them to the main data files. If pg_wal/ is missing or incomplete after a migration, PostgreSQL cannot guarantee data consistency and will refuse to start.
  • pg_hba.conf controls client authentication rules. It defines which hosts can connect, which users they can connect as, and which authentication method applies.
  • PG_VERSION is a plain text file containing the PostgreSQL major version number. PostgreSQL reads this at startup to confirm the data directory format matches the running binary.

The data_directory directive in postgresql.conf tells PostgreSQL where to find those files. After the migration, the only PostgreSQL configuration change you typically need is updating data_directory, alongside the AppArmor path permissions required on Ubuntu.

Prerequisites

Before you move files, confirm your PostgreSQL major version and verify that AppArmor is active on the host.

To complete this guide, you will need:

psql --version
pg_lsclusters
  • AppArmor enabled. Ubuntu installs AppArmor by default on many systems, and it should be active for the profile behavior described in Step 6. Verify with:
sudo aa-status
  • A new mounted location for the data directory. In examples, this uses /mnt/volume_nyc1_01, but you must substitute your own mount point path.

If you need a refresher on the file copy method used here, read How to Use rsync to Sync Local and Remote Directories. If you need to prepare the disk that backs the mount point, see How to Partition and Format Storage Devices in Linux.

Step 1 - Prepare the New Storage Location

Confirm where PostgreSQL is currently reading data from, then check that the new mount point has enough free space for the full cluster directory.

Run psql as the postgres user and print the current data_directory:

sudo -u postgres psql
SHOW data_directory;
       data_directory
-----------------------------
 /var/lib/postgresql/14/main
(1 row)

Exit psql with:

\q

Now confirm the mount point is available and has enough space:

df -h /mnt/volume_nyc1_01
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdx1       100G   10G   90G  10% /mnt/volume_nyc1_01

Also check how large your current data directory is so you can compare it to the destination free space: <$>[warning] The destination must have at least as much free space as the current data directory size. <$>

Check the current size of your data directory:

sudo du -sh /var/lib/postgresql/
12G    /var/lib/postgresql/

If the mount point is new or freshly attached, confirm it is accessible before running the copy:

ls -la /mnt/volume_nyc1_01
total 8
drwxr-xr-x  2 root root 4096 Jan 10 09:00 .
drwxr-xr-x 12 root root 4096 Jan 10 08:55 ..

The rsync command in Step 3 preserves permissions and ownership from the source directory, but the destination filesystem must support standard POSIX file attributes. FAT32 and exFAT volumes do not; use ext4 or xfs for a PostgreSQL data directory.

Step 2 - Stop the PostgreSQL Service

Stop PostgreSQL before copying files so the data directory does not change during the migration.

Run:

sudo systemctl stop postgresql

Then verify the service is stopped:

sudo systemctl status postgresql
● postgresql.service - PostgreSQL RDBMS
     Loaded: loaded
     Active: inactive (dead)

As an alternative check, use pg_lsclusters to confirm the cluster is down:

pg_lsclusters
Ver Cluster Port Status Owner    Data directory               Log file
14  main    5432 down   postgres /var/lib/postgresql/14/main  ...

Step 3 - Copy the Data Directory with rsync

Copy the stopped cluster files with rsync so permissions, timestamps, and special metadata carry over to the new mount point.

Run the copy command:

sudo rsync -av /var/lib/postgresql /mnt/volume_nyc1_01
sending incremental file list
postgresql/
postgresql/14/
postgresql/14/main/
postgresql/14/main/PG_VERSION
postgresql/14/main/base/
postgresql/14/main/global/
postgresql/14/main/pg_hba.conf
postgresql/14/main/pg_wal/
...

sent 2,873,452 bytes  received 412 bytes  819,733.47 bytes/sec
total size is 2,872,198  speedup is 1.00

The final summary line confirms rsync completed the transfer.

Optional progress for large directories:

sudo rsync -av --progress /var/lib/postgresql /mnt/volume_nyc1_01

Flag behavior:

  • -a enables archive mode, which preserves permissions, timestamps, symlinks, owner, group, and recursively copies directories.
  • -v prints more detail so you can follow what rsync is transferring.
  • There is no trailing slash on the source path. Without a trailing slash, rsync copies the postgresql directory itself into the destination, which results in the expected directory layout under your mount point.

If you want background on what these flags mean, see How to Use rsync to Sync Local and Remote Directories.

rsync can be safely re-run if the copy is interrupted. After the cluster is stopped, rerunning rsync continues by transferring any remaining differences, which makes the copy effectively idempotent.

Do not use cp -r for this migration. cp -r does not reliably preserve all POSIX permissions and special file attributes that PostgreSQL expects for database directories.

Step 4 - Rename the Original Directory as a Backup

Rename the current data directory to a .bak path so you have a fast rollback option without re-copying data.

After the copy completes, move the old directory out of the way:

sudo mv /var/lib/postgresql/14/main /var/lib/postgresql/14/main.bak

Keep this .bak directory until you confirm the new cluster starts and the application behaves as expected. This preserves a rollback path that does not require re-running rsync.

To roll back, rename the backup directory back to main:

sudo mv /var/lib/postgresql/14/main.bak /var/lib/postgresql/14/main

Then revert postgresql.conf so data_directory points to /var/lib/postgresql/<version>/main, and start PostgreSQL again.

Step 5 - Update the PostgreSQL Configuration

Point PostgreSQL at the new location by updating the data_directory directive in the active postgresql.conf.

Edit the configuration file:

sudo nano /etc/postgresql/14/main/postgresql.conf

On a default PostgreSQL installation, the data_directory line is often commented out because PostgreSQL uses a compiled-in default when no explicit value is set. When you open the file, you may see one of these three states:

The line is commented out with a # prefix:

#data_directory = '/var/lib/postgresql/14/main'  # use data in another directory

The line is absent entirely, with only a comment block referencing the default.

The line is already uncommented with the default path set explicitly:

data_directory = '/var/lib/postgresql/14/main'

In the first two cases, add the data_directory line explicitly with the new path. In the third case, replace the existing value. In all cases, the result should be a single uncommented data_directory line pointing to the new mount point.

Find the existing data_directory line in postgresql.conf:

data_directory = '/var/lib/postgresql/14/main'

Replace it with the new path (substitute your major version and mount point):

data_directory = '/mnt/volume_nyc1_01/postgresql/14/main'

After saving the file, confirm the value that PostgreSQL will read:

grep -v '^#' /etc/postgresql/14/main/postgresql.conf | grep data_directory
data_directory = '/mnt/volume_nyc1_01/postgresql/14/main'

The -v '^#' flag excludes commented lines so only the active directive is returned. Without this filter, grep would also return commented-out data_directory lines and make the output ambiguous.

Step 6 - Update the AppArmor Profile

Update the AppArmor local override so PostgreSQL can read and write the new data directory path on Ubuntu.

On Ubuntu, AppArmor restricts which filesystem paths PostgreSQL is allowed to access. After a data directory move to a non-default path, PostgreSQL can fail to start with a permission denied error unless the AppArmor profile allows the new directory.

First check whether AppArmor is enforcing the PostgreSQL profile:

sudo aa-status | grep postgres
   /usr/lib/postgresql/14/bin/postgres

List the available AppArmor local override files to confirm the correct filename for your PostgreSQL version:

ls /etc/apparmor.d/local/
usr.lib.postgresql.14.bin.postgres

If the local override file for your PostgreSQL binary does not exist, create it as an empty file. AppArmor will load it when you reload the profile.

Next, edit the AppArmor local override file for the PostgreSQL binary. Use your PostgreSQL major version in the path:

sudo nano /etc/apparmor.d/local/usr.lib.postgresql.14.bin.postgres

The rules in this local file are applied to the corresponding AppArmor profile for the PostgreSQL server binary. The path in the rule should match the data_directory path you set in Step 5.

Add a rule that grants read/write and locks on the new data directory tree:

/mnt/volume_nyc1_01/postgresql/** rwk,

In AppArmor rule syntax, r grants read access, w grants write access, and k grants file locking, which PostgreSQL requires to coordinate concurrent access to data files.

This rule covers the whole subtree under the mount point, so PostgreSQL can write files in base/, global/, and pg_wal/.

Reload AppArmor so the change takes effect:

sudo systemctl reload apparmor

Alternatively, you can reload the profile by parsing it directly:

sudo apparmor_parser -r /etc/apparmor.d/usr.lib.postgresql.14.bin.postgres

The path here points to the main profile file in /etc/apparmor.d/, not the local override file in /etc/apparmor.d/local/. When apparmor_parser reloads the main profile, it automatically includes any rules in the corresponding local override file, so editing the local file and reloading the main profile is the correct sequence.

Skipping Step 6 is one of the most common reasons for Permission denied errors after a PostgreSQL data directory migration on Ubuntu, even when directory ownership and chmod values are correct.

Step 7 - Restart PostgreSQL and Verify the Migration

Start PostgreSQL, then confirm it is online and that psql reports the new data_directory.

Start the service:

sudo systemctl start postgresql

Check service status:

sudo systemctl status postgresql
● postgresql.service - PostgreSQL RDBMS
     Active: active (exited) since ...

Confirm the cluster is online and points to the new directory:

pg_lsclusters
Ver Cluster Port Status Owner    Data directory                             Log file
14  main    5432 online postgres /mnt/volume_nyc1_01/postgresql/14/main    ...

Verify inside psql:

sudo -u postgres psql
SHOW data_directory;
             data_directory
----------------------------------------
 /mnt/volume_nyc1_01/postgresql/14/main
(1 row)

Run additional checks to catch obvious data access issues:

-- List all databases to find your database name
\l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges
-----------+----------+----------+-------------+-------------+-----------------------
 myapp_db  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
(2 rows)
-- Connect to your database and list its tables
\c myapp_db
\dt
          List of relations
 Schema |    Name     | Type  |  Owner
--------+-------------+-------+----------
 public | users       | table | postgres
 public | orders      | table | postgres
(2 rows)
-- Spot-check a critical table row count to confirm data integrity
SELECT COUNT(*) FROM users;
 count
-------
  1042
(1 row)

Substitute your actual database name from the \l output and your table name from the \dt output. The row count should match what you expect based on your application data. A count of zero on a table that should have rows indicates a copy problem — do not proceed until the cause is identified. Before removing the backup directory, also run an application smoke test, as migrations can look healthy at startup but surface issues when your app reads specific tables or uses specific extensions.

Step 8 - Remove the Backup Directory (Optional)

Remove the backup only after the new data directory has been running stably for at least one application cycle.

Before removal, confirm that pg_lsclusters shows the correct Data directory path, the application has completed at least one full read/write cycle without errors (for example, one business day of normal traffic or one successful deployment and smoke test), and no application errors have appeared in logs since the migration.

Then remove the backup directory:

sudo rm -Rf /var/lib/postgresql/14/main.bak

Troubleshooting Common Migration Errors

When migration steps do not work, narrow down the cause by checking service status, AppArmor enforcement, and the exact configuration file PostgreSQL is reading.

PostgreSQL Fails to Start After Migration

Symptom: systemctl start postgresql returns a failure, and journalctl shows permission denied messages for the new data directory path.

Cause 1: The AppArmor profile was not updated. Before pointing to Step 6, confirm AppArmor is the actual cause by checking the PostgreSQL journal for the specific error:

sudo journalctl -u postgresql -n 50

Look for lines containing Permission denied referencing the new data directory path. If those lines are present, complete Step 6, reload AppArmor, and try starting PostgreSQL again. If the journal shows a different error, continue to Cause 2 and Cause 3 before revisiting the AppArmor profile.

Cause 2: Incorrect filesystem ownership or permissions. Fix:

sudo chown -R postgres:postgres /mnt/volume_nyc1_01/postgresql
sudo chmod 700 /mnt/volume_nyc1_01/postgresql/14/main

Cause 3: The data_directory path contains a typo or trailing whitespace. Fix: re-check the configured value in the active postgresql.conf file using grep.

PostgreSQL Starts But Loads the Old Data Directory

Symptom: SHOW data_directory; returns the old path after restart.

Cause 1: You updated the wrong postgresql.conf file. This happens when multiple PostgreSQL versions are installed or when you edited a file under a different version directory.

Cause 2: The change was not saved before restarting PostgreSQL.

Fix: confirm which config file PostgreSQL is using:

sudo -u postgres psql -c "SHOW config_file;"

Then verify data_directory inside that exact file.

Connection Refused After Migration

Symptom: psql or applications cannot connect, even though PostgreSQL appears to be running.

Cause 1: pg_hba.conf in the new data directory differs from what your environment expects, or PostgreSQL authentication settings were not copied as expected.

Cause 2: The socket directory configuration changed, or the client is connecting to the wrong socket path.

Fix: check PostgreSQL logs:

sudo journalctl -u postgresql --since "10 minutes ago"

Also check the configured Unix socket directories:

sudo -u postgres psql -c "SHOW unix_socket_directories;"

Frequently Asked Questions

Use these Q&A entries to handle common variations and failure modes during a data directory migration.

Q: How do I move a PostgreSQL data directory to a new location on Ubuntu?

A: Stop PostgreSQL so the data files do not change during the copy. Copy the stopped data directory to the new mount point with rsync, then update the active postgresql.conf data_directory value to the new path. Add an AppArmor local rule for that path, start PostgreSQL again, and verify with psql that SHOW data_directory; reports the new directory.

Q: Where is PostgreSQL data stored on Ubuntu by default?

A: PostgreSQL stores cluster data under /var/lib/postgresql/<version>/main. The <version> segment matches the PostgreSQL major version installed on your system. You can confirm the exact directory with SHOW data_directory; in psql.

Q: How do I change PostgreSQL’s data directory on Linux?

A: Edit postgresql.conf and change the data_directory directive to the new absolute path. Restart PostgreSQL so it reads the updated configuration and starts using the migrated directory.

Q: How do I set the data directory path for PostgreSQL in Linux?

A: Set data_directory in postgresql.conf to the absolute path of the migrated cluster directory. The directive typically looks like data_directory = '/mnt/your-mount/postgresql/<version>/main'. Substitute your mount point and PostgreSQL major version.

Q: Why does PostgreSQL fail to start after moving the data directory?

A: The most common cause on Ubuntu is an AppArmor profile restriction that does not allow PostgreSQL to access the new path. Check Step 6 first, then verify ownership and permissions on the new directory. Also confirm that data_directory in the active postgresql.conf matches the real path on disk.

A: Yes, but you still need to account for AppArmor. AppArmor applies to the real target path that the filesystem resolves, so a symlink does not bypass path restrictions. If your profile only allows the default directory, PostgreSQL can still fail until you update the AppArmor local rule.

Q: Is rsync the best tool for copying a PostgreSQL data directory?

A: rsync is typically the best choice because it preserves permissions and special metadata while copying recursively. Since you stop PostgreSQL first, rerunning rsync after interruptions converges on the same target state without needing to recopy everything from scratch.

Q: Do I need to reinitialize the database after moving the data directory?

A: No. Moving an existing data directory does not require initdb because PostgreSQL is already initialized inside the migrated directory. initdb is for creating a brand-new cluster, not for relocating an existing one.

Conclusion

After you stop PostgreSQL, copy the data directory to the new mount point, update postgresql.conf, and update AppArmor, PostgreSQL should start cleanly from the new path. Verify the migration with pg_lsclusters and psql, then keep the .bak directory until you confirm stable application behavior.

For related migration patterns, see the tutorial collection at How to Move a PostgreSQL Data Directory to a New Location.

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.