Database

How to Deploy Metabase 0.46.5 in Docker Swarm Behind Caddy v2.6.4

Metabase is the easy, open-source way for everyone in your company to ask questions and learn from data without knowing SQL. Learn how to deploy it in a Docker Swarm cluster.

Rajasekhar Gundala··6 min read

Metabase is the easy, open-source way for everyone in your company to ask questions and learn from your data.

This post will show you how to deploy Metabase 0.46.5 to our Docker Swarm Cluster using Docker Compose, sitting securely behind a Caddy reverse proxy.

You can learn more by visiting the official Metabase website and their GitHub repository.

Let’s start with the actual deployment.

Prerequisites

Please ensure you fulfill the following requirements before proceeding with the deployment:

  1. A Docker Swarm Cluster configured with GlusterFS for persistent storage.
  2. Caddy deployed as the ingress reverse proxy to expose microservices externally.
  3. A running MariaDB database stack to host the application databases.

Introduction to Metabase

If you are looking for a tool to easily summarize and visualize your data without ever writing a single line of SQL or having to wait on a coworker, Metabase is the perfect solution.

When you need to dig into the complicated stuff, Metabase provides both a flexible GUI query builder and an elegant SQL interface. You can generate beautiful graphs and charts from your data with just a few clicks.

Why Choose Metabase?

Metabase is a simple and powerful analytics tool that lets anyone learn and make decisions from their company’s data. No technical knowledge is required!

Metabase allows you to browse or search through all tables in your databases, filter things down to find exactly what you need, and visualize the results intuitively.

Schedule and send charts or results to your team via email or Slack.

Let everyone on your team create, organize, and share beautiful collections of visualizations and dashboards.

Set up alerts to let everyone know when something needs attention or when you’ve finally met a specific goal.

Core Features

Metabase is built for accessibility:

  • Let anyone on your team ask questions without knowing SQL.
  • Create rich, beautiful dashboards with auto-refresh and full-screen modes.
  • Native SQL Mode for analysts and data pros.
  • Create canonical segments and metrics for everyone to use consistently.
  • Send automated data reports to Slack or email on a schedule with Pulses.
  • View data directly in Slack anytime with MetaBot.
  • Humanize data for your team by renaming, annotating, and hiding raw database fields.
  • Track changes in your data using intelligent alerts.

Supported Databases

Metabase supports connecting to most modern data sources, including:

  • PostgreSQL
  • MySQL / MariaDB
  • Druid
  • SQL Server
  • Redshift
  • MongoDB
  • Google BigQuery
  • SQLite
  • H2
  • Oracle
  • Vertica
  • Presto
  • Snowflake
  • SparkSQL

Persisting Metabase Data with GlusterFS

Containers are fast to deploy and make efficient use of system resources. However, their filesystems are ephemeral. If a container restarts or is rescheduled, local data is lost.

To overcome this, we use GlusterFS. I previously set up a replicated GlusterFS volume to ensure data is mirrored across all nodes in the cluster.

GlusterFS Replicated Volume

The volume is mounted across all nodes. When data is written to the /mnt partition, it is instantly replicated to the other nodes in the cluster.

If any node fails, the application automatically restarts on another node without losing data.

For disaster recovery, we need to persist the /metabase-data/metabase.db directory.

Create a folder in the /mnt directory for the persistent Metabase data:

cd /mnt
sudo mkdir -p metabase

Watch the video below for a complete guide on setting up a GlusterFS Replicated Volume.


Prepare the Deployment Environment

We will use Docker Compose to define the deployment environment.

Navigate to the /opt directory on your Swarm manager node and create the configuration directory for Metabase:

cd /opt
sudo mkdir -p metabase
cd metabase
sudo touch metabase.yml

Metabase Docker Compose Configuration

Open metabase.yml using your editor:

sudo nano metabase.yml

Paste the following Docker Compose configuration. For production readiness, we connect Metabase to the internal MariaDB database rather than using its default internal H2 database.

version: "3.7"

services:
  metabase:
    image: metabase/metabase:latest
    depends_on:
      - maria
    ports:
      - '4000:3000'
    volumes:
      - /mnt/metabase:/metabase-data
    environment:
      - MB_DB_TYPE=mysql
      - MB_DB_HOST=maria
      - MB_DB_PORT=3306
      - MB_DB_DBNAME_FILE=/run/secrets/mysql_db
      - MB_DB_USER_FILE=/run/secrets/mysql_user
      - MB_DB_PASS_FILE=/run/secrets/mysql_password
      - MB_DB_FILE=/metabase-data/metabase.db
    secrets:
      - mysql_db
      - mysql_user
      - mysql_password
    networks:
      - caddy
    deploy:
      placement:
        constraints: [node.role == worker]
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

