- Metabase open source 7 năm tuổi, 38k star GitHub, là BI tool tốt nhất cho startup không muốn trả Tableau/Looker hàng nghìn USD/tháng.
- Chạy Docker Compose trên Cloud VPS 80 (799k/tháng) là đủ cho team 20 người và database 50GB.
- Kết nối read-replica PostgreSQL để query không block production. Cache result 5-15 phút cho dashboard nặng.
- Quan trọng: MRR, churn, conversion funnel, cohort retention, DAU/WAU/MAU. Code SQL mẫu sẵn dùng được.
- Bonus: alert email khi metric vượt threshold, embed dashboard public, schedule daily report.
Bạn launched product 6 tháng, có 500 user, doanh thu 100 triệu/tháng. Nhưng hỏi: tuần này MRR tăng hay giảm? Churn rate bao nhiêu? Cohort tháng 3 retain còn mấy phần trăm? Không trả lời được vì data nằm rải rác trong PostgreSQL, chưa ai pull ra dashboard. Metabase giải bài đó: kết nối DB, viết SQL, render chart, share team, alert khi bất thường. Hoàn toàn open source, self-host trên VPS, không tốn xu nào ngoài hosting.
Mình deploy Metabase cho 6 startup khách hàng và 2 dự án nội bộ. Workflow chuẩn: 1 ngày setup, 2 ngày build dashboard cốt lõi, sau đó founder tự xem chart hằng ngày không cần dev support. Bài này chia sẻ full setup, SQL template, và 12 metric startup nào cũng nên track.
1. Tại sao chọn Metabase thay vì Tableau, Looker, Power BI?
3 lý do chính: rẻ, dễ dùng cho non-dev, self-host được.
| Tool | Chi phí/user/tháng | Self-host | SQL editor | Khó học |
|---|---|---|---|---|
| Metabase OSS | Free | Có | Có | Dễ |
| Metabase Cloud | ~85 USD/5 user | Không | Có | Dễ |
| Tableau | 70 USD | Server enterprise | Có | Khó |
| Looker | ~5000 USD/cluster | GCP only | LookML | Rất khó |
| Power BI | 10 USD | Server enterprise | DAX | Trung bình |
| Superset | Free | Có | Có | Trung bình |
Metabase thắng cho team Việt: UI tiếng Anh dễ đọc, có tính năng "question builder" để non-tech tự tạo chart bằng click chuột, SQL editor có autocomplete, dashboard share link với password public.
2. Cài Metabase trên VPS bằng Docker Compose
Metabase cần database riêng để lưu config (questions, dashboards, user). Khuyến nghị dùng PostgreSQL chính cho app, tạo schema metabase_app riêng. Không nên dùng H2 mặc định vì không bền và khó backup.
mkdir -p /opt/metabase
cd /opt/metabase
File docker-compose.yml:
services:
metabase:
image: metabase/metabase:v0.51.0
container_name: metabase
restart: unless-stopped
environment:
MB_DB_TYPE: postgres
MB_DB_HOST: db.your-domain.com
MB_DB_PORT: 5432
MB_DB_DBNAME: metabase_app
MB_DB_USER: metabase
MB_DB_PASS: "your_secure_password"
MB_SITE_URL: https://bi.your-domain.com
JAVA_TIMEZONE: Asia/Ho_Chi_Minh
MB_PASSWORD_COMPLEXITY: strong
MB_PASSWORD_LENGTH: 12
ports:
- "127.0.0.1:3000:3000"
healthcheck:
test: curl -f http://localhost:3000/api/health || exit 1
interval: 30s
timeout: 10s
retries: 5
Tạo database và user trên PostgreSQL trước:
psql -U postgres
CREATE USER metabase WITH PASSWORD 'your_secure_password';
CREATE DATABASE metabase_app OWNER metabase;
GRANT ALL PRIVILEGES ON DATABASE metabase_app TO metabase;
Khởi động Metabase, đợi 1-2 phút lần đầu migrate schema:
docker compose up -d
docker logs -f metabase
3. Caddy reverse proxy với HTTPS
bi.your-domain.com {
reverse_proxy 127.0.0.1:3000
encode gzip
header {
Strict-Transport-Security "max-age=31536000;"
X-Frame-Options "SAMEORIGIN"
}
log {
output file /var/log/caddy/metabase.log
}
}
Reload caddy, vào https://bi.your-domain.com sẽ thấy wizard setup: tạo admin account, kết nối database production (read-only user khuyến nghị), import sample data Metabase cung cấp để học.
4. Tạo read-only user PostgreSQL cho Metabase
Quan trọng: KHÔNG cho Metabase user toàn quyền DB production. Nếu lỡ ai đó query DROP TABLE trong SQL editor là xong. Tạo user chỉ SELECT:
CREATE USER metabase_ro WITH PASSWORD 'another_secure_password';
GRANT CONNECT ON DATABASE app_production TO metabase_ro;
GRANT USAGE ON SCHEMA public TO metabase_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO metabase_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO metabase_ro;
-- Set statement timeout 30s để query nặng không treo DB
ALTER USER metabase_ro SET statement_timeout = '30s';
ALTER USER metabase_ro SET idle_in_transaction_session_timeout = '60s';
Nếu có read-replica thì connect Metabase vào replica chứ không phải primary, đảm bảo query analytics không ảnh hưởng app.
5. Metric thứ 1: MRR (Monthly Recurring Revenue)
SQL tính MRR cho SaaS subscription:
SELECT
date_trunc('month', invoice_date) AS month,
SUM(amount / EXTRACT(month FROM (period_end::date - period_start::date)::interval + interval '1 day')) AS mrr
FROM invoices
WHERE status = 'paid'
AND invoice_date >= NOW() - interval '12 months'
GROUP BY 1
ORDER BY 1;
Chart line, x = month, y = mrr. Set Metabase question refresh cache 1 hour. Save vào dashboard "Finance".
6. Metric thứ 2: Churn Rate hằng tháng
WITH monthly_stats AS (
SELECT
date_trunc('month', current_date - generate_series(0, 11) * interval '1 month') AS month
)
SELECT
ms.month,
COUNT(DISTINCT s.user_id) FILTER (WHERE s.canceled_at >= ms.month AND s.canceled_at < ms.month + interval '1 month') AS churned,
COUNT(DISTINCT s.user_id) FILTER (WHERE s.created_at < ms.month AND (s.canceled_at IS NULL OR s.canceled_at >= ms.month)) AS active_start,
ROUND(
100.0 * COUNT(DISTINCT s.user_id) FILTER (WHERE s.canceled_at >= ms.month AND s.canceled_at < ms.month + interval '1 month')
/ NULLIF(COUNT(DISTINCT s.user_id) FILTER (WHERE s.created_at < ms.month), 0), 2
) AS churn_pct
FROM monthly_stats ms
CROSS JOIN subscriptions s
GROUP BY 1
ORDER BY 1;
Industry benchmark: SaaS B2B healthy churn dưới 2%/tháng. Trên 5%/tháng là red flag. Set alert email khi churn_pct vượt 5%.
7. Metric thứ 3: Cohort Retention
SELECT
cohort_month,
months_since_signup,
COUNT(DISTINCT user_id) AS retained_users,
ROUND(100.0 * COUNT(DISTINCT user_id) / FIRST_VALUE(COUNT(DISTINCT user_id))
OVER (PARTITION BY cohort_month ORDER BY months_since_signup), 1) AS retention_pct
FROM (
SELECT
u.id AS user_id,
date_trunc('month', u.created_at) AS cohort_month,
EXTRACT(year FROM age(date_trunc('month', e.event_date), date_trunc('month', u.created_at))) * 12
+ EXTRACT(month FROM age(date_trunc('month', e.event_date), date_trunc('month', u.created_at))) AS months_since_signup
FROM users u
JOIN events e ON e.user_id = u.id
WHERE u.created_at >= NOW() - interval '12 months'
) AS cohorts
GROUP BY cohort_month, months_since_signup
ORDER BY cohort_month, months_since_signup;
Trong Metabase chọn visualization "Pivot Table" hoặc "Heat map" để hiển thị cohort matrix. Row = cohort_month, Column = months_since_signup, Cell = retention_pct.
8. Metric thứ 4: DAU/WAU/MAU và stickiness
-- DAU 30 ngày qua
SELECT
event_date::date AS day,
COUNT(DISTINCT user_id) AS dau
FROM events
WHERE event_date >= NOW() - interval '30 days'
GROUP BY 1
ORDER BY 1;
-- Stickiness = DAU/MAU ratio
WITH daily AS (
SELECT day::date, COUNT(DISTINCT user_id) AS dau
FROM events
WHERE event_date >= NOW() - interval '30 days'
GROUP BY day::date
),
monthly AS (
SELECT COUNT(DISTINCT user_id) AS mau
FROM events
WHERE event_date >= NOW() - interval '30 days'
)
SELECT
ROUND(AVG(d.dau)::numeric / m.mau, 3) AS stickiness
FROM daily d, monthly m
GROUP BY m.mau;
Stickiness > 0.2 (DAU/MAU) là tốt cho consumer app, > 0.5 là xuất sắc (Facebook level). Cho SaaS B2B 0.1-0.15 đã chấp nhận được.
9. Metric thứ 5: Conversion Funnel
WITH funnel AS (
SELECT
COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'visit_landing') AS step_1_visit,
COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'click_signup') AS step_2_click_signup,
COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'create_account') AS step_3_register,
COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'activate_account') AS step_4_activate,
COUNT(DISTINCT user_id) FILTER (WHERE event_name = 'first_purchase') AS step_5_purchase
FROM events
WHERE event_date >= NOW() - interval '30 days'
)
SELECT
step_1_visit, step_2_click_signup, step_3_register, step_4_activate, step_5_purchase,
ROUND(100.0 * step_2_click_signup / NULLIF(step_1_visit, 0), 2) AS conv_1_to_2,
ROUND(100.0 * step_3_register / NULLIF(step_2_click_signup, 0), 2) AS conv_2_to_3,
ROUND(100.0 * step_4_activate / NULLIF(step_3_register, 0), 2) AS conv_3_to_4,
ROUND(100.0 * step_5_purchase / NULLIF(step_4_activate, 0), 2) AS conv_4_to_5
FROM funnel;
Tìm step nào drop nhiều nhất, đó là chỗ cần optimize UX/copy đầu tiên. Mình thường thấy step "create_account to activate" drop 50-70%, fix bằng email onboarding sequence.
10. Metric thứ 6: LTV, CAC, payback period
-- LTV avg
SELECT
AVG(total_revenue) AS avg_ltv,
AVG(lifetime_months) AS avg_lifetime_months
FROM (
SELECT
user_id,
SUM(amount) AS total_revenue,
EXTRACT(month FROM age(MAX(invoice_date), MIN(invoice_date))) + 1 AS lifetime_months
FROM invoices
WHERE status = 'paid'
GROUP BY user_id
) AS user_revenue;
-- CAC by channel
SELECT
acquisition_channel,
SUM(spend) / NULLIF(COUNT(DISTINCT user_id), 0) AS cac
FROM marketing_spend ms
JOIN users u ON u.acquisition_channel = ms.channel AND date_trunc('month', u.created_at) = ms.month
GROUP BY 1
ORDER BY cac;
LTV/CAC > 3 là healthy. < 1 là đốt tiền marketing không hiệu quả. Payback period = CAC / (ARPU * gross margin), nên dưới 12 tháng cho SaaS.
11. Dashboard cấu trúc cho startup
- Executive Dashboard: MRR, ARR, total users, total revenue, growth rate. View hằng ngày.
- Acquisition Dashboard: visits, signups, conversion funnel, CAC by channel, ad spend ROI.
- Retention Dashboard: cohort retention, churn rate, NPS score, feature adoption.
- Finance Dashboard: revenue by plan, refund rate, failed payment, AR aging.
- Product Dashboard: DAU/MAU, top features used, time-to-aha-moment, session duration.
Mỗi dashboard nên có 6-12 chart, đừng quá nhồi nhét. Founder xem 30 giây phải hiểu trạng thái business.
12. Setup alert email khi metric bất thường
Metabase có tính năng Alert built-in. Cấu hình SMTP trong Admin Settings (dùng Mailgun, Postmark, hoặc Gmail SMTP):
SMTP Host: smtp.mailgun.org
SMTP Port: 587
SMTP Security: STARTTLS
SMTP Username: [email protected]
SMTP Password: $MAILGUN_PASSWORD
From Address: [email protected]
Trong mỗi question, click Settings (chuông), set điều kiện: "Send email when this question returns any results" và viết SQL kiểu:
SELECT
date,
churn_pct
FROM monthly_churn
WHERE date = date_trunc('month', NOW())
AND churn_pct > 5;
Khi query trả về row, Metabase tự gửi email tới founder. Cron schedule mỗi ngày 8h sáng.
13. Performance tuning cho dashboard nặng
- Cache result: Admin Settings, Caching, set TTL 60-300 giây cho question nặng. Không cần realtime cho metric tháng/tuần.
- Materialized view: tạo view trong PostgreSQL aggregate sẵn data, Metabase chỉ query view. Refresh view 1h/lần qua cron.
- Index column hay dùng: CREATE INDEX trên cột date, user_id, status để query GROUP BY và WHERE nhanh.
- Tách database analytics: nếu DB lớn, dùng dbt copy data sang warehouse riêng (ClickHouse, DuckDB), Metabase query warehouse.
- Limit row trong SQL editor: thêm LIMIT 10000 cho query exploration, tránh load hàng triệu row vào browser.
14. Bonus: embed dashboard public không cần login
Metabase có "Public Sharing" toggle cho dashboard. Bật xong sẽ có link share, có thể embed iframe vào website company:
Hữu ích cho investor update, monthly board report, hoặc public metric như Buffer/Baremetrics. Backup an toàn: disable feature này nếu data sensitive.
15. Backup config Metabase và disaster recovery
Tất cả dashboard, question, user permission của Metabase lưu trong metabase_app database. Backup hằng đêm là bắt buộc, không thì khi VPS hỏng phải build lại 200+ chart từ đầu.
#!/bin/bash
# /opt/metabase/backup.sh
DATE=$(date +%F-%H%M)
BACKUP_DIR=/opt/backups/metabase
mkdir -p $BACKUP_DIR
# Dump metabase config db
PGPASSWORD=your_pass pg_dump -h db.your-domain.com -U metabase metabase_app
| gzip > $BACKUP_DIR/metabase-$DATE.sql.gz
# Push lên S3-compatible storage
aws s3 cp $BACKUP_DIR/metabase-$DATE.sql.gz s3://your-backup-bucket/metabase/
--endpoint-url https://s3.your-domain.com
# Xoá file cũ hơn 30 ngày local
find $BACKUP_DIR -name "metabase-*.sql.gz" -mtime +30 -delete
Crontab: 0 3 * * * /opt/metabase/backup.sh. Test restore quarterly: pull file gz, restore vào DB staging, mở Metabase staging xem dashboard có hiện lại không. Snapshot Cloud VPS TND cũng là backup layer thứ 2 cho cả OS và Docker volume.
16. Khi nào nên upgrade từ Metabase sang data warehouse?
Metabase chạy SQL trực tiếp trên DB application. Khi DB > 500GB hoặc query analytics ăn quá nhiều CPU/IO, cần tách layer warehouse:
- Stage 1 (Metabase + DB primary): dưới 50GB DB, dưới 10 user analytics.
- Stage 2 (Metabase + DB read-replica): 50-500GB DB, 10-30 user.
- Stage 3 (Metabase + dbt + ClickHouse/DuckDB): 500GB+, query phức tạp, cần materialized model.
- Stage 4 (warehouse cloud): nhiều TB, multi-source data (web, app, ads, CRM), cân nhắc Snowflake/BigQuery.
Mình advise startup ở stage 1-2 trong 2-3 năm đầu. Sang stage 3 chỉ khi thực sự có nhu cầu, tránh over-engineering tốn tiền và thời gian setup.
VPS chạy Metabase + PostgreSQL cho startup
Cloud VPS TND sẵn AlmaLinux 9, Ubuntu 22/24, Debian 12/13. SSD CEPH, snapshot 1-click, backup hằng ngày, network 200Mbps trong nước. Cấu hình 4-8GB RAM đủ chạy Metabase + Postgres analytics cho team 20 dev, dashboard render dưới 1 giây.
Xem 8 cấu hình Cloud VPS →FAQ
Metabase OSS có giới hạn gì so với Pro/Enterprise?
OSS đủ 95% nhu cầu startup: dashboard, SQL editor, alert email, public share, embed iframe, kết nối 20+ database. Pro/Enterprise có thêm: SSO SAML, audit log, multi-tenant, white-label, official iframe embedding signed. Cho startup giai đoạn đầu OSS dư dùng.
VPS bao nhiêu RAM đủ cho Metabase?
Metabase JVM ăn tối thiểu 2GB RAM, idle khoảng 1.2GB. Cho team 5 user và database 10GB, Cloud VPS 40 (399k, 2GB RAM) là đủ. Team 20 user và DB 100GB, lên Cloud VPS 80 (4GB RAM). Lưu ý chia sẻ VPS với PostgreSQL thì cộng thêm 2-4GB nữa.
Có nên connect Metabase trực tiếp vào DB production?
Không khuyến nghị. Tạo read-only user PostgreSQL, set statement_timeout 30s, tốt nhất là dùng read-replica để query analytics không impact production. Nếu DB nhỏ và load thấp thì có thể dùng primary với read-only user.
So với Apache Superset thì Metabase có gì khác?
Metabase dễ dùng cho non-dev, có Question Builder click-and-go. Superset mạnh hơn về visualization (50+ chart type), SQL Lab cho power user, multi-tenant tốt hơn. Cho startup dưới 50 người, Metabase win về UX. Cho team data engineer chuyên nghiệp, Superset linh hoạt hơn.
Metabase kết nối được những database nào?
PostgreSQL, MySQL/MariaDB, MongoDB, SQL Server, Oracle, Redshift, BigQuery, Snowflake, ClickHouse, Druid, Presto, Trino, SQLite, H2 và nhiều hơn nữa qua community driver. Startup phổ biến: PostgreSQL cho app + ClickHouse cho event analytics nếu data lớn.


