2026-06-24 · 7 min
Mongo vs Postgres JSONB di SaaS 2026: Pilih Mana
Klien startup Jakarta minta saya bantu pilih DB untuk SaaS baru: form builder + report generator dengan schema super flexible per tenant. “Mongo atau Postgres dengan JSONB?” Saya benchmark keduanya di sample workload nyata 2 minggu. Hasil terukur, plus context untuk decision.
Setup
Workload yang saya simulate:
- 1000 tenant, masing-masing punya 5-20 form unique schema
- 50,000 submission/hari total (rata-rata)
- Peak hour: 8000 submission/jam (lunch time enterprise customer)
- Query pattern: filter by tenant, by date range, by JSONB field value, plus aggregate
Mongo 7.0 setup:
- Self-hosted di Hetzner cx51 (8 vCPU, 32GB RAM)
- WiredTiger storage engine
- Replica set 1 primary + 1 secondary
- Indexes pada
tenantId,submittedAt, dan compound untuk common query
Postgres 17 setup:
- Self-hosted di Hetzner cx51 (same spec)
- Table
submissionsdengan column JSONB untuk dynamic field - GIN index pada JSONB column + B-tree pada tenantId, submittedAt
- Partial index untuk top 10% schema field paling sering di-query
Schema design
Mongo:
{
_id: ObjectId(...),
tenantId: "tenant_abc",
formId: "form_123",
submittedAt: ISODate(...),
data: {
customer_name: "Budi",
age: 34,
products: ["A", "B"],
custom_field_xyz: "value"
}
}
Postgres:
CREATE TABLE submissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL,
form_id TEXT NOT NULL,
submitted_at TIMESTAMPTZ DEFAULT NOW(),
data JSONB NOT NULL
);
CREATE INDEX idx_tenant_date ON submissions (tenant_id, submitted_at DESC);
CREATE INDEX idx_data_gin ON submissions USING gin (data);
CREATE INDEX idx_data_customer ON submissions ((data->>'customer_name'))
WHERE data ? 'customer_name';
Both flexible untuk dynamic schema.
Write throughput
Test: bulk insert 100,000 submission dengan 8 worker parallel.
Mongo:
- Sustained: 18,500 op/sec
- P99 latency: 28ms
Postgres:
- Sustained: 11,200 op/sec
- P99 latency: 42ms
Mongo ~65% lebih cepat untuk pure write. Source: Mongo write path lebih lean, no WAL synchronous commit yang Postgres punya.
Untuk klien saya yang peak 8000/jam (~2,2 op/sec): both vastly over-provisioned. Beda write perf tidak material.
Read latency
Query 1: simple tenant filter + date range, return 100 row.
// Mongo
db.submissions.find({tenantId: "x", submittedAt: {$gte: ...}}).limit(100)
// Postgres
SELECT * FROM submissions WHERE tenant_id = 'x' AND submitted_at >= '...' LIMIT 100;
- Mongo: P50 8ms, P95 22ms
- Postgres: P50 6ms, P95 18ms
Tie. Index hit at both.
Query 2: filter by nested JSON field.
// Mongo
db.submissions.find({tenantId: "x", "data.customer_name": "Budi"})
// Postgres dengan GIN
SELECT * FROM submissions WHERE tenant_id = 'x' AND data @> '{"customer_name": "Budi"}';
- Mongo: P50 14ms, P95 38ms
- Postgres GIN: P50 12ms, P95 28ms (partial index hit)
- Postgres tanpa partial index: P50 145ms (GIN scan slower)
Postgres dengan partial index pada field yang sering di-query menang. Tapi butuh schema awareness — define index per high-traffic field.
Query 3: aggregate (sum amount group by date).
// Mongo aggregation pipeline
db.submissions.aggregate([
{$match: {tenantId: "x"}},
{$group: {_id: {$dateToString: {...}}, total: {$sum: "$data.amount"}}}
])
// Postgres
SELECT date_trunc('day', submitted_at), SUM((data->>'amount')::numeric)
FROM submissions WHERE tenant_id = 'x' GROUP BY 1;
- Mongo: P50 380ms, P95 980ms
- Postgres: P50 190ms, P95 540ms
Postgres menang aggregate ~2x. SQL engine matang untuk OLAP-style.
Query power
Postgres menang besar di:
- JOIN dengan table lain (tenant, user, form schema)
- Window function (RANK, LAG, dll)
- CTE untuk complex query
- Full-text search native (
to_tsvector) - Vector similarity (pgvector — sama infrastructure)
Mongo menang di:
- Schema-on-read flexibility (truly schemaless)
- Aggregation pipeline syntax compact untuk transformation
- Native sharding (untuk scale > single node)
Untuk SaaS klien yang akan join submission dengan user, tenant, dan form metadata: Postgres clear menang. Cross-collection join di Mongo via $lookup slow + verbose.
Ops complexity
Mongo:
- Replica set setup: 1 hari
- Backup: mongodump / oplog tail / WiredTiger snapshot
- Schema migration: by convention (Mongoose), no DB-level enforcement
- Monitoring: mongostat + Prometheus exporter
Postgres:
- Replication setup: 1 hari (lihat Postgres 17 incremental backup saya)
- Backup: pg_basebackup + WAL archive
- Schema migration: explicit (drizzle-kit, etc) — DB-level enforce
- Monitoring: pg_stat_* views + Prometheus exporter
Both production-mature. Postgres ekosistem tooling sedikit lebih luas (matang sejak 1990-an).
Cost
Self-hosted Hetzner cx51 same spec: $35/mo. Equal.
Managed alternative:
- Mongo Atlas M30 (Singapore): $389/mo
- Supabase Pro: $25/mo (+ usage tier)
- Neon Scale: ~$100-200/mo depending volume
For klien startup with budget constraint: Supabase Postgres wins on cost vs managed Mongo.
Yang break (kalau pilih Mongo)
Saya pernah operate Mongo di project lain. Issue yang muncul:
-
Schema drift: tanpa enforcement, beberapa tenant accidentally write field naming inconsistent (
customerNamevscustomer_name). Query lapse. Sekarang saya pakai Mongoose dengan strict schema, defeating purpose dari schemaless to some extent. -
Aggregation memory limit: 100MB sort limit di-hit pernah saat report bulanan. Saya pakai
allowDiskUse: trueworkaround. Postgres tidak ada limit serupa di default config. -
Lookup performance: cross-collection join untuk report yang involve tenant + user + submission slow. Postgres native join lebih clean dan fast.
Yang break (Postgres JSONB)
-
Index maintenance: GIN index update lambat (saat write heavy). Saya pakai
fillfactor=80+ maintenance window untuk reindex. -
JSONB query syntax: lebih verbose dari Mongo dot-notation.
data->>'customer_name'vs"data.customer_name". Team butuh ramp-up untuk pattern. -
Schema migration JSONB: tidak ada schema-level enforcement untuk struktur JSONB. Saya pakai zod schema di app layer untuk validate sebelum write.
Verdict untuk klien startup
Saya rekomendasi Postgres JSONB. Reasoning:
- Query power untuk cross-data join (submission + user + form schema)
- Single-store untuk OLTP + OLAP + future vector use case (pgvector)
- Cost lebih affordable untuk startup phase (Supabase Pro vs Mongo Atlas)
- Ekosistem tooling matang (backup, replication, monitoring)
Klien setuju. Sekarang 4 bulan production, no regret.
Kapan saya pilih Mongo
Kalau workload:
- Sustained write > 50k op/sec (kasus IoT, log ingestion massive)
- Schema benar-benar unknown / change frequent
- Tim sudah mature Mongo expertise
- Native horizontal sharding requirement
Untuk most SaaS Indonesia: jarang hit threshold ini.
Bukan magic — pilih DB based on workload, bukan trend. Postgres + JSONB sudah cover 90%+ “saya butuh schema flexible” case di 2026.
Ditulis oleh Reza Pradipta