Metabase with PostgreSQL: Setup, Sizing & Cost Guide
Setup Metabase with PostgreSQL for production. Learn sizing, backup, maintenance, and true cost of self-hosting vs managed hosting. EU-hosted managed option available.
- Metabase's application database stores configuration and dashboards, not your data warehouse
- PostgreSQL 14+ is production-recommended over H2 (embedded) and MySQL for reliability and tooling
- Setup requires environment variables (JAR) or Docker Compose; migration from H2 takes 30 minutes
- Small teams (5 concurrent users) need 1 core + 2GB RAM for PostgreSQL; costs scale with concurrent users, not team size
- Self-hosting requires daily backups and weekly maintenance (VACUUM, monitoring) totaling 150-200 labor hours annually
Metabase's application database stores all configuration, dashboards, alerts, and user data. It is not your data warehouse: it is Metabase's own metadata layer. PostgreSQL is the recommended production choice over H2 (embedded, default) or MySQL. This guide walks you through setup, sizing for your team, and the real operational cost of self-hosting vs managed alternatives.
What Is Metabase's Application Database?
The application database is Metabase's internal store, separate from the data sources you connect to for analytics. It holds saved questions, dashboards, user accounts, permissions, alert definitions, and query caches. H2 is the default: a lightweight embedded database bundled with Metabase. For production use, Metabase recommends migrating to PostgreSQL, MySQL, or MariaDB because H2 is not designed for concurrent access or persistence across upgrades.
Your application database must be reliable. If it goes down, your entire Metabase instance is unavailable. If it corrupts, you lose dashboard definitions and user permissions. This is why sizing and backup discipline matter, even for small teams.
The application database does not store the data you analyze. That data lives in your data warehouse, data lake, or transactional database. The application database is Metabase's operating system.
Why PostgreSQL Over H2 and MySQL?
PostgreSQL is Metabase's recommended choice. Here is why:
H2 is not suitable for production use. H2 is embedded and handles single connections well, but concurrent requests cause locks and contention. Users experience dashboard timeouts. Upgrades risk data loss if H2 shuts down unexpectedly mid-transaction. H2 is fine for testing, never for a production instance serving more than one user.
PostgreSQL is the safest choice. Metabase documents PostgreSQL as production-ready and tested at scale. PostgreSQL 14 or later is required. PostgreSQL offers ACID compliance, tablespace management for performance tuning, and mature backup tooling (pg_dump, WAL archiving). Recovery from corruption is straightforward. PostgreSQL scales predictably: you know what to monitor and what to upgrade.
MySQL/MariaDB works but adds complexity. MySQL is supported, and MariaDB (the open-source fork) is cheaper to run in some cloud environments. But MySQL has fewer tools for advanced backup strategies and disaster recovery than PostgreSQL. Metabase's own documentation does not provide as many MySQL production examples. If your team is already MySQL-proficient, it works. If you are starting fresh, PostgreSQL is the lower-risk choice.
The operational difference is subtle but real: PostgreSQL has better tooling, more Metabase documentation, and simpler recovery procedures. For a business depending on dashboards, that difference matters.
Step-by-Step PostgreSQL Setup: JAR and Docker
JAR Deployment
If you run Metabase as a JAR file on a server, configure PostgreSQL via environment variables:
- Create a PostgreSQL database:
createdb metabase_app - Set environment variables before starting the JAR:
MB_DB_TYPE=postgresMB_DB_HOST=your-postgres-server.comMB_DB_PORT=5432MB_DB_USER=metabase_userMB_DB_PASS=secure_passwordMB_DB_DBNAME=metabase_app
- Start Metabase:
java -jar metabase.jar - Verify connection by checking logs:
tail -f /var/log/metabase/metabase.log
Alternatively, use a JDBC connection string for complex auth scenarios:
MB_DB_CONNECTION_STRING=jdbc:postgresql://your-postgres-server.com:5432/metabase_app?user=metabase_user&password=secure_password
Docker Deployment
For containerized setups, add environment variables to your docker-compose.yml:
version: '3.8'
services:
metabase:
image: metabase/metabase:v63.18
ports:
- "3000:3000"
environment:
MB_DB_TYPE: postgres
MB_DB_HOST: postgres-db
MB_DB_PORT: 5432
MB_DB_USER: metabase_user
MB_DB_PASS: secure_password
MB_DB_DBNAME: metabase_app
depends_on:
- postgres-db
postgres-db:
image: postgres:16
environment:
POSTGRES_DB: metabase_app
POSTGRES_USER: metabase_user
POSTGRES_PASSWORD: secure_password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Start the stack: docker-compose up -d. Metabase will initialize the schema on first startup. Verify by accessing your local Metabase instance at localhost:3000.
Both deployments require the same PostgreSQL setup. The difference is how you pass credentials: environment variables for JAR, docker-compose services for containers.
Sizing Your PostgreSQL App Database for Your Team
Metabase provides baseline sizing in their production documentation. Here is how it scales:
Baseline infrastructure (1-5 concurrent users):
- PostgreSQL: 1 CPU core, 2GB RAM
- Metabase server: 1 CPU core, 1GB RAM
- Storage: 10-20GB initially (questions, dashboards, query caches)
Per 20 additional concurrent Metabase users:
- Metabase server: +1 CPU core, +2GB RAM
Per 40 additional concurrent PostgreSQL users:
- PostgreSQL: +1 CPU core, +1GB RAM
- Storage: +5-10GB (depends on query caching and history)
Worked Examples
Small team (5 team members, 2-3 concurrent): 1 core + 2GB RAM for PostgreSQL is sufficient. Monthly cost on AWS t3.small or Hetzner VPS: approximately USD 10-15.
Growing team (20 team members, 8-10 concurrent): Scale to 2 cores + 4GB RAM. Concurrent user growth drives PostgreSQL sizing more than team size. Cost: approximately USD 30-50/month.
Large team (100+ team members, 30-40 concurrent): 3-4 cores + 8GB RAM. Add replicas for failover. Cost: approximately USD 150-250/month depending on cloud provider and redundancy.
Don't over-provision. Metabase application databases rarely max out CPU until you hit 50+ concurrent users. Monitor actual usage for 4 weeks before upgrading.
The Operational Burden: Backups, Maintenance & Monitoring
Self-hosting PostgreSQL means you own the operational tasks. This is the largest hidden cost of self-hosted Metabase.
Daily backups are mandatory. Use pg_dump or WAL archiving. A simple cron job:
0 2 * * * pg_dump -h localhost -U metabase_user metabase_app | gzip > /backups/metabase_$(date +\%Y-\%m-\%d).sql.gz
Run this every night. Test restoration monthly. A corrupted backup is worse than no backup.
Weekly maintenance tasks:
VACUUM ANALYZE metabase_app;to reclaim space and update query planner stats.- Check disk usage:
SELECT pg_database_size('metabase_app'); - Verify replica lag if you have standby servers.
Monitoring required:
- Connection count:
SELECT count(*) FROM pg_stat_activity;Alert if > 80% of max_connections. - Disk usage: Alert if > 80% full.
- Replication lag: If using streaming replication, monitor
pg_stat_replication. - Query performance: Log slow queries with
log_min_duration_statement = 1000(in milliseconds).
A single person can manage this for a small instance. A team with 5+ admins needs a dedicated database engineer on call for incidents. Connection pooling software (pgBouncer) becomes necessary around 50 concurrent users to prevent connection exhaustion.
Recovery procedures must be documented. Know exactly how you will restore from backup, how long it takes, and test quarterly. Recovery time objective (RTO) for most teams is 2-4 hours. If your dashboards are critical to operations, you need redundancy (primary + hot standby), not just backups.
What Self-Hosting Actually Costs: Infrastructure + Labor
Official Metabase documentation provides sizing and backup guidance but skips the total cost calculation. Here is the honest accounting:
Monthly infrastructure (small team, 5 concurrent users):
- PostgreSQL VPS: USD 15-25 (Hetzner, Linode, DigitalOcean)
- Metabase server: USD 10-20
- Storage/backup: USD 5-10
- Total: USD 30-55/month
Annual labor (self-hosted, hands-on):
- Backup validation and recovery testing: 4 hours/month = 48 hours/year
- Maintenance (VACUUM, monitoring setup, upgrades): 8 hours/month = 96 hours/year
- Incident response (corruption, connection issues): 4-10 hours/month depending on reliability culture
- Total: 150-200 hours/year
At USD 75/hour average cost for a technical person, that is USD 11,250-15,000 annually in labor.
Total first-year cost: approximately USD 11,500-15,500 (infrastructure + labor).
For a 10-person company, that is USD 1,150-1,550 per person annually. That calculation changes if your ops person is already salaried and has spare capacity, but the hours are still real and displace other work.
Managed PostgreSQL services (AWS RDS, Google Cloud SQL, Azure Database) reduce operational hours by 60-80%, but cost more: approximately USD 80-150/month for similar capacity. You trade labor for higher cloud bills.
When to Migrate to Managed Hosting
Self-hosting is a valid choice if you have all four:
- A person on staff with PostgreSQL experience.
- Time allocated for 10-15 hours monthly for maintenance.
- Comfort with backup restoration procedures.
- A recovery procedure tested in the last 90 days.
Migrate to managed hosting when:
- Your dashboards are core to business operations and downtime costs more than the hosting fee.
- Your team has no person dedicated to database operations.
- You cannot find 10+ hours monthly for maintenance without sacrificing other work.
- You need failover and high availability but lack the infrastructure to build it.
- Your security policy requires daily encrypted backups and point-in-time recovery.
Opsily's managed Metabase hosting handles all operational burden: daily backups, weekly maintenance, uptime monitoring, and disaster recovery in EU data centers with GDPR compliance. Cost is approximately USD 200-400/month depending on scale, which eliminates the labor cost entirely. For teams without a database engineer, it is often the lower total cost of ownership.
Compare self-hosted costs against managed Metabase alternatives before deciding. For a 20-person team, the math often favors managed if uptime matters.
Troubleshooting Common Issues
Connection timeouts: Verify network access to PostgreSQL. Check pg_stat_activity for idle connections. Enable connection pooling (pgBouncer) if connections exceed 30.
Slow dashboard loads: Query Metabase's caching layer first. Then check PostgreSQL slow query log. Run ANALYZE to refresh table statistics.
Corruption after power loss: Restore from backup (daily backup should exist). Verify backup integrity before deletion. Consider uninterruptible power supply (UPS) for both Metabase and PostgreSQL servers if reliability is critical.
Migration from H2: Use Metabase's built-in migration tool in Settings > Databases. It copies all configuration to the new PostgreSQL database. Test on a copy of production data first.
Backup restore fails: Practice monthly. Know which backup was last tested. Maintain a disaster recovery runbook specific to your deployment.
Frequently Asked Questions
Can I change my application database after launch?
Yes. Metabase provides migration tools to move from H2 to PostgreSQL or from one PostgreSQL instance to another. The process copies all dashboards, questions, and user accounts. Plan 30 minutes of downtime during the migration. Always back up before starting.
How often must I back up my application database?
Daily at minimum. If your dashboards represent recent business decisions, back up multiple times daily. Test one backup monthly by restoring to a test instance. A backup you have not restored is not a backup.
What happens if the application database goes down?
Metabase is unavailable until the database recovers. No dashboards load. Users cannot access saved questions. If the failure is filesystem corruption, recovery depends on your backup strategy. With hourly backups, you lose at most one hour of configuration changes. Design your SLA around recovery time objective (RTO).
Is PostgreSQL in the cloud (AWS RDS, Cloud SQL) recommended?
Yes, if you can afford it. Cloud-managed PostgreSQL eliminates most operational burden and provides automatic failover. Costs are higher (USD 80-150/month) but labor is lower. Trade-off: you lose direct access to advanced tuning options but gain reliability.
Do I need a separate PostgreSQL server or can I run it on the same machine as Metabase?
Both work. Running on separate machines is safer: if one crashes, the other survives. Running on the same machine (containerized together) is simpler for small teams. Decision point: if you have more than 20 concurrent users, separate servers reduce contention and improve responsiveness.
What data does the application database store?
Dashboard definitions, saved questions, user accounts, permissions, alerts, subscriptions, and query caches. It does not store raw data from your data warehouse. Your data warehouse is a separate connection.
How do I monitor PostgreSQL health while Metabase runs?
Use native PostgreSQL tools: pg_stat_activity for connections, pg_stat_database for I/O, pg_stat_user_tables for table growth. Export these as Prometheus metrics and scrape them in Grafana. Alerting libraries exist for OpenMetrics-format PostgreSQL exporters. Most teams start simpler: cron job checking disk space and connection count every hour.
Can I use Metabase's application database for my own data analysis?
No. It is not suitable and not recommended. The application database stores Metabase configuration only. Connect your own data sources separately in Metabase's UI.
The Bottom Line
PostgreSQL is the right choice for production Metabase deployments. Setup is straightforward: environment variables, a Docker Compose file, or a JDBC connection string. The real cost lies in operational burden: daily backups, weekly maintenance, monitoring, and incident response.
For a 5-person team with a capable ops person, self-hosting costs USD 30-55/month in infrastructure and USD 150-200 hours annually in labor. For teams without database expertise, managed hosting eliminates labor but costs more. Evaluate your team's capacity, your uptime requirements, and your budget before committing to self-hosted. If you lack the depth to maintain PostgreSQL, explore Opsily's managed Metabase hosting as a fully supported EU-hosted alternative.