Skip to main content

Self-Hosting Guide: Deploy Metabase for Analytics 2026

·OSSAlt Team
metabasebusiness-intelligenceself-hostingdockerguide
Share:

Metabase is the most popular open source BI tool — it lets non-technical users explore data, build dashboards, and share insights without writing SQL. Self-hosting gives you unlimited users and dashboards for free.

Requirements

  • VPS with 2 GB RAM minimum (4 GB recommended)
  • Docker and Docker Compose
  • Domain name (e.g., bi.yourdomain.com)
  • 10+ GB disk
  • A database to analyze (PostgreSQL, MySQL, etc.)

Step 1: Create Docker Compose

# docker-compose.yml
services:
  metabase:
    image: metabase/metabase:latest
    container_name: metabase
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - MB_DB_TYPE=postgres
      - MB_DB_DBNAME=metabase
      - MB_DB_PORT=5432
      - MB_DB_USER=metabase
      - MB_DB_PASS=your-strong-password
      - MB_DB_HOST=db
      - MB_SITE_URL=https://bi.yourdomain.com
      - MB_ENCRYPTION_SECRET_KEY=your-random-32-char-key
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    container_name: metabase-db
    restart: unless-stopped
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=metabase
      - POSTGRES_USER=metabase
      - POSTGRES_PASSWORD=your-strong-password

volumes:
  postgres_data:

Note: This PostgreSQL is for Metabase's own data (dashboards, users, settings). Your business data lives in separate databases that you'll connect in Step 6.

Step 2: Generate Encryption Key

openssl rand -hex 16

This encrypts database credentials stored in Metabase.

Step 3: Start Metabase

docker compose up -d

First boot takes 1-2 minutes to initialize.

Step 4: Reverse Proxy (Caddy)

# /etc/caddy/Caddyfile
bi.yourdomain.com {
    reverse_proxy localhost:3000
}
sudo systemctl restart caddy

Step 5: Initial Setup Wizard

  1. Open https://bi.yourdomain.com
  2. Select your language
  3. Create admin account
  4. Connect your first database (can skip and add later)
  5. Choose usage tracking preference

Step 6: Connect Your Data Sources

Go to AdminDatabasesAdd database:

DatabaseConnection String
PostgreSQLhost:5432/dbname
MySQLhost:3306/dbname
MongoDBmongodb://host:27017/dbname
SQLite/path/to/database.db
BigQueryService account JSON
SnowflakeAccount + credentials
Redshifthost:5439/dbname

Tip: Create a read-only database user for Metabase:

CREATE USER metabase_reader WITH PASSWORD 'read-only-password';
GRANT CONNECT ON DATABASE myapp TO metabase_reader;
GRANT USAGE ON SCHEMA public TO metabase_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO metabase_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO metabase_reader;

Step 7: Build Your First Dashboard

Create a question (query):

  1. Click NewQuestion
  2. Pick your database and table
  3. Use the visual query builder or write SQL
  4. Save the question

Build a dashboard:

  1. Click NewDashboard
  2. Add saved questions as cards
  3. Add filters (date range, category, etc.)
  4. Arrange and resize cards
  5. Save and share

Common dashboard patterns:

DashboardMetrics
RevenueMRR, churn rate, LTV, new subscriptions
ProductDAU/MAU, feature usage, retention cohorts
SupportTicket volume, response time, CSAT scores
MarketingTraffic, conversion rate, CAC, channel performance

Step 8: Configure SMTP for Alerts

AdminSettingsEmail:

SettingValue
SMTP Hostsmtp.resend.com
SMTP Port587
SMTP SecurityTLS
SMTP Usernameresend
SMTP Passwordre_your_api_key
From Addressbi@yourdomain.com

Enables:

  • Scheduled dashboard emails (daily/weekly reports)
  • Alert notifications (when metrics cross thresholds)
  • User invitation emails

Step 9: Set Up Alerts

  1. Open a question/chart
  2. Click the bell icon → Create alert
  3. Choose condition: "When results go above/below X"
  4. Set recipients and frequency
  5. Alerts fire automatically when conditions are met

Step 10: Embedding (Optional)

Embed Metabase dashboards in your app:

// Generate signed embed URL (server-side)
const jwt = require('jsonwebtoken');

const METABASE_SITE_URL = 'https://bi.yourdomain.com';
const METABASE_SECRET_KEY = 'your-embedding-secret-key';

const payload = {
  resource: { dashboard: 1 },
  params: {},
  exp: Math.round(Date.now() / 1000) + (10 * 60), // 10 min expiration
};

