Migrating PostgreSQL to Pilvio
In this tutorial, we will migrate an existing PostgreSQL database to a Pilvio VM, set up automatic backups, and configure a firewall.
What we will build
- PostgreSQL 16 server on a Pilvio VM
- Data migration from an existing server
- Automatic backups to Pilvio StorageVault (S3)
- Firewall rules to protect the database
Prerequisites
- Pilvio account and API token (see overview)
- Access to an existing PostgreSQL server (if migrating data)
pg_dumpandpsqltools
Step 1: Creating a VM for the database
We recommend a dedicated VM with sufficient disk space for the database:
curl "https://api.pilvio.com/v1/user-resource/vm" \
-H "apikey: SINU_PILVIO_TOKEN" \
-X POST \
-d "name=postgresql-server" \
-d "os_name=ubuntu" \
-d "os_version=24.04" \
-d "vcpu=2" \
-d "ram=4096" \
-d "disks=50" \
-d "username=deploy" \
-d "password=TurvalineParool123!" \
-d "public_key=ssh-ed25519 AAAA... sinu@arvuti"
Additional disks for data (optional, recommended)
Separate database data from the system disk:
# Create a separate disk for data
curl "https://api.pilvio.com/v1/user-resource/vm/storage" \
-H "apikey: SINU_PILVIO_TOKEN" \
-X POST \
-d "uuid=SINU_VM_UUID" \
-d "size_gb=100"
Mount the disk on the server:
ssh deploy@SINU_FLOATING_IP
# Find the new disk device name (typically /dev/vdb)
lsblk
# Format and mount
sudo mkfs.ext4 /dev/vdb
sudo mkdir -p /mnt/pgdata
sudo mount /dev/vdb /mnt/pgdata
# Add to fstab for persistent mounting
echo '/dev/vdb /mnt/pgdata ext4 defaults 0 2' | sudo tee -a /etc/fstab
Step 2: Configuring the firewall
Allow the PostgreSQL port only from specific IPs (your application servers):
curl "https://api.pilvio.com/v1/network/firewall" \
-H "apikey: SINU_PILVIO_TOKEN" \
-H "Content-Type: application/json" \
-X POST \
--data '{
"name": "postgresql-fw",
"rules": [
{
"protocol": "tcp",
"direction": "inbound",
"port_start": 22,
"port_end": 22,
"endpoint_spec_type": "any"
},
{
"protocol": "tcp",
"direction": "inbound",
"port_start": 5432,
"port_end": 5432,
"endpoint_spec_type": "ip_prefixes",
"endpoint_spec": ["SINU_APP_SERVERI_IP/32", "10.0.0.0/8"]
}
]
}'
Security: Do not open the PostgreSQL port to the entire internet. Use the Pilvio private network for communication between VMs.
Step 3: Installing and configuring PostgreSQL
ssh deploy@SINU_FLOATING_IP
# Install PostgreSQL 16
sudo apt-get update
sudo apt-get install -y postgresql-16 postgresql-client-16
# Stop the service for configuration
sudo systemctl stop postgresql
Data directory on a separate disk (if you created an additional disk in Step 1)
sudo chown postgres:postgres /mnt/pgdata
sudo -u postgres /usr/lib/postgresql/16/bin/initdb -D /mnt/pgdata/16/main
# Update configuration
sudo sed -i "s|data_directory = '.*'|data_directory = '/mnt/pgdata/16/main'|" \
/etc/postgresql/16/main/postgresql.conf
Configuring PostgreSQL
# Allow external connections
sudo sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/" \
/etc/postgresql/16/main/postgresql.conf
# Performance tuning (for a 4 GB RAM VM)
sudo tee -a /etc/postgresql/16/main/postgresql.conf <<'EOF'
# Pilvio jõudluse seadistused
shared_buffers = 1GB
effective_cache_size = 3GB
maintenance_work_mem = 256MB
work_mem = 16MB
wal_buffers = 16MB
max_connections = 100
random_page_cost = 1.1
effective_io_concurrency = 200
EOF
# Authentication — allow connections from your network
echo "host all app_user 10.0.0.0/8 scram-sha-256" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf
echo "host all app_user SINU_APP_SERVERI_IP/32 scram-sha-256" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf
# Start
sudo systemctl start postgresql
Creating user and database
sudo -u postgres psql <<'SQL'
CREATE USER app_user WITH PASSWORD 'tugev-parool-siia';
CREATE DATABASE myapp OWNER app_user;
GRANT ALL PRIVILEGES ON DATABASE myapp TO app_user;
SQL
Step 4: Migrating data
Small database (< 1 GB): pg_dump/pg_restore
# From the old server: export
pg_dump -h vana-server.ee -U vana_kasutaja -d vana_andmebaas \
-Fc -f /tmp/dump.backup
# Copy to Pilvio server
scp /tmp/dump.backup deploy@SINU_FLOATING_IP:/tmp/
# On Pilvio server: import
ssh deploy@SINU_FLOATING_IP
sudo -u postgres pg_restore -d myapp /tmp/dump.backup
Large database (> 1 GB): streaming
# Directly from old server to Pilvio server
pg_dump -h vana-server.ee -U vana_kasutaja -d vana_andmebaas -Fc | \
ssh deploy@SINU_FLOATING_IP "sudo -u postgres pg_restore -d myapp"
Schema only (without data)
pg_dump -h vana-server.ee -U vana_kasutaja -d vana_andmebaas \
--schema-only -f schema.sql
scp schema.sql deploy@SINU_FLOATING_IP:/tmp/
ssh deploy@SINU_FLOATING_IP "sudo -u postgres psql myapp < /tmp/schema.sql"
Step 5: Automatic backups to StorageVault (S3)
Create the file /home/deploy/backup-pg.sh:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/tmp/pg-backups"
BUCKET="minu-pg-backups"
S3_ENDPOINT="https://s3.pilvio.com:8080"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/myapp-${DATE}.sql.gz"
mkdir -p "$BACKUP_DIR"
# Dump ja tihendamine
sudo -u postgres pg_dump myapp | gzip > "$BACKUP_FILE"
# Üleslaadimine StorageVault'i
aws s3 cp "$BACKUP_FILE" "s3://${BUCKET}/postgresql/${DATE}.sql.gz" \
--endpoint-url "$S3_ENDPOINT"
# Kohalike failide puhastamine (hoia viimased 3)
ls -t ${BACKUP_DIR}/myapp-*.sql.gz | tail -n +4 | xargs rm -f
# Kustuta S3-st vanemad kui 30 päeva
aws s3 ls "s3://${BUCKET}/postgresql/" --endpoint-url "$S3_ENDPOINT" | \
awk '{print $4}' | while read -r file; do
file_date=$(echo "$file" | grep -oP '\d{8}')
if [[ $(date -d "$file_date" +%s) -lt $(date -d '30 days ago' +%s) ]]; then
aws s3 rm "s3://${BUCKET}/postgresql/$file" --endpoint-url "$S3_ENDPOINT"
fi
done
echo "Varundamine lõpetatud: $BACKUP_FILE"
chmod +x /home/deploy/backup-pg.sh
# Cron: every day at 3:00
(crontab -l 2>/dev/null; echo "0 3 * * * /home/deploy/backup-pg.sh >> /var/log/pg-backup.log 2>&1") | crontab -
Pilvio automatic VM backup (additionally)
# Enable Pilvio VM automatic backups
curl "https://api.pilvio.com/v1/user-resource/vm/backup" \
-H "apikey: SINU_PILVIO_TOKEN" \
-X POST \
-d "uuid=SINU_VM_UUID"
Step 6: Restoring from a backup
# Download backup from StorageVault
aws s3 cp s3://minu-pg-backups/postgresql/20250211-030000.sql.gz /tmp/ \
--endpoint-url https://s3.pilvio.com:8080
# Restore
gunzip -c /tmp/20250211-030000.sql.gz | sudo -u postgres psql myapp
Connecting to the application
# Connection string (use private IP if on the same network)
postgresql://app_user:tugev-parool@10.x.x.x:5432/myapp
# Or via Floating IP (slower, firewall must allow it)
postgresql://app_user:tugev-parool@SINU_FLOATING_IP:5432/myapp
Next steps: Connect PostgreSQL to your Node.js or FastAPI backend.