TL;DR
You can run Metabase Community Edition on a $6/month VPS in under an hour using Docker, Nginx, and a free SSL certificate from Let’s Encrypt. the full setup covers provisioning the server, containerising Metabase with a dedicated PostgreSQL metadata store, and sitting Nginx in front as a reverse proxy. you need basic comfort with SSH and a domain name you control.
What You Need Before You Start
- a VPS with at least 2 GB RAM and 1 vCPU (Hetzner CX22, DigitalOcean Basic, or Vultr Cloud Compute all work at roughly $5-6/month)
- Ubuntu 22.04 LTS on that server (the commands below assume this)
- a domain or subdomain pointed at the server’s IP address (an A record, TTL 300 is fine)
- SSH access to the server as a non-root sudo user
- Docker 25+ and Docker Compose v2 (installed in Step 3)
- a local terminal, or use the provider’s browser console if you prefer
- optional: Metabase Cloud if you want zero maintenance, but this guide is for the self-hosted path
Step 1: Provision Your VPS and Create a Sudo User
Log into your hosting provider and spin up a Ubuntu 22.04 instance. pick a region close to your users. once the server is live, SSH in as root:
ssh root@YOUR_SERVER_IP
create a non-root user and give it sudo access:
adduser metauser
usermod -aG sudo metauser
rsync --archive --chown=metauser:metauser ~/.ssh /home/metauser
then switch to that user for everything that follows:
su - metauser
you should now see your shell prompt change to metauser@hostname:~$. running as a non-root user with sudo rights is safer and is how most production Linux work is done.
Step 2: Update the Server and Set the Hostname
Before installing anything, pull in the latest package lists and security patches:
sudo apt update && sudo apt upgrade -y
set a meaningful hostname so you can identify the server later:
sudo hostnamectl set-hostname metabase-server
also make sure your firewall allows SSH, HTTP, and HTTPS:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
you should now see Status: active after the ufw enable command, and the allowed rules listed. do not skip this step. a server without a firewall is a server waiting to be owned.
Step 3: Install Docker and Docker Compose
Docker is the fastest way to run Metabase without wrestling with Java dependencies. install it via the official convenience script:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
add your user to the docker group so you can run containers without sudo:
sudo usermod -aG docker metauser
newgrp docker
verify both Docker and Compose v2 are ready:
docker --version
docker compose version
you should now see output like Docker version 26.x.x and Docker Compose version v2.x.x. if compose is missing, install it with sudo apt install docker-compose-plugin -y.
Step 4: Set Up a PostgreSQL Metadata Database
By default Metabase stores its own config and question history in H2, a file-based database. that is fine for a quick test but H2 corrupts easily on power loss and does not survive container restarts gracefully. swap it for PostgreSQL now while setup is clean.
create a directory for your stack:
mkdir ~/metabase && cd ~/metabase
create a postgres-data subfolder for the volume:
mkdir postgres-data
you will wire this into the compose file in the next step. using a proper relational database for Metabase’s internal state means your dashboards and user permissions survive upgrades without drama.
you should now see ~/metabase/postgres-data exists when you run ls -la ~/metabase/. this directory will hold all PostgreSQL files on the host.
Step 5: Write Your docker-compose.yml
create the compose file:
nano ~/metabase/docker-compose.yml
paste in the following (swap the passwords for something real):
version: "3.9"
services:
postgres:
image: postgres:16-alpine
restart: always
environment:
POSTGRES_DB: metabase
POSTGRES_USER: metabase
POSTGRES_PASSWORD: CHANGE_THIS_PASSWORD
volumes:
- ./postgres-data:/var/lib/postgresql/data
metabase:
image: metabase/metabase:latest
restart: always
ports:
- "3000:3000"
environment:
MB_DB_TYPE: postgres
MB_DB_DBNAME: metabase
MB_DB_PORT: 5432
MB_DB_USER: metabase
MB_DB_PASS: CHANGE_THIS_PASSWORD
MB_DB_HOST: postgres
depends_on:
- postgres
save with Ctrl+O, exit with Ctrl+X. note that Metabase listens on port 3000 inside Docker. Nginx will sit in front and forward public traffic to it in Step 7.
you should now see the file saved without errors. run cat docker-compose.yml to double-check the contents look right before proceeding.
Step 6: Start Metabase and Confirm It Is Running
cd ~/metabase
docker compose up -d
Docker pulls the images (about 500 MB total) and starts both containers. watch the Metabase logs to see when it finishes initialising:
docker compose logs -f metabase
Metabase takes 60-120 seconds to start the first time. you are waiting for this line:
Metabase Initialization COMPLETE
press Ctrl+C to stop following logs once you see it. you can also test locally on the server:
curl -I http://localhost:3000
you should now see an HTTP 200 or 302 response from localhost:3000. if you get Connection refused, wait another 30 seconds and try again.
Step 7: Install Nginx as a Reverse Proxy
running Metabase directly on port 3000 over HTTP is not something you want exposed to the internet. install Nginx and configure it to proxy traffic to the container:
sudo apt install nginx -y
create a site config. replace analytics.yourdomain.com with your actual subdomain:
sudo nano /etc/nginx/sites-available/metabase
server {
listen 80;
server_name analytics.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/metabase /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
you should now see nginx: configuration file ... syntax is ok from the test command. visiting http://analytics.yourdomain.com in a browser should load the Metabase setup screen over plain HTTP.
Step 8: Add a Free SSL Certificate with Certbot
Certbot automates Let’s Encrypt certificates and auto-renews them. install it:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d analytics.yourdomain.com
Certbot will ask for an email address and whether to redirect HTTP to HTTPS. choose option 2 (redirect). it rewrites your Nginx config automatically.
verify auto-renewal works:
sudo certbot renew --dry-run
you should now see Congratulations, all simulated renewals succeeded. your Metabase instance is now available at https://analytics.yourdomain.com with a valid TLS certificate that renews itself every 90 days without you touching it.
Step 9: Complete the Metabase Setup Wizard and Connect Your Data
open https://analytics.yourdomain.com in your browser. Metabase walks you through:
- create your admin account (use a strong password, not
admin@admin.com) - set the instance name
- connect your first database
for the database connection, you need the host, port, database name, username, and password for your actual data source (not the internal PostgreSQL you set up earlier). Metabase supports MySQL, PostgreSQL, BigQuery, Redshift, Snowflake, SQLite, and more.
once connected, Metabase scans your schema and suggests questions automatically. head to Browse Data to verify your tables appear, then try a quick question from + New > Question to confirm queries run.
you should now see your database tables listed under Browse Data with row counts next to them. if the connection fails, double-check that your data source allows inbound connections from your VPS IP address.
For more on connecting external data sources, see how to connect Google Sheets to Metabase and the BI tools category for a broader look at your options.
Common Mistakes To Avoid
- leaving Metabase on H2 in production. H2 works for demos but the file gets corrupted if the container restarts mid-write. set up PostgreSQL as the metadata DB from day one, which is exactly what Step 4 covers.
- skipping the reverse proxy and exposing port 3000 directly. opening port 3000 to the internet means no HTTPS, no host-header filtering, and a much larger attack surface. always put Nginx in front.
- using 1 GB RAM VPS. Metabase’s JVM needs headroom. on a 1 GB instance you will hit OOM kills during complex queries or after a few users log in at once. 2 GB is the real minimum.
- not setting
MB_DB_PASSto something random. the example in every tutorial usespasswordorsecret. use at least a 20-character random string. generate one withopenssl rand -hex 16. - forgetting to back up postgres-data. your dashboards, questions, and user settings live in that volume. add a daily cron job that runs
pg_dumpand ships the output to S3 or Backblaze B2. - running
docker compose upwithout-d. without the detached flag, your Metabase process dies when you close your SSH session. always use-dfor background services.
When To Level Up
self-hosting Metabase on a single VPS is solid for one person or a small team querying a couple of databases. it breaks down in specific situations.
if your queries start timing out because your data source is large and slow, the bottleneck is rarely Metabase itself. you need a dedicated analytical layer like a data warehouse. that is a different problem from where to run Metabase.
if you reach 20+ users all running heavy queries simultaneously, the single container approach struggles. at that point you are looking at a proper Kubernetes deployment or Metabase Pro/Enterprise with its caching and connection pooling features.
if your company’s compliance requirements mean you cannot store user credentials and query history on a server you manage yourself, Metabase Cloud removes that burden and the price difference versus a $6 VPS plus your time is smaller than it looks.
for a wider view of what alternatives exist once you outgrow a simple self-hosted setup, the BI tools category covers Redash, Apache Superset, Grafana, and cloud-managed options side by side. also worth reading: Metabase vs Redash for small teams.
Frequently Asked Questions
does Metabase Community Edition cost anything?
Metabase Community Edition is free and open source under the AGPL license. you pay for the server ($5-6/month) and your domain, nothing else. Metabase Pro and Enterprise add SSO, serialisation, and priority support at a monthly fee.
how much traffic can a 2 GB VPS handle?
comfortably five to ten concurrent users running normal exploratory queries. if everyone hammers complex joins at the same moment you may hit memory limits. monitor with docker stats and upgrade to 4 GB RAM if you routinely see swap usage.
can I connect multiple databases?
yes. Metabase has no limit on the number of database connections in Community Edition. go to Admin > Databases > Add database and add as many as you need. each connection appears as a separate source in Browse Data.
what happens when Metabase releases a new version?
pull the new image and recreate the container. your data is safe in the postgres-data volume.
cd ~/metabase
docker compose pull
docker compose up -d
check the Metabase release notes before upgrading across major versions, as some releases include database migrations that cannot be rolled back.
do I need to know Java to run this?
no. Docker abstracts away the Java runtime entirely. you interact with Linux, Docker, and Nginx. you do not touch the JVM at all unless you want to tune memory settings via the JAVA_OPTS environment variable in the compose file.
Bottom Line
self-hosting Metabase on a VPS is a legitimate production setup for solopreneurs and small teams who want full control over their analytics without a monthly SaaS bill. the core path is straightforward: spin up a 2 GB Ubuntu server, run Metabase and PostgreSQL in Docker Compose, put Nginx in front with a free Let’s Encrypt certificate, and connect your database. the whole process takes under an hour if your DNS has propagated. the main gotchas are using H2 in production and skipping the reverse proxy, both of which this guide avoids from the start. once your instance is live, you can build questions, dashboards, and automated reports without touching the server again. if you want to explore what else the BI tool landscape offers beyond Metabase, start with the BI tools guide on this site.