There are two ways to build Metabase dashboards on a private database, one that has no public endpoint. Metabase Cloud connects through an SSH tunnel to a bastion host that accepts the Metabase Cloud IP addresses, and the bastion reaches the database on its private IP. A self-hosted Metabase can run inside the same VPC as the database on AWS, Google Cloud, or Azure, so the database never accepts a connection from outside your network.
Metabase Cloud connects to databases in private networks through SSH tunneling. Whichever way you choose, connect Metabase with a read-only database user. The steps below follow the current Metabase docs.
Which option fits your private database
Use Metabase Cloud with an SSH tunnel when you can run a small bastion host that accepts SSH from three IP addresses. Metabase then hosts, upgrades, and backs up the Metabase app for you.
Run Metabase inside your VPC when your security policy forbids inbound connections from outside the network, or when the data must stay inside your own cloud account.
Option 1: Metabase Cloud through an SSH tunnel
A bastion host is a small server at the edge of your private network that accepts SSH connections. Metabase opens an SSH connection to the bastion, and then connects from the bastion to the database inside the network. Metabase has a built-in SSH client, so you set up the whole tunnel in the database connection form.
- Run a bastion host in a public subnet of the database’s VPC, and set
AllowTcpForwarding yesin its SSH server configuration. - Allow inbound SSH on port 22 to the bastion only from the Metabase Cloud IP addresses for your region.
- Allow the database port on the database only from the bastion.
- In Metabase, go to Admin > Databases > Add a database, pick your database type, and turn on Use an SSH tunnel.
- In Host and Port, enter the database’s hostname and port as seen from inside the network, such as the RDS endpoint or a private IP on port 5432.
- In SSH tunnel host, enter the bastion’s public name or IP, and in SSH tunnel port, enter the SSH port, which is usually 22.
- For authentication, paste an SSH private key (and its passphrase, if it has one), or enter a username and password.
If the connection fails, test the SSH login from your own machine with the same values:
ssh <SSH tunnel username>@<SSH tunnel host> -p <SSH tunnel port>
If the network accepts no inbound connections at all, use a reverse SSH tunnel. A server inside the private network opens a connection to an intermediate server, and Metabase connects to that intermediate server.
Metabase Cloud IP addresses to allow
Metabase Cloud connects to your databases from three fixed IP addresses in each region. Add the three addresses for your Metabase’s region to your security group or firewall rules:
- United States (North Virginia),
us-east-1: 18.207.81.126, 3.211.20.157, and 50.17.234.169. - Europe (Frankfurt),
eu-central-1: 18.192.2.142, 18.184.191.58, and 3.65.184.173. - Asia Pacific (Singapore),
ap-southeast-1: 54.179.42.215, 3.1.176.8, and 18.139.48.211. - Asia Pacific (Sydney),
ap-southeast-2: 13.238.131.1, 3.105.237.132, and 54.252.39.14. - South America (São Paulo),
sa-east-1: 18.228.120.123, 54.207.106.108, and 18.228.120.162.
The same addresses work when your database has a public endpoint behind a firewall. In that case, allow them on the database port, and you don’t need a tunnel. The Metabase Cloud IP allowlist setting is a different feature: it limits who can reach your Metabase, and it’s part of the Pro and Enterprise plans on Metabase Cloud.
Security group inbound rules on AWS
On AWS, the bastion’s security group needs an inbound rule for TCP port 22 from each Metabase Cloud address. The database’s security group needs an inbound rule for the database port from the bastion’s security group. These AWS CLI commands set up both rules for us-east-1 and PostgreSQL:
BASTION_SG=sg-0123456789abcdef0 # security group of the bastion host
DB_SG=sg-0fedcba9876543210 # security group of the database
# Bastion: allow SSH only from the Metabase Cloud addresses for us-east-1.
for ip in 18.207.81.126 3.211.20.157 50.17.234.169; do
aws ec2 authorize-security-group-ingress \
--group-id "$BASTION_SG" \
--protocol tcp \
--port 22 \
--cidr "$ip/32"
done
# Database: allow PostgreSQL (port 5432) only from the bastion's security group.
aws ec2 authorize-security-group-ingress \
--group-id "$DB_SG" \
--protocol tcp \
--port 5432 \
--source-group "$BASTION_SG"
You don’t need an extra inbound rule for return traffic. AWS security groups are stateful, so the response to a request that a rule allows comes back regardless of the inbound rules. AWS also recommends that inbound rules for port 22 allow only specific IP address ranges, which is what the three /32 rules do.
Option 2: run Metabase inside your VPC
Metabase ships as an official Docker image that runs on any system with Docker. Run it in a private subnet of the VPC that holds your database. Then allow the database port from the Metabase host’s security group, the same way as the bastion rule above. Metabase stores its own questions and dashboards in an application database, so for production use PostgreSQL or MySQL instead of the built-in H2 file.
This command follows Running Metabase on Docker and points Metabase at a PostgreSQL application database:
docker run -d -p 3000:3000 \
-e "MB_DB_TYPE=postgres" \
-e "MB_DB_DBNAME=metabaseappdb" \
-e "MB_DB_PORT=5432" \
-e "MB_DB_USER=metabase" \
-e "MB_DB_PASS=your-password" \
-e "MB_DB_HOST=metabase-app-db.internal.example.com" \
--name metabase metabase/metabase:v0.64.1
Your team then reaches Metabase through a load balancer or your VPN, like any internal web app. Here is how the same setup looks on each cloud:
- On AWS, run the container on an EC2 instance or on Amazon ECS in the database’s VPC. Metabase no longer recommends Elastic Beanstalk for production. On self-hosted Pro and Enterprise plans, Metabase can also connect to RDS and Aurora PostgreSQL or MySQL with IAM authentication instead of a password.
- On Google Cloud, run Metabase on a Compute Engine VM in the same VPC network as your database. A Cloud SQL instance with a private IP accepts connections from inside its VPC network, so you put the private IP in the Host field.
- On Azure, follow Running Metabase on Microsoft Azure, which runs the Docker image as a Web App with VNet integration. The guide reaches Azure Database for PostgreSQL through a private endpoint and denies public network access, and the same pattern works for the database you analyze.
Redshift Serverless in a private VPC
A Redshift Serverless workgroup runs inside a VPC. Its endpoint has the form workgroup-name.account-number.aws-region.redshift-serverless.amazonaws.com, the default port is 5439, and the default database is named dev. In Metabase, choose Amazon Redshift, enter the endpoint in Host, 5439 in Port, and your database name.
From Metabase Cloud, turn on the SSH tunnel and use a bastion EC2 instance in the workgroup’s VPC. Then add an inbound rule on port 5439 to the workgroup’s security group from the bastion’s security group. A self-hosted Metabase in the same VPC connects to the endpoint directly, once the workgroup’s security group allows port 5439 from the Metabase host’s security group. For Metabase’s sync and scan to work, the database user needs access to information_schema.
Cloud SQL with a private IP
A Cloud SQL instance that only has a private IP has no endpoint that Metabase Cloud can reach directly. Create a small Compute Engine VM with a public IP in the same VPC network, give it the network tag metabase-bastion, and use it as the bastion. This firewall rule allows SSH to it only from the Metabase Cloud addresses for us-east-1:
gcloud compute firewall-rules create allow-metabase-cloud-ssh \
--network=default \
--direction=INGRESS \
--allow=tcp:22 \
--source-ranges="18.207.81.126/32,3.211.20.157/32,50.17.234.169/32" \
--target-tags=metabase-bastion
In Metabase, enter the instance’s private IP in Host and the VM’s public IP in SSH tunnel host. Cloud SQL needs private services access before an instance can use a private IP.
If your instance has a public IP instead, add the Metabase Cloud addresses as authorized networks. Run gcloud sql instances patch INSTANCE_ID --authorized-networks=... with every range you need, because the command replaces the existing list.
Create a read-only database user
Metabase recommends a dedicated metabase database user with read-only access to the schemas you analyze. The Metabase docs group the privileges in an analytics role and give that role to the user. On PostgreSQL, run these statements as an admin:
CREATE ROLE analytics;
GRANT CONNECT ON DATABASE your_database TO analytics;
GRANT USAGE ON SCHEMA public TO analytics;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analytics;
-- Also cover tables that the role running this statement creates later.
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO analytics;
CREATE USER metabase WITH PASSWORD 'use-a-long-random-password';
GRANT analytics TO metabase;
On PostgreSQL 14 or later, GRANT pg_read_all_data TO analytics; gives read access to all data in one statement. Keep the Metabase user read-only unless you turn on a feature that writes to your database, such as CSV uploads.
Where Amazon Quick Sight fits
Amazon Quick Sight continues Amazon QuickSight inside Amazon Quick. It fits teams that want their BI service from AWS, set up in the AWS Management Console and billed with their AWS account. Its Enterprise edition reaches private data through VPC connections, which add network interfaces to your VPC. Metabase reaches the same private databases through an SSH tunnel from Metabase Cloud or from a Metabase you run in the VPC. Both ways also work on Google Cloud and Azure.