Skillversitas

PostgreSQL on a MicroVM

PostgreSQL is lighter than MySQL out of the box and has more advanced features. It is well suited to MicroVMs with limited resources.

Preparing the Image

# Mount the image
sudo mount -o loop rootfs.img /mnt/microvm

# Debian/Ubuntu
sudo chroot /mnt/microvm bash -c '
  apt update && apt install -y postgresql postgresql-client
'

# Alpine
sudo chroot /mnt/microvm bash -c '
  apk add postgresql postgresql-client
  mkdir -p /var/lib/postgresql/data
  chown postgres:postgres /var/lib/postgresql/data
  su - postgres -c "initdb -D /var/lib/postgresql/data"
'

sudo umount /mnt/microvm

Optimal Configuration

sudo mount -o loop rootfs.img /mnt/microvm

sudo tee /mnt/microvm/etc/postgresql/15/main/postgresql.conf << 'PGCONF'
# Memory
shared_buffers = 64MB
effective_cache_size = 128MB
work_mem = 2MB
maintenance_work_mem = 32MB

# Write Ahead Log
wal_buffers = 4MB
checkpoint_completion_target = 0.9
max_wal_size = 256MB
min_wal_size = 64MB

# Connections
max_connections = 50
superuser_reserved_connections = 3

# Query Planner
random_page_cost = 1.1
effective_io_concurrency = 200

# Logging
logging_collector = off
log_min_duration_statement = 1000

# Disable expensive features
ssl = off
PGCONF

sudo tee /mnt/microvm/etc/postgresql/15/main/pg_hba.conf << 'PGHBA'
# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             all                                     peer
host    all             all             0.0.0.0/0               md5
host    all             all             ::1/128                 md5
PGHBA

sudo umount /mnt/microvm

Running the MicroVM

qemu-system-x86_64 \
  -M microvm -enable-kvm -cpu host -m 512m -smp 2 \
  -kernel vmlinuz \
  -append "console=ttyS0 root=/dev/vda rw" \
  -drive id=rootfs,file=rootfs.img,format=raw,if=none,cache=none,aio=linux \
  -device virtio-blk-device,drive=rootfs \
  -netdev user,id=net0,hostfwd=tcp::5432-:5432 \
  -device virtio-net-device,netdev=net0 \
  -nographic -serial stdio

Setting Up the Guest

# Start PostgreSQL (Debian/Ubuntu)
service postgresql start

# Or Alpine
su - postgres -c "pg_ctl -D /var/lib/postgresql/data start"

# Create a user and a database
su - postgres -c "psql -c \"CREATE USER app_user WITH PASSWORD 'secure_password';\""
su - postgres -c "psql -c \"CREATE DATABASE app_db OWNER app_user;\""
su - postgres -c "psql -c \"GRANT ALL PRIVILEGES ON DATABASE app_db TO app_user;\""

# Test the connection
psql -h localhost -U app_user -d app_db

Auto Start (init.d Alpine)

sudo tee /mnt/microvm/etc/init.d/postgresql << 'PGINIT'
#!/sbin/openrc-run

PGDATA=/var/lib/postgresql/data
PGUSER=postgres

depend() {
    need net
    keyword timeout 300
}

start() {
    ebegin "Starting PostgreSQL"
    su - $PGUSER -c "pg_ctl -D $PGDATA -l /var/log/postgresql.log start"
    eend $?
}

stop() {
    ebegin "Stopping PostgreSQL"
    su - $PGUSER -c "pg_ctl -D $PGDATA stop -m fast"
    eend $?
}
PGINIT

sudo chmod +x /mnt/microvm/etc/init.d/postgresql
sudo chroot /mnt/microvm rc-update add postgresql default

Backup

# Logical backup
pg_dump -h localhost -U app_user app_db > backup.sql

# All databases
pg_dumpall -h localhost -U postgres > all_backup.sql

Performance Tuning

# Check connection statistics
psql -c "SELECT * FROM pg_stat_activity;"

# Check database sizes
psql -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database ORDER BY pg_database_size(pg_database.datname) DESC;"

# Check index usage
psql -c "SELECT schemaname, tablename, indexrelname, idx_scan FROM pg_stat_user_indexes ORDER BY idx_scan DESC;"
psql -d app_db -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
psql -d app_db -c "CREATE EXTENSION IF NOT EXISTS uuid-ossp;"
psql -d app_db -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;"

Troubleshooting

Cannot connect

# Check that PostgreSQL is running
ps aux | grep postgres

# Check the port
ss -tlnp | grep 5432

# Check pg_hba.conf
cat /etc/postgresql/15/main/pg_hba.conf

Out of memory

# Reduce shared_buffers
shared_buffers = 32MB
effective_cache_size = 64MB
work_mem = 1MB
maintenance_work_mem = 16MB

# Disable statistics collector
track_activities = off
track_counts = off