Metabase в Docker Compose: готовый файл
Строить дашборды через SQL-запросы каждый раз, когда менеджеру нужна очередная цифра, — это медленно и утомляет обе стороны. Metabase закрывает этот разрыв: подключаете источник данных один раз, а дальше отдел продаж или маркетинга сам собирает графики через понятный интерфейс, без единой строчки кода. Ниже — рабочий docker-compose.yml, который поднимает Metabase с полноценной базой под метаданные, HTTPS и бэкапом, а не игрушечный вариант «для теста».
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Metabase и когда он нужен
Metabase — open-source BI-инструмент на Java (Clojure), который умеет:
- подключаться почти к любой реляционной базе (PostgreSQL, MySQL, ClickHouse, SQL Server, BigQuery и т.д.);
- строить дашборды и графики через визуальный конструктор («Notebook editor») без SQL;
- давать доступ к «Native query» — прямому SQL для тех, кто умеет и хочет писать запросы сам;
- рассылать отчёты по расписанию на email и в Slack;
- разграничивать доступ по группам пользователей и коллекциям дашбордов.
Он не заменяет Grafana для мониторинга инфраструктуры и не тянет объёмы данных, которые нужны ClickHouse для аналитики на миллиардах строк — там, где действительно нужна колоночная СУБД, есть отдельный разбор: ClickHouse или PostgreSQL для аналитики — что выбрать. Metabase хорош ровно там, где он и задумывался: бизнес-дашборды поверх обычной транзакционной или аналитической базы, которые собирает не разработчик, а аналитик или менеджер.
Для комфортной работы достаточно VPS с 2 vCPU и 4 ГБ RAM — Metabase на старте съедает около 1–1.5 ГБ, плюс место под контейнер с PostgreSQL. Если дашбордов и активных пользователей будет много, закладывайтесь на 8 ГБ.
Готовый docker-compose.yml для Metabase
Минимальный рабочий вариант — Metabase хранит свои метаданные (пользователи, дашборды, вопросы, настройки) в собственной базе. По умолчанию образ поднимает встроенную H2, но это годится только для разового теста: H2 — это файл на диске без нормальной поддержки конкурентного доступа, и при сбое контейнера легко потерять всё содержимое.
Правильный подход — сразу указать внешний PostgreSQL под метаданные. Структура каталога:
metabase/
├── docker-compose.yml
├── .env
└── data/
└── postgres/
.env:
MB_DB_USER=metabase
MB_DB_PASS=change_me_strong_password
MB_DB_NAME=metabase
MB_ENCRYPTION_SECRET_KEY=замените_на_случайную_строку_32+_символов
TZ=Europe/Moscow
Секретный ключ шифрования сгенерируйте один раз командой:
openssl rand -base64 32
Его нельзя терять и менять на проде — Metabase шифрует им хранимые пароли к источникам данных, и при замене ключа существующие подключения перестанут расшифровываться.
docker-compose.yml:
services:
metabase-db:
image: postgres:16-alpine
container_name: metabase-db
restart: unless-stopped
environment:
POSTGRES_USER: ${MB_DB_USER}
POSTGRES_PASSWORD: ${MB_DB_PASS}
POSTGRES_DB: ${MB_DB_NAME}
volumes:
- ./data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${MB_DB_USER} -d ${MB_DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- metabase-net
metabase:
image: metabase/metabase:v0.51.5
container_name: metabase
restart: unless-stopped
depends_on:
metabase-db:
condition: service_healthy
environment:
MB_DB_TYPE: postgres
MB_DB_DBNAME: ${MB_DB_NAME}
MB_DB_PORT: 5432
MB_DB_USER: ${MB_DB_USER}
MB_DB_PASS: ${MB_DB_PASS}
MB_DB_HOST: metabase-db
MB_ENCRYPTION_SECRET_KEY: ${MB_ENCRYPTION_SECRET_KEY}
MB_JETTY_PORT: 3000
JAVA_TIMEZONE: ${TZ}
ports:
- "127.0.0.1:3000:3000"
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:3000/api/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
networks:
- metabase-net
networks:
metabase-net:
driver: bridge
Обратите внимание на два момента, которые часто упускают:
image: metabase/metabase:v0.51.5— версия зафиксирована жёстко, а неlatest. Мажорные обновления Metabase иногда меняют схему внутренней БД и ломают кастомные дашборды на нестабильных сборках; фиксация версии даёт контроль над моментом апгрейда.- порт
3000пробрасывается только на127.0.0.1, наружу Metabase отдаёт reverse-proxy — это разобрано в отдельном разделе ниже.
Поднимаем:
docker compose up -d
docker compose logs -f metabase
Первый старт занимает 1–2 минуты — Metabase накатывает миграции в базу метаданных. Как только в логах появится Metabase Initialization COMPLETE, интерфейс доступен на http://<ip-сервера>:3000 (или через прокси, если он уже настроен) для мастера первичной настройки: создание админ-аккаунта и подключение первого источника данных.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверMetabase с PostgreSQL как хранилищем метаданных: почему это важно
Разница между H2 и PostgreSQL под метаданные — не теоретическая. С H2 вы получаете:
- один файл
metabase.db.mv.db, который блокируется на запись — при параллельной активности нескольких админов возможны конфликты; - отсутствие горячего бэкапа: файл нужно копировать при остановленном контейнере;
- риск повреждения файла при жёстком перезапуске (
docker compose downбез штатного завершения, OOM-killer, обрыв питания хоста).
С внешним PostgreSQL (как в конфиге выше) вы получаете обычную SQL-базу, которую можно бэкапить pg_dump на лету, реплицировать и мониторить теми же средствами, что и остальные ваши базы. Если PostgreSQL на сервере уже используется под другие проекты, имеет смысл поднять для Metabase отдельную базу на существующем инстансе, а не отдельный контейнер — экономит память. Базовая установка PostgreSQL с нуля, если её ещё нет, описана в статье PostgreSQL на Ubuntu 24.04: пошаговая установка.
Важно не путать эту базу с базами, которые Metabase анализирует как источники данных — это разные вещи: одна база хранит «как выглядит ваш дашборд», другие базы хранят собственно бизнес-данные, которые дашборд показывает.
Reverse-proxy и HTTPS для Metabase
Отдавать Metabase напрямую на порт 3000 без TLS — плохая идея: в интерфейсе передаются пароли и токены доступа к вашим базам данных. Быстрее всего поднять Traefik с автоматическими сертификатами Let's Encrypt; сравнение с nginx proxy manager и практические нюансы разобраны в статье Traefik или Nginx Proxy Manager — что выбрать для сервера.
Если Traefik уже развёрнут на сервере как отдельный стек с общей сетью proxy, добавьте к сервису metabase лейблы вместо проброса порта наружу:
metabase:
# ...
ports: [] # порт наружу больше не пробрасываем
labels:
- "traefik.enable=true"
- "traefik.http.routers.metabase.rule=Host(`bi.example.com`)"
- "traefik.http.routers.metabase.entrypoints=websecure"
- "traefik.http.routers.metabase.tls.certresolver=letsencrypt"
- "traefik.http.services.metabase.loadbalancer.server.port=3000"
networks:
- metabase-net
- proxy
networks:
metabase-net:
driver: bridge
proxy:
external: true
Если прокси нет и разворачивать целый Traefik ради одного сервиса избыточно, подойдёт связка Caddy с автоматическим SSL — конфиг буквально в три строки, разбор есть в статье про Let's Encrypt SSL на VPS.
Отдельно стоит настроить MB_SITE_URL в переменных окружения Metabase на конечный https://bi.example.com — иначе ссылки в email-рассылках и приглашениях пользователей будут генерироваться с неправильным адресом.
Подключение источников данных
После первичной настройки в Admin → Databases → Add database подключаете нужные источники. Для PostgreSQL/MySQL, которые крутятся в соседних Docker-сетях на том же хосте, достаточно указать имя контейнера как хост:
Host: postgres-app
Port: 5432
Database name: app_production
Username: metabase_ro
Password: ***
Практический совет: не подключайте Metabase под тем же пользователем, что использует приложение. Создайте отдельного read-only пользователя:
CREATE USER metabase_ro WITH PASSWORD 'strong_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;
Это защищает от двух проблем сразу: аналитик не сможет ничего испортить через Native Query, а тяжёлый SELECT не встанет в очередь блокировок с продовыми транзакциями (для этого стоит дополнительно настроить statement_timeout для роли metabase_ro).
Если источник — не сама прод-база, а отдельное аналитическое хранилище (например, ClickHouse), подключение делается через соответствующий драйвер — с версии 0.48+ большинство «нестандартных» коннекторов идёт отдельными плагинами или подключается как JAR-плагин в volume /plugins.
Производительность и потребление ресурсов
Metabase — Java-приложение, и по умолчанию JVM резервирует под себя ощутимую часть доступной памяти. Если сервер общий и на нём крутится что-то ещё, стоит явно ограничить heap:
metabase:
environment:
JAVA_OPTS: "-Xmx1200m"
и одновременно выставить лимиты самого контейнера, чтобы Metabase не «съел» память соседних сервисов при пиковой нагрузке:
metabase:
deploy:
resources:
limits:
memory: 1536M
reservations:
memory: 512M
Кэширование результатов запросов (Admin → Performance → Caching) стоит включить сразу — для дашбордов, которые смотрят раз в день, а не в реальном времени, это ощутимо снижает нагрузку на источник данных. Дашборды с прямым мониторингом инфраструктуры (например, метрики хостов) в Metabase тащить не стоит — для этого есть связка Grafana с Prometheus, разбор — в статье Prometheus и Grafana: связка, настройка.
Бэкап и обновление
Бэкапить нужно ровно то, что превращает Metabase в конкретный набор дашбордов — базу метаданных PostgreSQL, а не сам контейнер приложения (он полностью воссоздаётся из образа).
docker exec metabase-db pg_dump -U metabase metabase | gzip > metabase_backup_$(date +%F).sql.gz
Оформите это cron-задачей и держите хотя бы недельную историю копий:
0 3 * * * docker exec metabase-db pg_dump -U metabase metabase | gzip > /backup/metabase_$(date +\%F).sql.gz
find /backup -name "metabase_*.sql.gz" -mtime +7 -delete
Восстановление:
gunzip -c metabase_backup_2026-08-20.sql.gz | docker exec -i metabase-db psql -U metabase -d metabase
Обновление Metabase — это смена тега образа и рестарт, миграции базы применяются автоматически при старте:
# в docker-compose.yml: metabase/metabase:v0.51.5 → v0.52.x
docker compose pull metabase
docker compose up -d metabase
docker compose logs -f metabase
Перед мажорным обновлением (смена первой цифры версии) обязательно снимите свежий дамп базы метаданных — миграции необратимы, откат на предыдущую версию образа со «съехавшей» схемой БД не сработает.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли обойтись без отдельного PostgreSQL и оставить встроенную H2?
Технически да, для разового локального теста. Для чего-то, к чему возвращаются регулярно и на что полагаются другие сотрудники, — нет: H2 не переживёт жёсткий рестарт контейнера так же надёжно, как внешняя СУБД с нормальным бэкапом.
Metabase умеет писать в базу данных, а не только читать?
Начиная с версии 0.49 появилась экспериментальная функция «Actions» для записи через формы, но по умолчанию и в подавляющем большинстве сценариев Metabase используется только на чтение — и именно поэтому read-only пользователь БД, описанный выше, разумная практика по умолчанию.
Сколько источников данных можно подключить к одному Metabase?
Ограничения на число подключений в самом приложении нет, лимитирует только память и то, сколько параллельных тяжёлых запросов выдержит сервер. Для 3–5 источников среднего размера хватает конфигурации из этой статьи.
Нужен ли отдельный сервер под Metabase или можно на том же, где крутится приложение?
Можно на том же, если ресурсов хватает с запасом — Metabase не требователен к CPU в состоянии простоя. Единственное, за чем стоит следить, — чтобы тяжёлые аналитические запросы из Metabase не конкурировали за память и I/O с продовой базой в момент пиковой нагрузки приложения.
Как перенести Metabase на другой сервер?
Перенести нужно только volume с PostgreSQL (папку data/postgres) и файл .env с тем же MB_ENCRYPTION_SECRET_KEY — без него существующие подключения к источникам данных не расшифруются. Сам контейнер Metabase на новом сервере поднимается из того же docker-compose.yml с нуля.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →