How to Install Metabase
Metabase is the most popular open source BI tool, and its Docker-based deployment is designed to be accessible even for teams without deep DevOps experience. The installation described here produces a production-ready setup suitable for organizations with up to 50 concurrent dashboard users. For larger deployments, the same architecture scales by increasing server resources.
Step 1: Prepare Your Server
Start with a Linux server running Ubuntu 24.04 LTS or Debian 12. The minimum hardware requirements are 2 CPU cores, 4 GB of RAM, and 20 GB of SSD storage. For comfortable performance with 10-20 concurrent users, 4 GB of RAM is recommended because Metabase runs on the JVM, which benefits from available heap space.
Install Docker Engine and the Docker Compose plugin following the official Docker documentation for your distribution. On Ubuntu, this involves adding the Docker repository, installing the docker-ce and docker-compose-plugin packages, and adding your user to the docker group. Verify the installation by running docker --version and docker compose version.
Configure your firewall to allow only SSH (port 22), HTTP (port 80), and HTTPS (port 443). On Ubuntu, use ufw: enable it, then allow OpenSSH, allow 80/tcp, and allow 443/tcp. Deny all other incoming traffic by default. Metabase will run on an internal port (3000) that is not exposed to the public internet, because Nginx will proxy requests to it.
Step 2: Create the Docker Compose Configuration
Create a project directory for your Metabase deployment, such as /opt/metabase. Inside it, create a docker-compose.yml file that defines two services: the Metabase application and a PostgreSQL database for storing Metabase's internal data (dashboards, saved questions, user accounts, and permissions).
The Metabase service uses the official metabase/metabase Docker image. Pin the image to a specific version tag (such as v0.60.2) rather than using "latest" so that updates are deliberate and tested rather than automatic. Map the container's port 3000 to localhost only (127.0.0.1:3000:3000) so that the Metabase web interface is accessible only through the Nginx reverse proxy, not directly from the internet.
The PostgreSQL service uses the official postgres image (version 16 is current and well-tested). Define a named Docker volume for PostgreSQL data persistence so that your metadata survives container restarts and rebuilds. Set the POSTGRES_DB, POSTGRES_USER, and POSTGRES_PASSWORD environment variables to configure the database. Use a strong, randomly generated password of at least 32 characters.
Step 3: Configure Environment Variables
Metabase reads its configuration from environment variables, which you can define in the docker-compose.yml file or in a separate .env file. The essential variables for a production deployment are the database connection (MB_DB_TYPE, MB_DB_DBNAME, MB_DB_PORT, MB_DB_USER, MB_DB_PASS, MB_DB_HOST), the encryption key (MB_ENCRYPTION_SECRET_KEY), and the site URL (MB_SITE_URL).
Set MB_DB_TYPE to postgres and configure the remaining database variables to match the PostgreSQL service defined in your Docker Compose file. The MB_DB_HOST value should be the service name of your PostgreSQL container (such as "postgres") since Docker Compose creates a network where services can reach each other by name.
Generate a random 32-character string for MB_ENCRYPTION_SECRET_KEY using openssl rand -hex 16. This key encrypts sensitive data stored in the application database, including database connection passwords. Store this key securely and back it up, because changing it will invalidate all stored credentials and require re-entering them. Set MB_SITE_URL to the public URL where users will access Metabase (such as https://bi.yourdomain.com).
Optionally, tune the JVM memory allocation with JAVA_OPTS. Setting -Xmx2g allocates up to 2 GB of heap memory to Metabase, which improves performance on servers with sufficient RAM. The default allocation works for small deployments, but larger instances benefit from explicit memory tuning.
Step 4: Launch Metabase
From your project directory, run docker compose up -d to start both containers in detached mode. Docker will pull the Metabase and PostgreSQL images on first run, which may take a few minutes depending on your internet connection. Once the containers are running, check the Metabase logs with docker compose logs -f metabase to monitor the startup process.
Metabase takes 30 to 90 seconds to initialize on first launch while it creates its database schema and performs setup tasks. Watch the logs for the message "Metabase Initialization COMPLETE" which confirms that the application is ready. You can verify access by running curl http://localhost:3000 from the server, which should return the Metabase HTML page.
If the container fails to start, the most common causes are incorrect database connection variables (check that the host, port, username, password, and database name match your PostgreSQL configuration) or insufficient memory (ensure at least 4 GB of RAM is available to the server). The Metabase logs will indicate the specific error.
Step 5: Complete Initial Setup
Access Metabase through your browser at http://your-server-ip:3000 (this temporary direct access is acceptable during initial setup, before Nginx is configured). The setup wizard walks you through creating your admin account, setting the organization name, and optionally connecting your first data source.
Create a strong password for the admin account. This account has full control over all settings, users, and data sources. After the initial setup, create individual user accounts for team members rather than sharing the admin credentials. Metabase supports email-based account creation, Google OAuth, and LDAP authentication for user management.
When connecting your first data source, use read-only database credentials. Create a dedicated database user on your production database that has SELECT permissions only, with no ability to INSERT, UPDATE, DELETE, or modify schema. This protects your production data from accidental or malicious modifications through the BI tool.
Step 6: Set Up Nginx and SSL
Install Nginx from your distribution's package manager. Create a server block configuration that listens on port 80, includes your domain name, and proxies all requests to http://127.0.0.1:3000. Include proxy headers to pass the original client IP address and protocol to Metabase (X-Forwarded-For, X-Forwarded-Proto, X-Real-IP, and Host headers).
Install Certbot and its Nginx plugin to obtain a free SSL certificate from Let's Encrypt. Run the certbot command with the --nginx flag and your domain name. Certbot will automatically modify your Nginx configuration to handle SSL termination, add a redirect from HTTP to HTTPS, and set up automatic certificate renewal via a systemd timer or cron job.
After Certbot completes, verify that HTTPS works by accessing your Metabase instance through the domain name. Update the MB_SITE_URL environment variable to use the HTTPS URL, then restart the Metabase container for the change to take effect. At this point, remove the temporary direct port 3000 access by changing the Docker Compose port mapping from 3000:3000 to 127.0.0.1:3000:3000 if it was not already restricted.
Step 7: Harden for Production
Set up automated daily backups of the PostgreSQL database. A cron job that runs pg_dump and stores the output in a compressed file, then copies it to offsite storage (S3, another server, or a backup service) provides basic protection against data loss. Test the restoration process by restoring a backup to a separate database and verifying that Metabase functions correctly with the restored data.
Configure basic monitoring to detect outages. A health check script that pings the Metabase /api/health endpoint every 5 minutes and sends a notification (via email, Slack webhook, or a monitoring service like Uptime Robot) when it fails will alert you to problems before users notice them. Monitor disk space and set up alerts at 80% utilization, because a full disk will crash both Metabase and PostgreSQL.
Establish an update schedule. Check the Metabase release notes monthly and apply updates that include security fixes. The update process with Docker is straightforward: pull the new image version, update the version tag in docker-compose.yml, and run docker compose up -d. Always back up the application database before updating, in case a version upgrade introduces issues that require rolling back.
A production Metabase installation requires Docker, PostgreSQL for metadata, Nginx for reverse proxying, SSL from Let's Encrypt, and automated backups. The entire setup runs comfortably on a $20 per month VPS and serves analytics to your entire organization without per-user licensing fees.