Migrating the Pyramid Database from PostgreSQL 9.6 to PostgreSQL 18 (Linux)
CRITICAL: Required Before Upgrading to Pyramid 2025.11 or Later
IMPORTANT: This migration from PostgreSQL 9.6 to PostgreSQL 18 MUST be completed BEFORE upgrading Pyramid to version 2025.11 or any later version. If you attempt to upgrade Pyramid without completing this database migration first, it will fail.
This article only applies to you, if you installed Pyramid using the "New Internal Repository" option.https://help.pyramidanalytics.com/Content/Root/Guides/installation/Main/Database%20Repository.htm
Migrate → verify for 24 hours → then upgrade Pyramid.
Supported platforms: Ubuntu, Debian, RHEL, Oracle Linux, Amazon Linux.
Before You Start
Pyramid’s bundled PostgreSQL always runs on port 12130.
Two values are specific to your install — you get both in Step 5:
| Placeholder | Replace with |
| <YOUR_DATABASE> | Your database name from config.ini |
| <YOUR_PASSWORD> | The password you derive in Step 5 |
The database service is pyramidPG.service. Stopping it before the backup is the most common way to break this migration.
See what’s on your box:
systemctl list-units --type=service --all | grep -i pyramid
Paths by Platform
| Item | Ubuntu / Debian | RHEL / Oracle / Amazon |
| pg_hba.conf | /etc/postgresql/18/main/pg_hba.conf | /var/lib/pgsql/18/data/pg_hba.conf |
| postgresql.conf | /etc/postgresql/18/main/postgresql.conf | /var/lib/pgsql/18/data/postgresql.conf |
By default Pyramid installs to /opt/Pyramid with config.ini inside it.
Prerequisites
- sudo access
- 2x the current database size free on the backup filesystem
- Pyramid offline during the migration
Step 1: Install PostgreSQL 18
Ubuntu / Debian
sudo apt install -y curl ca-certificates
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
--fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
. /etc/os-release
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \
https://apt.postgresql.org/pub/repos/apt ${VERSION_CODENAME}-pgdg main" \
| sudo tee /etc/apt/sources.list.d/pgdg.list
sudo apt update
sudo apt install -y postgresql-18 postgresql-client-18
Initialises and starts automatically — no initdb.
RHEL / Oracle Linux
sudo dnf install -y \
https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql
sudo dnf install -y postgresql18-server postgresql18-contrib
sudo /usr/pgsql-18/bin/postgresql-18-setup initdb
sudo systemctl enable --now postgresql-18
If Oracle’s own PostgreSQL packages conflict:
sudo dnf install -y postgresql18-server --disablerepo=ol\* --enablerepo=pgdg18
Amazon Linux 2023
rpm -E %{rhel} doesn’t resolve here, so pin EL9:
sudo dnf install -y \
https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql 2>/dev/null || true
sudo dnf install -y postgresql18-server postgresql18-contrib
sudo /usr/pgsql-18/bin/postgresql-18-setup initdb
sudo systemctl enable --now postgresql-18
GPG key complaints:
sudo rpm --import https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL
Verify
sudo -u postgres psql -c "SELECT version();"
Do Step 2 before anything else.
Step 2: Configure PostgreSQL 18
2a. postgresql.conf
Ubuntu / Debian:
sudo nano /etc/postgresql/18/main/postgresql.conf
RHEL / Oracle / Amazon:
sudo nano /var/lib/pgsql/18/data/postgresql.conf
In the CONNECTIONS AND AUTHENTICATION section:
listen_addresses ships commented out, which means it defaults to localhost and refuses remote connections. Remove the # and set:
listen_addresses = '*'
Single-server installs can leave this at the default. Multi-server must change it.
port should already read:
port = 5432
max_connections — 100 is too low for Pyramid’s service pools:
max_connections = 1000
2b. pg_hba.conf
Ubuntu / Debian:
sudo nano /etc/postgresql/18/main/pg_hba.conf
RHEL / Oracle / Amazon:
sudo nano /var/lib/pgsql/18/data/pg_hba.conf
Pick one based on your security requirements.
Allow all (simplest, least secure):
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all 0.0.0.0/0 scram-sha-256
host all all ::1/128 scram-sha-256
host all all ::/0 scram-sha-256
Allow a subnet — replace with your own:
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all 192.168.1.0/24 scram-sha-256
host all all ::1/128 scram-sha-256
Keep the local ... peer line — later steps need it.
2c. Restart and verify
Ubuntu / Debian:
sudo systemctl restart postgresql@18-main
RHEL / Oracle / Amazon:
sudo systemctl restart postgresql-18
Then:
sudo -u postgres psql -c "SHOW listen_addresses;"
sudo -u postgres psql -c "SHOW max_connections;"
sudo ss -lntp | grep 5432
2d. Firewall
Ubuntu / Debian — replace with your subnet:
sudo ufw allow from 192.168.1.0/24 to any port 5432 proto tcp
RHEL / Oracle Linux:
sudo firewall-cmd --permanent --add-port=5432/tcp
sudo firewall-cmd --reload
Amazon Linux / EC2 has no local firewall by default — add an inbound TCP 5432 rule to the instance’s Security Group.
RHEL / Oracle with SELinux issues:
sudo ausearch -m avc -ts recent
sudo setsebool -P httpd_can_network_connect_db on
Step 3: Create the Backup Directory
Own it yourself, or later steps hit permission errors:
sudo mkdir -p /var/backups/pyramid
sudo chown -R $(whoami):$(whoami) /var/backups/pyramid
chmod 700 /var/backups/pyramid
ls -ld /var/backups/pyramid
The output should show your username, not root. If you created it root-owned earlier, the chown -R above fixes it.
Step 4: Confirm the Pyramid Path
ls -l /opt/Pyramid/config.ini
If it’s elsewhere:
sudo find / -name config.ini -path '*yramid*' 2>/dev/null
Step 5: Get Your Database Name and Password
sudo sed -n '/\[data\]/,/^\[/p' /opt/Pyramid/config.ini
Output looks like:
[data]
password=encG_0_KsIojX/E13v+ylAocWuRpaTALBicK864V7YWlmGCU/UuCPGd5n/ZpTMI/y4=
type=postgres
port=12130
username=pyramid
database=pyramidb3e0d8e8
dbhost=localhost
The username is always pyramid, the port always 12130. You need the database= value.
Derive the password. The config.ini value is encrypted, but the real password is built from the database name — you are not decrypting anything. The rule is pyramid! plus everything after pyramid in the database name:
database name: pyramidb3e0d8e8
password: pyramid!b3e0d8e8
Print your actual values:
DB=$(sudo sed -n '/\[data\]/,/^\[/p' /opt/Pyramid/config.ini | grep '^database=' | cut -d= -f2)
if [[ "$DB" == pyramid* ]]; then
echo "Database name: $DB"
echo "Password: pyramid!${DB#pyramid}"
else
echo "Database '$DB' does not start with 'pyramid' - the rule does not apply."
echo "Get the password from whoever built this environment."
fi
Verify them:
PGPASSWORD='<YOUR_PASSWORD>' psql -h 127.0.0.1 -p 12130 -U pyramid \
-d <YOUR_DATABASE> -c "\dt" | head
You should see a table list. If not, stop and fix it — the backup will fail the same way.
Use 127.0.0.1, not localhost. On some hosts localhost resolves to IPv6 first and the bundled PostgreSQL isn’t bound to it.
Step 6: Check Disk Space
df -h /var/backups/pyramid
PGPASSWORD='<YOUR_PASSWORD>' psql -h 127.0.0.1 -p 12130 -U pyramid -d postgres \
-c "SELECT pg_size_pretty(pg_database_size('<YOUR_DATABASE>'));"
Step 7: Stop the Pyramid Services — Leave pyramidPG Running
pyramidPG is the source database. The backup fails without it.
This stops every Pyramid service except the database, and skips anything not installed on this server:
for u in $(systemctl list-unit-files --type=service --no-legend \
| awk '{print $1}' | grep -i pyramid | grep -vi pyramidPG); do
echo "Stopping $u"
sudo systemctl stop "$u"
done
Confirm the database is still up — this must print active:
systemctl is-active pyramidPG.service
If it prints inactive:
sudo systemctl start pyramidPG.service
sleep 5
systemctl is-active pyramidPG.service
Step 8: Back Up config.ini
cp -a /opt/Pyramid/config.ini \
/var/backups/pyramid/config.ini.backup_$(date +%Y%m%d_%H%M%S)
Step 9: Back Up the Database. -- replace 'your_password' and 'yourdatabase' with your details.
PostgreSQL 18’s pg_dump can dump from a 9.6 server, so no 9.6 binaries are needed.
Do not use sudo — it creates a root-owned file that breaks Step 12.
Ubuntu / Debian
BACKUP=/var/backups/pyramid/pyramid_backup_$(date +%Y%m%d_%H%M%S).backup
PGPASSWORD='<YOUR_PASSWORD>' /usr/lib/postgresql/18/bin/pg_dump \
-h 127.0.0.1 -p 12130 -U pyramid \
-Fc -b -v -f "$BACKUP" <YOUR_DATABASE>
ls -lh "$BACKUP"
RHEL / Oracle Linux / Amazon Linux
BACKUP=/var/backups/pyramid/pyramid_backup_$(date +%Y%m%d_%H%M%S).backup
PGPASSWORD='<YOUR_PASSWORD>' /usr/pgsql-18/bin/pg_dump \
-h 127.0.0.1 -p 12130 -U pyramid \
-Fc -b -v -f "$BACKUP" <YOUR_DATABASE>
ls -lh "$BACKUP"
-Fc custom format (required for pg_restore), -b include blobs, -v verbose, -f output file. The database name goes at the end with no flag — that’s normal for pg_dump.
Verify before continuing. ls -lh must show a real size owned by your user. If it’s 0 bytes or missing, the dump failed — do not go to Step 10.
In a new shell, recover the variable with:
BACKUP=$(ls -t /var/backups/pyramid/*.backup | head -1)
Step 10: Disable pyramidPG
sudo systemctl disable --now pyramidPG.service
systemctl is-enabled pyramidPG.service
Should print disabled.
Step 11: Create the pyramid Role in PostgreSQL 18
sudo -u postgres psql -c "CREATE USER pyramid WITH PASSWORD '<YOUR_PASSWORD>';"
sudo -u postgres psql -c "ALTER USER pyramid WITH SUPERUSER CREATEDB LOGIN;"
If the role already exists, use ALTER USER pyramid WITH PASSWORD '<YOUR_PASSWORD>'; instead of CREATE.
Step 12: Restore into PostgreSQL 18
Create the database — the name must match the original exactly:
sudo -u postgres psql -c 'CREATE DATABASE "<YOUR_DATABASE>" OWNER pyramid;'
Restore as pyramid over TCP, not as the postgres OS user. The postgres user can’t read into your 700 backup directory; connecting over TCP means the file is read with your permissions.
Ubuntu / Debian
PGPASSWORD='<YOUR_PASSWORD>' /usr/lib/postgresql/18/bin/pg_restore \
-h 127.0.0.1 -p 5432 -U pyramid \
-d <YOUR_DATABASE> --exit-on-error -v "$BACKUP"
RHEL / Oracle Linux / Amazon Linux
PGPASSWORD='<YOUR_PASSWORD>' /usr/pgsql-18/bin/pg_restore \
-h 127.0.0.1 -p 5432 -U pyramid \
-d <YOUR_DATABASE> --exit-on-error -v "$BACKUP"
--exit-on-error matters — without it pg_restore exits successfully even when every object failed.
To start over:
sudo -u postgres psql -c 'DROP DATABASE "<YOUR_DATABASE>";'
sudo -u postgres psql -c 'CREATE DATABASE "<YOUR_DATABASE>" OWNER pyramid;'
Step 13: Verify the Restore
All three must pass before you touch config.ini.
Table count must be greater than zero:
sudo -u postgres psql -d <YOUR_DATABASE> -t -A \
-c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';"
Size should be comparable to the original:
sudo -u postgres psql -t -A \
-c "SELECT pg_size_pretty(pg_database_size('<YOUR_DATABASE>'));"
This is how Pyramid itself will connect:
PGPASSWORD='<YOUR_PASSWORD>' psql -h 127.0.0.1 -p 5432 \
-U pyramid -d <YOUR_DATABASE> -c "\dt" | head
config.ini is still untouched at this point, so Pyramid can be brought back on the old database if anything’s wrong.
Step 14: Update config.ini
Change only the port:
sudo sed -i 's/^port=12130$/port=5432/' /opt/Pyramid/config.ini
sudo grep '^port=' /opt/Pyramid/config.ini
Leave everything else alone, including the encrypted password= field.
Step 15: Start the Pyramid Services
pyramidPG stays disabled.
for u in $(systemctl list-unit-files --type=service --no-legend \
| awk '{print $1}' | grep -i pyramid | grep -vi pyramidPG); do
echo "Starting $u"
sudo systemctl start "$u"
sleep 3
done
systemctl list-units --type=service --all | grep -i pyramid
Wait 60–90 seconds. Investigate anything that failed:
journalctl -u <unit-name> -n 50 --no-pager
Step 16: Update the Data Source in Admin Hub
- Admin → Data → Data Source
- Select Local PG
- Change Port from 12130 to 5432
- Test — wait for the green check
- Apply
Step 17: Other Servers in the Cluster
On each remaining Pyramid server, stop the services, change the port, start them again:
for u in $(systemctl list-unit-files --type=service --no-legend \
| awk '{print $1}' | grep -i pyramid | grep -vi pyramidPG); do
sudo systemctl stop "$u"
done
sudo sed -i 's/^port=12130$/port=5432/' /opt/Pyramid/config.ini
sudo grep -E '^(port|dbhost)=' /opt/Pyramid/config.ini
for u in $(systemctl list-unit-files --type=service --no-legend \
| awk '{print $1}' | grep -i pyramid | grep -vi pyramidPG); do
sudo systemctl start "$u"
sleep 3
done
Check that dbhost= points at the server running PostgreSQL 18 — not localhost.
Step 18: Verify
Log into Pyramid and confirm that it is working as it should.
Step 19: Upgrade Pyramid
After 24 hours of clean operation, proceed with the 2025.11+ upgrade.
Rollback
Checklist
- PostgreSQL 18 installed (RHEL / Oracle / Amazon: initdb run)
- listen_addresses uncommented and set (multi-server)
- max_connections = 300
- pg_hba.conf configured, local all all peer kept
- PG18 restarted, settings confirmed with SHOW
- Firewall / Security Group allows 5432
- Backup directory owned by your user, not root
- Database name and password verified against port 12130
- config.ini backed up
- All Pyramid services stopped except pyramidPG
- systemctl is-active pyramidPG.service printed active before the backup
- pg_dump run without sudo, file has a real size
- pyramidPG disabled after the backup
- Restore run over TCP with --exit-on-error
- Table count greater than zero
- pyramid connects over TCP on 5432
- config.ini port updated on every server
- Admin Hub → Local PG on 5432, Test passes
- 24 hours clean before upgrading Pyramid
Leave pyramidPG disabled — it costs nothing and keeps rollback available.
Reply
Content aside
- 3 hrs agoLast active
- 21Views
- 1 Following