Hampir setiap aplikasi web butuh database. Dan PostgreSQL adalah pilihan terbaik untuk sebagian besar use case: reliable, feature-rich, performa tinggi, dan open source.
Di Helipod, deploy PostgreSQL semudah menambahkan service baru di canvas — tidak perlu konfigurasi manual, tidak perlu setup user dan permission dari nol, dan database langsung terhubung ke aplikasimu via internal network.
Cara Deploy PostgreSQL
Langkah 1: Tambah Service PostgreSQL
Di canvas dashboard Helipod:
- Klik tombol + untuk menambah service baru
- Pilih PostgreSQL dari daftar template
- Beri nama service — misalnya
postgresataudb - Klik Deploy
Helipod akan menjalankan container PostgreSQL dengan konfigurasi default yang sudah production-ready.
Langkah 2: Lihat Kredensial
Setelah PostgreSQL running, buka tab Variables pada service PostgreSQL. Kamu akan melihat:
POSTGRES_DB=mydb
POSTGRES_USER=postgres
POSTGRES_PASSWORD=auto-generated-password
DATABASE_URL=postgresql://postgres:password@postgres:5432/mydb
Catat nilai-nilai ini untuk dipakai di service aplikasimu.
Langkah 3: Hubungkan ke Aplikasi
Di service aplikasi (Laravel, Django, NestJS, dll), buka tab Variables dan tambahkan:
Laravel:
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=mydb
DB_USERNAME=postgres
DB_PASSWORD=auto-generated-password
Django / FastAPI / Flask:
DATABASE_URL=postgresql://postgres:password@postgres:5432/mydb
NestJS (TypeORM):
DB_HOST=postgres
DB_PORT=5432
DB_NAME=mydb
DB_USER=postgres
DB_PASSWORD=auto-generated-password
postgresdiDB_HOSTadalah nama service yang kamu beri saat membuat PostgreSQL di canvas. Ini langsung bisa dipakai sebagai hostname karena semua service dalam satu project terhubung via internal network.
Setup Persistent Volume untuk PostgreSQL
Ini langkah yang paling penting dan sering terlupakan.
Secara default, data PostgreSQL disimpan di ephemeral storage container — artinya semua data akan hilang jika container di-restart atau redeploy.
Untuk production, kamu wajib mount persistent volume:
- Buka service PostgreSQL → tab Settings
- Scroll ke Storage Volumes → Add Volume
- Isi:
- Mount Path:
/var/lib/postgresql/data - Size: 10GB (atau lebih sesuai kebutuhan)
- Mount Path:
- Klik Save
Atau via helipack.json (jika PostgreSQL dari custom image):
{
"volume": {
"mountPath": "/var/lib/postgresql/data",
"size": 20
}
}
Setelah volume di-mount, data PostgreSQL akan persist meski container di-restart atau di-redeploy.
⚠️ Warning: Jika kamu menambahkan volume setelah PostgreSQL sudah berjalan dan ada data, pastikan path mount benar. Mount ke path yang salah bisa menyebabkan PostgreSQL tidak bisa start (karena menemukan direktori kosong alih-alih data directory yang sudah ada).
Konfigurasi Database Pertama Kali
Setelah PostgreSQL running dan persistent volume terpasang, setup database dari terminal:
Akses psql dari Terminal Aplikasi
Buka terminal di service aplikasi (bukan PostgreSQL) dan jalankan:
# Test koneksi
psql $DATABASE_URL -c "SELECT version();"
# Atau dengan individual params
psql -h postgres -U postgres -d mydb
# List semua database
psql -h postgres -U postgres -l
# Buat database baru
createdb -h postgres -U postgres newdatabase
# Buat user baru
psql -h postgres -U postgres -c "CREATE USER myuser WITH PASSWORD 'mypassword';"
psql -h postgres -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;"
Atau Akses dari Terminal PostgreSQL langsung
Buka tab Terminal pada service PostgreSQL:
# Masuk ke psql
psql -U postgres
# Perintah psql yang berguna
\l -- list databases
\c mydb -- connect ke database
\dt -- list tables
\d tablename -- describe table
\du -- list users
\q -- quit
Jalankan Migrasi
Setelah database siap, jalankan migrasi dari terminal service aplikasi:
Laravel:
php artisan migrate --force
php artisan migrate:status
Django:
python manage.py migrate
python manage.py showmigrations
NestJS + TypeORM:
npx typeorm migration:run -d dist/data-source.js
NestJS + Prisma:
npx prisma migrate deploy
npx prisma db seed # opsional, untuk seed data
FastAPI + Alembic:
alembic upgrade head
Otomasi via helipack.json
Agar migrasi berjalan otomatis setiap deploy:
{
"run": {
"before": "php artisan migrate --force"
}
}
Konfigurasi PostgreSQL untuk Production
PostgreSQL default sudah cukup untuk kebanyakan use case. Tapi untuk optimasi lebih lanjut, kamu bisa override konfigurasi via environment variable di service PostgreSQL:
# Jumlah max connections
POSTGRES_MAX_CONNECTIONS=100
# Shared buffers (25% dari RAM yang tersedia)
# Untuk pod 1GB RAM:
POSTGRES_SHARED_BUFFERS=256MB
# Effective cache size (50-75% dari RAM)
POSTGRES_EFFECTIVE_CACHE_SIZE=512MB
# Work memory per sort operation
POSTGRES_WORK_MEM=4MB
# Maintenance work memory
POSTGRES_MAINTENANCE_WORK_MEM=64MB
Atau mount custom postgresql.conf sebagai Secret File.
Connection Pooling
Jika aplikasimu menjalankan banyak proses (gunicorn workers, replicas), setiap proses akan membuka koneksi ke PostgreSQL. Dengan max_connections=100 default, ini bisa menjadi bottleneck.
PgBouncer sebagai Connection Pooler
Deploy PgBouncer sebagai pod terpisah:
- Tambah service baru di canvas dengan Docker image
pgbouncer/pgbouncer - Beri nama
pgbouncer - Set environment variables:
DATABASE_URL=postgresql://postgres:password@postgres:5432/mydb
POOL_MODE=transaction
MAX_CLIENT_CONN=100
DEFAULT_POOL_SIZE=20
- Di service aplikasi, ubah
DB_HOSTdaripostgreskepgbouncer
Dengan PgBouncer, ratusan koneksi dari aplikasi akan di-multiplex menjadi puluhan koneksi aktual ke PostgreSQL — jauh lebih efisien.
Connection Pool di ORM
Sebagai alternatif PgBouncer, konfigurasi connection pool di ORM:
Laravel (config/database.php):
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST'),
// ...
'options' => [
PDO::ATTR_PERSISTENT => true,
],
'pool' => [
'min' => 2,
'max' => 10,
],
],
SQLAlchemy (Python):
engine = create_async_engine(
os.environ['DATABASE_URL'],
pool_size=10, # koneksi yang selalu ada
max_overflow=20, # koneksi extra saat load tinggi
pool_timeout=30, # waktu tunggu dapat koneksi (detik)
pool_recycle=3600, # recycle koneksi tiap 1 jam
)
TypeORM (NestJS):
TypeOrmModule.forRoot({
type: 'postgres',
// ...
extra: {
max: 10, // max pool size
min: 2, // min pool size
idleTimeoutMillis: 30000,
},
})
Backup PostgreSQL
Helipod belum menyediakan managed backup otomatis. Setup backup manual:
Backup via pg_dump dari Terminal
# Dari terminal service aplikasi
pg_dump $DATABASE_URL | gzip > backup_$(date +%Y%m%d_%H%M%S).sql.gz
# Upload ke object storage
# Dengan AWS CLI (untuk S3/DO Spaces/R2):
aws s3 cp backup_*.sql.gz s3://mybucket/postgres-backups/ \
--endpoint-url https://xxx.r2.cloudflarestorage.com
Cron Backup Otomatis
Deploy pod terpisah sebagai backup worker dengan schedule:
{
"run": {
"command": "sh -c 'while true; do pg_dump $DATABASE_URL | gzip | aws s3 cp - s3://mybucket/backup_$(date +%Y%m%d).sql.gz; sleep 86400; done'"
}
}
Restore dari Backup
# Restore ke database
gunzip -c backup_20260515.sql.gz | psql $DATABASE_URL
# Atau ke database baru
createdb -h postgres -U postgres newdb
gunzip -c backup_20260515.sql.gz | psql -h postgres -U postgres newdb
Monitoring PostgreSQL
Dari terminal service PostgreSQL atau aplikasi:
-- Cek koneksi aktif
SELECT count(*), state
FROM pg_stat_activity
GROUP BY state;
-- Cek query yang sedang berjalan
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY duration DESC;
-- Cek ukuran database
SELECT pg_size_pretty(pg_database_size('mydb'));
-- Cek ukuran tabel terbesar
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;
-- Cek index yang tidak pernah dipakai
SELECT relname, indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY relname;
Scaling PostgreSQL
Untuk PostgreSQL di Helipod, scale via Settings → Scaling & Resources:
RAM lebih penting dari CPU untuk database:
- 512MB RAM — aplikasi kecil, <10 concurrent users
- 1GB RAM — aplikasi medium, 10–100 concurrent users
- 2GB RAM — aplikasi production, 100–1000 concurrent users
- 4GB+ RAM — high-traffic production
PostgreSQL menggunakan RAM untuk shared_buffers (cache data) dan work_mem (sorting/joining). Lebih banyak RAM = lebih banyak data di-cache = query lebih cepat.
Catatan: PostgreSQL tidak bisa di-horizontal scale dengan cara biasa (tidak bisa multiple replicas yang semuanya write). Untuk read scaling, setup read replica secara manual.
Troubleshooting
"FATAL: role 'myuser' does not exist"
Buat user dari psql: CREATE USER myuser WITH PASSWORD 'password';
"Connection refused" dari aplikasi
Pastikan nama DB_HOST sama dengan nama service PostgreSQL di canvas. Cek dari terminal aplikasi: nc -zv postgres 5432.
"Too many connections"
Implementasikan connection pooling (PgBouncer atau pool di ORM). Atau naikkan max_connections di konfigurasi PostgreSQL.
Data hilang setelah restart
Persistent volume belum di-setup! Tambahkan volume mount ke /var/lib/postgresql/data segera.
"directory is not empty" saat pertama kali Ini terjadi jika kamu mount volume ke PostgreSQL yang sudah running. Coba reset volume atau pastikan directory kosong sebelum pertama kali mount.
Kesimpulan
PostgreSQL di Helipod bisa running dalam hitungan menit dengan konfigurasi yang sudah production-ready. Yang terpenting: selalu mount persistent volume ke /var/lib/postgresql/data sebelum mulai memasukkan data production.
Untuk koneksi dari aplikasi ke PostgreSQL, gunakan nama service sebagai hostname — tidak perlu IP, tidak perlu konfigurasi networking tambahan.
Ingin tahu cara koneksi multi-service di Helipod? Baca Internal Network Helipod.
Punya pertanyaan? Hubungi kami di support@helipod.id atau bergabung ke komunitas di hangar.helipod.io.