const token = jwt.sign(payload, METABASE_SECRET_KEY);
const embedUrl = `${METABASE_SITE_URL}/embed/dashboard/${token}`;
<iframe
  src="https://bi.yourdomain.com/embed/dashboard/TOKEN"
  width="100%"
  height="600"
  frameborder="0"
></iframe>

Enable embedding in AdminSettingsEmbedding.

Production Hardening

Environment tuning:

environment:
  - JAVA_OPTS=-Xmx2g  # Increase for large datasets
  - MB_JETTY_MAXTHREADS=100
  - MB_ASYNC_QUERY_THREAD_POOL_SIZE=10

Backups:

# Database backup (daily cron)
docker exec metabase-db pg_dump -U metabase metabase > /backups/metabase-$(date +%Y%m%d).sql

Updates:

docker compose pull
docker compose up -d

Security:

  • Use read-only database users
  • Enable SSL for all database connections
  • Restrict Admin access to specific users
  • Set session timeout in Admin settings

Resource Usage

UsersRAMCPUDisk
1-102 GB2 cores10 GB
10-504 GB4 cores15 GB
50-2008 GB8 cores30 GB

VPS Recommendations

ProviderSpec (20 users)Price
Hetzner4 vCPU, 8 GB RAM€8/month
DigitalOcean2 vCPU, 4 GB RAM$24/month
Linode2 vCPU, 4 GB RAM$24/month

Why Self-Host Metabase

Metabase Cloud's pricing scales aggressively with seat count. The Starter plan is $85/month for up to 5 users — reasonable for a small team. But the Pro plan, which adds SSO, advanced permissions, and sandboxing, starts at $500/month. If your data team grows to 20+ people who need BI access, managed Metabase costs $1,000–3,000/month depending on features required. Tableau and Looker charge even more: Tableau Creator licenses run $75/user/month, and Looker starts at $5,000/month for its entry tier.

Self-hosted Metabase Open Source is completely free — unlimited users, unlimited dashboards, unlimited data sources. You pay only for the server: a Hetzner CX31 at €6.49/month comfortably handles a 20-person analytics team. The annual savings against Metabase Cloud Pro (20 users) exceed $12,000 per year. For startups with limited budget but real analytics needs, this is a significant advantage.

Full data access control: In SaaS BI tools, your data queries leave your infrastructure. For companies handling sensitive business data — revenue figures, user PII, customer contracts — having query results pass through a vendor's servers creates compliance risk. Self-hosted Metabase queries your databases directly from within your own infrastructure; nothing routes through Metabase Labs' servers.

Embedding without vendor lock-in: Metabase's signed embedding feature is exceptionally useful for SaaS products that want to offer customers analytics dashboards. The embedding feature is fully available in the open source version — something that competitors like Looker and Tableau charge substantial premiums for as an "embedded analytics" product.

When NOT to self-host Metabase: Metabase is a Java application with a non-trivial resource footprint. It requires 2 GB RAM minimum, and the startup time is 1–2 minutes from cold. If your data team needs advanced features like custom models (formerly DBT integration), advanced caching, or enterprise SSO, some of these require Metabase Pro or Enterprise. Evaluate whether the open source feature set meets your requirements before committing to self-hosting.

Prerequisites (Expanded)

VPS with 2 GB RAM minimum (4 GB recommended): Metabase runs on a JVM and has a real memory floor. With 2 GB total, you're splitting memory between Metabase (which needs 1.5 GB for JVM heap) and PostgreSQL (which handles the application's own metadata). Under memory pressure, Metabase becomes slow and may crash mid-query. Budget 4 GB for any deployment with more than 5 concurrent users.

Docker and Docker Compose: The Compose file above deploys Metabase and its dedicated PostgreSQL instance together. The PostgreSQL in this Compose file stores Metabase's own data (dashboards, saved questions, user accounts, permissions). Your business databases that you want to analyze are separate — you'll connect them in Step 6 using Metabase's database connection UI.

10+ GB disk: Metabase's PostgreSQL database stays small (a few hundred MB even for complex deployments), but query result caching and Metabase's internal analytics can consume additional space over time. 10 GB provides comfortable headroom. Monitor disk usage with df -h and set up alerts if it approaches 80%.

A database to analyze: Metabase's value comes from connecting to your existing data sources. If you're running PostgreSQL or MySQL for your application, you can create a read-only user (see Step 6) and point Metabase at it. Start with GRANT SELECT ON ALL TABLES and narrow permissions as you understand what Metabase needs.

For VPS selection, the JVM heap requirement pushes Metabase into a higher tier than most self-hosted tools. See the VPS comparison for self-hosters for a breakdown of providers at the 4–8 GB RAM tier, where Hetzner offers the best price-to-performance ratio.

Production Security Hardening

Metabase has privileged access to your business databases — it can read (and potentially write, if you misconfigure permissions) all your data. Its security posture matters.

Firewall with UFW: Port 3000 is Metabase's internal port. Only expose 80, 443, and 22 publicly.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Use read-only database connections: As shown in Step 6, create a dedicated metabase_reader user with SELECT-only permissions on the databases Metabase analyzes. Never connect Metabase with superuser or application-write credentials. This contains the blast radius if Metabase is compromised or a user runs a destructive query.

Fail2ban for SSH:

sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Keep secrets in environment variables: MB_DB_PASS and MB_ENCRYPTION_SECRET_KEY must not be committed to version control. Store them in a .env file excluded from Git. If using Docker Compose without a separate .env file, be careful never to docker compose config output in a public terminal session — it prints all resolved environment variables including secrets.

Disable SSH password authentication:

sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd

Enable SSL for database connections in Metabase: When adding databases in Admin → Databases, check "Use a secure connection" to enforce TLS between Metabase and your data sources, especially if they're on separate servers.

Automatic security updates:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades

Back up the Metabase PostgreSQL database regularly: This database stores all your dashboards, saved questions, collections, and user permissions. Losing it means rebuilding your entire BI setup. Use automated server backups with restic to automate daily PostgreSQL dumps with off-site copies. For a complete server hardening guide, see the self-hosting security checklist.

Troubleshooting Common Issues

Metabase takes 5+ minutes to start

Metabase runs database migrations on first start and after each upgrade. The first boot is always slow — 2–5 minutes is normal. If subsequent starts are consistently slow, your PostgreSQL container may be competing for resources. Check container resource usage with docker stats. If the JVM is hitting memory limits, increase JAVA_OPTS=-Xmx2g to allocate more heap.

"Connection refused" when adding a database

Metabase runs in a Docker container and uses container networking. When connecting to a database running in another container, use the container name or Docker network alias, not localhost. For a PostgreSQL container named app-db on the same Docker network, the host is app-db, not 127.0.0.1. For databases on external servers, use the server IP or hostname directly.

Dashboards loading very slowly

Slow dashboards usually indicate slow underlying queries. Click the clock icon on any card in the dashboard to see query execution time. Use Metabase's built-in query caching (Admin → Settings → Caching) to cache expensive query results — set cache TTL based on how frequently your data changes. For dashboards with many cards, stagger the cache expiry times to prevent all queries from running simultaneously.

Users can't see certain tables

Metabase's data permissions are configured in Admin → Permissions → Data. Check that the user's group has access to the schema and table in question. Note that permissions cascade: access to a database doesn't automatically grant access to all schemas within it. Also verify that the database user Metabase uses (metabase_reader) has SELECT permissions on the specific table — GRANT SELECT ON ALL TABLES IN SCHEMA public doesn't cover tables created after the grant was issued unless you also ran ALTER DEFAULT PRIVILEGES.

Scheduled reports not being sent

If scheduled dashboard emails aren't arriving, start by testing the SMTP configuration in Admin → Settings → Email → Test. Check the Metabase logs for email errors:

docker compose logs metabase | grep -i "mail\|smtp\|email" | tail -20

Also verify that the scheduled report is assigned to a user with a valid email address and that the user has permission to view all cards on the dashboard.

Embedding returns 403 or shows blank iframe

JWT embedding requires that the token's exp (expiration) claim is in the future, the signing key matches what's configured in Admin → Settings → Embedding, and the resource.dashboard ID is a valid dashboard ID. Generate a fresh token for each page load — don't cache JWT tokens. Also check that the MB_SITE_URL environment variable matches exactly the URL you're embedding from, including protocol.


Set up automated server backups with restic to protect your Metabase dashboards and database connections.

Compare BI tools on OSSAlt — features, data sources, and self-hosting options side by side.

See open source alternatives to Metabase on OSSAlt.

The SaaS-to-Self-Hosted Migration Guide (Free PDF)

Step-by-step: infrastructure setup, data migration, backups, and security for 15+ common SaaS replacements. Used by 300+ developers.

Join 300+ self-hosters. Unsubscribe in one click.