secrets:
  mysql_db:
    file: ./mysql_db.txt
  mysql_user:
    file: ./mysql_user.txt
  mysql_password:
    file: ./mysql_password.txt

volumes:
  metabase:
    driver: "local"

networks:
  caddy:
    external: true

As mentioned in the prerequisites, I used the MariaDB stack deployed earlier as the backend storage system for Metabase.

Caddyfile Configuration

The Caddyfile is a highly readable configuration format for the Caddy web server.

Caddyfile is easy to write, easy to understand, and expressive enough for almost all use cases.

Here is the production-ready Caddyfile block required to expose Metabase securely. Learn more about writing Caddyfiles here.

{
    email you@example.com
    cert_issuer acme
    acme_ca [https://acme-v02.api.letsencrypt.org/directory](https://acme-v02.api.letsencrypt.org/directory)
    
    servers {
        metrics
        protocols h1 h2c h3
        strict_sni_host on
        trusted_proxies cloudflare {
            interval 12h
            timeout 15s
        }
    }
}

metabase.example.com {
    log {
        output file /var/log/caddy/metabase.log {
            roll_size 20mb
            roll_keep 2
            roll_keep_for 6h
        }
        format console
        level error
    }
    encode gzip zstd
    reverse_proxy metabase:3000
}

If you want more insight into deploying Caddy in a Docker Swarm cluster, check out my previous post on Caddy.

Full Stack Deployment (Combined)

If you prefer to deploy Caddy and Metabase together in a single stack, here is the combined docker-compose.yml file.

version: "3.7"

services:
  caddy:
    image: tuneitme/caddy
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
      - target: 443
        published: 443
        mode: host
        protocol: udp
    networks:
      - caddy
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - /mnt/caddydata:/data
      - /mnt/caddyconfig:/config
      - /mnt/caddylogs:/var/log/caddy
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

  metabase:
    image: metabase/metabase:latest
    depends_on:
      - maria
    ports:
      - '4000:3000'
    volumes:
      - /mnt/metabase:/metabase-data
    environment:
      - MB_DB_TYPE=mysql
      - MB_DB_HOST=maria
      - MB_DB_PORT=3306
      - MB_DB_DBNAME_FILE=/run/secrets/mysql_db
      - MB_DB_USER_FILE=/run/secrets/mysql_user
      - MB_DB_PASS_FILE=/run/secrets/mysql_password
      - MB_DB_FILE=/metabase-data/metabase.db
    secrets:
      - mysql_db
      - mysql_user
      - mysql_password
    networks:
      - caddy
    deploy:
      placement:
        constraints: [node.role == worker]
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

secrets:
  mysql_db:
    file: ./mysql_db.txt
  mysql_user:
    file: ./mysql_user.txt
  mysql_password:
    file: ./mysql_password.txt

volumes:
  caddydata:
    driver: "local"
  caddyconfig:
    driver: "local"
  caddylogs:
    driver: "local"
  metabase:
    driver: "local"

networks:
  caddy:
    external: true

I used a custom Caddy Docker image bundled with specific plugins (like Cloudflare DNS) for my environment: Tuneit Caddy Docker Image

Deploy Metabase to Docker Swarm

Before deploying, ensure you have created the metabase database inside your MariaDB instance and created the .txt files containing your database secrets.

Now, deploy the stack to your Swarm using the following command:

docker stack deploy --compose-file metabase.yml metabase

In Docker Swarm, whatever you deploy via compose is called a “stack,” and it contains multiple “services.”

Check the status of the deployment to ensure it scheduled properly:

docker stack ps metabase

Access and Install Metabase

Open your browser and navigate to metabase.example.com. It will automatically redirect securely to HTTPS. (Ensure you replace example.com with your actual domain).

Ensure you have configured a DNS A-Record or CNAME pointing metabase.example.com to your Swarm ingress load balancer.

Follow the interactive setup wizard to complete the installation.

Reference Images from the Deployment:

Welcome screen to configure Metabase

Metabase Language Selection page

Metabase admin account creation

Metabase Add Data page

Metabase user preferences page

Metabase configuration complete

Metabase subscribe page

Metabase welcome page

Metabase settings option

Metabase login page

Metabase Admin settings page

I hope you enjoyed this tutorial! Please share your thoughts or feedback in the comments below.

Stay tuned for more open-source self-hosting deployments!

Share
Written by
Rajasekhar Gundala

Senior Infrastructure & Web Platform Leader.

Continue reading

Weekly Engineering Notes.

A weekly digest on infrastructure, observability, Rust, and the open web. No spam, just technical signals.

Free. Unsubscribe in one click.