Provision
export HCLOUD_TOKEN=<token from the project>
hcloud ssh-key create --name drew-mbp --public-key-from-file ~/.ssh/id_ed25519.pub
hcloud firewall create --name metabase-fw --rules-file firewall-rules.json
hcloud server create --name brosnan-growth-metabase --type cx22 \
--image ubuntu-24.04 --location ash --ssh-key drew-mbp --firewall metabase-fw
The firewall rules file allows SSH and HTTPS inbound and nothing else. Postgres is never published to the host; it lives on an internal Docker network.
Compose
- postgres:16 with an init script that creates two databases and the read-only
metabase_readerrole with default privileges on future tables. - metabase pinned to one version, with its own database for internal state and a 1.5 GB heap.
- caddy terminating HTTPS with a certificate from Let's Encrypt, proxying to Metabase.
Each container has a memory limit and an OOM score so that if something has to die under pressure, it is Metabase and not the database.
DNS and backups
The Metabase hostname is an A record set to DNS only, because Caddy needs direct traffic to prove it owns the name. A nightly cron runs pg_dump and keeps 14 days of dumps on disk, excluding the raw landing schema because it can be replayed from the API.
Free to borrow for your feed · Square edition
Where you are: a box with HTTPS and nothing in it yet. Next, the data arrives.
