What it costs
From 1 TB of data up, BigQuery and Snowflake cost 12 to 37 times as much as one server sized for the same work. At 50 GB the server costs $22 a month, BigQuery $88 and Snowflake $1,210.
The bigger difference is the number of questions. Agents ask far more of them than people do, and a cloud warehouse bills every one. A server is sized for your busiest hour and costs the same however many questions fit in it. And when something goes wrong, a warehouse sends a bigger bill, while a server only gets slower.
These are list prices read on 27 October 2026, for three made-up but ordinary companies. Every figure below is computed from one data file, so you can change a price or an assumption and run it again. The calculator on metricsbook.io runs the same arithmetic for any amount of data and questions.
More agents, more queries
Section titled “More agents, more queries”A warehouse’s bill comes mostly from the demand side: the queries and the work they do, not the data you store. Here is the 1 TB company from the cases below, with the same data and the same dbt runs, at three volumes of questions:
- 300 queries a day: about what 10 people asking by hand run. The warehouses cost 5 to 37 times as much as the server.
- 3,000 a day: the warehouses cost 12 to 37 times as much.
- 30,000 a day: 50 people each putting 20 questions a day to an agent that runs 30 queries per answer. The warehouses cost 14 to 28 times as much, and the server is a size bigger, because its busiest hour needs more threads.
| Option | 300 queries a day | 3,000 queries a day | 30,000 queries a day |
|---|---|---|---|
| Hetzner AX41 | $70 (1.0x) | $70 (1.0x) | |
| Netcup RS 4000 G12.5 | $77 (1.1x) | $77 (1.1x) | |
| BigQuery on-demand | $358 (5.2x) | $853 (12.3x) | $5,797 (27.9x) |
| Snowflake Enterprise | $2,594 (37.3x) | $2,609 (37.5x) | $2,843 (13.7x) |
Snowflake costs nearly the same at 300 questions a day as at 3,000. A warehouse bills every second it is awake, and it stays awake for 10 minutes after each query by default. Questions that arrive through the working day keep it up all day, so a small company with many questions pays for a warehouse that runs from morning to evening.
What a mistake costs
Section titled “What a mistake costs”On a cloud warehouse, a mistake shows up on the bill. Say an agent at the 1 TB company gets stuck in a loop: every 10 seconds it runs a query that reads all of the data, and nobody notices until the invoice arrives a month later.
| Option | Planned month | Month with a stuck loop |
|---|---|---|
| Hetzner AX41 | $70 | $70 |
| Netcup RS 4000 G12.5 | $77 | $77 |
| BigQuery on-demand | $853 | $37,522 |
| Snowflake Enterprise | $2,609 | $5,651 |
- BigQuery on-demand bills every byte a query reads. The loop asks for 7,858 TiB a day. Google stops each project at 200 TiB a day by default, which is still $37,522 for the month.
- Snowflake bills the hours a warehouse is awake, and the loop keeps it awake around the clock at the size you picked: $5,651 for the month. Pick a bigger size and the bill grows with it: a Large warehouse uses 4 times the credits of the Small one here. With auto-scaling on, a loop that runs queries side by side also starts extra clusters, each billed in full.
- Each vendor lets you set a ceiling: a custom quota in BigQuery, a resource monitor in Snowflake. Snowflake has none until you create one. Each protects you only once someone has set it, at the right level.
On your own server, the worst case is slow answers. The loop uses up the CPU and memory, other queries wait, and the bill stays the same. You find out because things get slow, not because an invoice arrives.
Then the agent finds out why. Today every load and dbt run already records each step’s wall time, CPU time and peak memory. The ops commands designed for phase 7 put that in front of the agent:
mb ops topranks what used the most time, CPU and memory across steps and queries.mb ops queries --slowshows the slowest metrics, who called them and the SQL they ran.mb ops checkfails when a run breaks a budget you set inperf/budgets.yml, such as query p95 under 800 ms.mb ops diskshows how many days are left until the disk is full.
With those, the agent fixes the cause, such as a loop or a model that reads too much, or tells you the machine is too small and which one to move to. Moving to a bigger machine is a decision you make, at a price you see first. The ops commands and the monitoring are not built yet; the changelog says where they stand.
How the comparison works
Section titled “How the comparison works”- Prices: public list prices, on demand, in EU regions, without VAT. Hetzner and Netcup charge in euros, converted to dollars at the European Central Bank’s reference rate. A month is 30 days. A setup fee is spread over 12 months. Netcup’s price is for a 12-month contract.
- The server: the cheapest machine from each vendor with the cores, memory and disk the case needs, running the whole stack: DuckDB, dlt, dbt, anfra, the API and Postgres. Netcup sells extra NVMe for a root server by the GB; Hetzner’s servers keep the disks they come with. Each server also pays for a Hetzner Storage Box big enough to back up its data.
- Queries: each case sets queries a day, the GiB each one scans on BigQuery and the seconds each keeps a Snowflake warehouse busy. BigQuery is billed for the bytes scanned, dbt’s included. Every query is counted, with no result cache.
- The warehouses: each also pays for a small server (Hetzner Cloud CX33), because the API, anfra, Postgres and the jobs still have to run somewhere.
- Left out on both sides: network egress, BI tool licences, committed-use discounts, BigQuery’s capacity editions, Snowflake’s cloud services above its free 10%, and people’s time.
How the server is sized
Section titled “How the server is sized”The server is sized from a measurement, not a guess. We ran TPC-H at scale factor 10 in DuckDB 1.5.6, the version the stack pins, on an Apple M5 Pro, one thread at a time. Query 18 was the slowest per byte: it reads 1.94 GiB as BigQuery would bill it, in 2.61 seconds. DuckDB stored the 13.33 GiB of data in 2.67 GB, about 19% of its size on BigQuery.
From that, each case gets a server with:
- Threads: enough that the busiest hour, which carries 20% of a day’s questions, keeps DuckDB’s threads busy at most 50% of the time; that a typical question returns in about 2 seconds; and that dbt finishes in a 4-hour night. At least 2. We assume a server thread does a third of the work of the laptop’s core, so DuckDB reads 0.25 GiB a second on each.
- Cores: the threads, plus 2 for the rest of the stack.
- Memory: 4 GB per thread, the top of what DuckDB’s guide asks for joins, but never more than the data itself; plus 4.5 GB for the API, Postgres, anfra, Cerbos, monitoring and the operating system, at the stack’s own container limits; plus 5% of the data kept in memory, such as the last few weeks of two years’ history.
- Disk: DuckDB’s files 2 times over, because dbt builds a table next to the old one before it swaps them, plus 40 GB for the system.
This is a careful sizing, not a benchmark of Metrics Book’s own workload. Two things to know. The stack’s compose file caps the jobs container, where dlt and dbt run DuckDB, at 2.5 GB, and anfra at 2 GB, so a bigger server only helps once those limits are raised to match it. And no server in the list holds 10 TB at 30,000 queries a day: that needs more than 128 GB of memory.
How Snowflake is billed
Section titled “How Snowflake is billed”- Enterprise edition. Auto-scaling across several clusters needs it, and its credit costs half as much again as Standard’s.
- Every second the warehouse is awake. Snowflake bills a warehouse while it runs, and by default it suspends only after 10 idle minutes. Questions arrive at random through a 10-hour working day, so with N questions, each keeping the warehouse up for 10 minutes plus its run time, it is awake for T × (1 - e^(-N × s / T)) of the day’s T seconds, where s is the 10 minutes plus the run time. dbt’s hours come on top, at night.
- Extra clusters. When the busiest hour runs more queries at once than one cluster takes (8 by default), auto-scaling starts another, and Snowflake stops it only after a sustained period of low load. We bill each extra cluster for the busiest hour, which is the least it costs.
Setting auto-suspend to one minute lowers the bill, and Snowflake bills at least a minute each time a warehouse resumes.
Hetzner is missing from the 10 TB case. Its servers with enough disk show no price on a public page we could read.
Three cases
Section titled “Three cases”50 GB, 500 queries a day
50 GB of data. dbt runs 0.25 hours a day. Agents, reports and people ask 500 queries a day, each scanning 0.6 GiB on BigQuery and keeping a warehouse busy for 20 seconds: 0.4 TiB scanned a day, dbt included, and an X-Small Snowflake warehouse awake 10.2 hours a day. The server needs 4 cores, 13 GB of memory and 59 GB of disk.
| Option | What you pay for | Per month | Times the cheapest server |
|---|---|---|---|
| Hetzner Cloud CX43 | Cloud CX43: 8 shared vCPUs, 16 GB RAM, 160 GB NVMe: $18; Storage Box BX11, 1 TB, for backups: $4 | $22 | 1.0 |
| Netcup RS 2000 G12.5 | RS 2000 G12.5: 8 dedicated cores, 16 GB RAM, 256 GB NVMe: $38; Storage Box BX11, 1 TB, for backups: $4 | $42 | 1.9 |
| BigQuery on-demand | 13 TiB scanned a month: $77; 47 GiB stored: $1; Hetzner Cloud CX33 for the rest of the stack: $10 | $88 | 4.0 |
| Snowflake Enterprise | X-Small warehouse, awake 10.2 hours a day (307 credits a month): $1,199; 0.05 TB stored: $1; Hetzner Cloud CX33 for the rest of the stack: $10 | $1,210 | 54.9 |
1 TB, daily dbt runs and 3,000 queries a day
1 TB of data. dbt runs 1 hour a day. Agents, reports and people ask 3,000 queries a day, each scanning 1 GiB on BigQuery and keeping a warehouse busy for 8 seconds: 4.4 TiB scanned a day, dbt included, and a Small Snowflake warehouse awake 11.0 hours a day. The server needs 5 cores, 26 GB of memory and 413 GB of disk.
| Option | What you pay for | Per month | Times the cheapest server |
|---|---|---|---|
| Hetzner AX41 | AX41: 6 cores (AMD Ryzen 5 3600), 64 GB RAM, 2 x 512 GB NVMe mirrored: $66; Storage Box BX11, 1 TB, for backups: $4 | $70 | 1.0 |
| Netcup RS 4000 G12.5 | RS 4000 G12.5: 12 dedicated cores, 32 GB RAM, 512 GB NVMe: $73; Storage Box BX11, 1 TB, for backups: $4 | $77 | 1.1 |
| BigQuery on-demand | 133 TiB scanned a month: $824; 931 GiB stored: $18; Hetzner Cloud CX33 for the rest of the stack: $10 | $853 | 12.3 |
| Snowflake Enterprise | Small warehouse, awake 11.0 hours a day (660 credits a month): $2,574; 1 TB stored: $25; Hetzner Cloud CX33 for the rest of the stack: $10 | $2,609 | 37.5 |
10 TB, dbt through the night and 10,000 queries a day
10 TB of data. dbt runs 3 hours a day. Agents, reports and people ask 10,000 queries a day, each scanning 1.5 GiB on BigQuery and keeping a warehouse busy for 4.5 seconds: 29.6 TiB scanned a day, dbt included, and a Medium Snowflake warehouse awake 13.0 hours a day. The server needs 9 cores, 126 GB of memory and 3771 GB of disk.
| Option | What you pay for | Per month | Times the cheapest server |
|---|---|---|---|
| Netcup RS 16000 G12.5 | RS 16000 G12.5: 24 dedicated cores, 128 GB RAM, 2 TB NVMe: $272; 1771 GB extra NVMe: $24; Storage Box BX21, 5 TB, for backups: $12 | $308 | 1.0 |
| BigQuery on-demand | 889 TiB scanned a month: $5,553; 9313 GiB stored: $186; Hetzner Cloud CX33 for the rest of the stack: $10 | $5,749 | 18.7 |
| Snowflake Enterprise | Medium warehouse, awake 13.0 hours a day (1560 credits a month): $6,084; 10 TB stored: $245; Hetzner Cloud CX33 for the rest of the stack: $10 | $6,339 | 20.6 |
Sources
Every price, as the vendor's own page showed it on 27 October 2026.
Run it yourself
Section titled “Run it yourself”The prices, the sizing rules and the three cases live in apps/docs/src/data/cost-comparison.json, and the arithmetic in apps/docs/src/lib/costs.ts. Change a value, then print the result:
pnpm --filter @metrics-book/docs costsThis page is built from the same files, so it always shows what the script prints.