Every managed data platform quote I got landed in the same place: over a thousand euros a month before a single dashboard existed, on US-owned infrastructure, with my data living somewhere I didn't control. I needed an analytics platform — ingest a handful of sources, model them, put numbers in front of non-technical people — and the modern data stack has perfectly good open-source answers for every layer of that. So I self-hosted the whole thing on Hetzner. It runs for under €200 a month. Here's the stack, the real trade-offs, and the two decisions I got wrong first.
What "managed" actually costs
The pitch for managed platforms is that they save you operational work, and they do. What the pitch skips is the shape of the bill. Microsoft Fabric quoted over €1,000/month for my workload. Snowflake and Databricks are usage-priced, which sounds cheaper until a backfill or a careless dashboard scans more than you budgeted for — the number is unpredictable by design. And all three put my data on infrastructure I don't own, under a jurisdiction I'd have to explain to a data protection officer.
None of that is a dealbreaker for everyone. If you have no ops capacity and a generous budget, managed is the right answer. But I had the opposite: a tight budget, a hard requirement for EU data sovereignty, and enough Kubernetes and SQL to run infrastructure myself. That combination points straight at self-hosting.
The stack, layer by layer
The "modern data stack" is really just a set of roles — storage, ingestion, a query engine, transformation, orchestration, and a BI layer — each of which now has a solid open-source option. Here's what I picked and, more usefully, why.
Storage: object storage + Apache Iceberg
The lake sits in Hetzner Object Storage (S3-compatible) as Apache Iceberg tables. Iceberg gives you table semantics — schema evolution, time travel, atomic writes — on top of plain Parquet files you fully own. The format is open, so nothing about this layer locks me to a vendor or even to the query engine. That single choice is what makes the rest of the stack swappable.
Query engine: ClickHouse on dedicated hardware
ClickHouse is the core, and it runs on a dedicated Hetzner box rather than inside my Kubernetes cluster. That was deliberate: an analytics column store wants direct access to fast local NVMe and predictable memory, and putting it in K8s next to noisy neighbours buys you nothing but scheduling headaches. A dedicated machine with a lot of RAM and mirrored NVMe costs a fraction of a managed warehouse and outruns it on the queries I actually run.
Transformation: dbt
Transformation is dbt with the ClickHouse adapter. dbt is the one layer where I'd have made the same choice managed or not — it's become the default for turning raw tables into modelled ones, and the dbt-clickhouse adapter is mature enough for real work. Models are version-controlled SQL, tested in CI, and materialised straight into ClickHouse. It's niche enough that you'll occasionally hit an adapter edge the Postgres or Snowflake worlds never see, but for standard modelling it simply works.
Orchestration: Dagster
Everything is stitched together by Dagster, running as open-source in Kubernetes. I went with Dagster over Airflow because it models the pipeline as assets — "this table exists and depends on that source" — rather than as a bag of tasks. When the question is "is this dashboard's data fresh and where did it come from", an asset graph answers it directly. The whole ingest → dbt → BI chain is one lineage graph I can look at.
BI: a self-hosted dashboard layer
For the last mile — numbers in front of people who will never write SQL — I use a self-hosted BI tool backed by its own Postgres. It reads straight from ClickHouse, so dashboards are fast, and because it's self-hosted the data never leaves the platform to be rendered. This is the layer that justifies the whole thing to everyone who isn't an engineer.
Ingestion: the part I got wrong first
My original design pulled changes out of the source databases with Debezium, the standard change-data-capture route. On paper it's the right answer: near-real-time, battle-tested, exactly what CDC is for. In practice it was too much moving machinery for what I actually needed. Debezium wants a Kafka-shaped world around it, and my sources didn't change fast enough to justify that weight.
So I reversed it. The batch sync now runs on dlt — a small Python library that reads a source and lands it in the lake on a schedule — with Dagster triggering it. It's less impressive on a diagram and far less to operate, and for data that's fresh enough at hourly or daily granularity it's simply the correct trade. The lesson I keep relearning: pick the ingestion cadence your data actually has, not the fastest one available.
What it costs — money and otherwise
The infrastructure bill is under €200/month: the dedicated ClickHouse machine, the object storage, and a small Kubernetes cluster carrying the orchestration and BI pods. That's a fraction of the managed quote, on hardware in an EU data centre, with my data in open formats I could move anywhere tomorrow.
The honest cost is on the other side of the ledger. Self-hosting means you own the pager. A managed warehouse that falls over is someone else's 3 a.m.; mine is mine. Until I add a second node the query engine is a single point of failure, and every component is one more thing to patch, back up and understand. If you can't comfortably operate Kubernetes and a database, that operational load will eat any money you saved. That number is real, but it isn't free — it's a trade of cash for control and competence.
Would I do it again?
For this situation, without hesitation. The combination that made it right — a real budget ceiling, a hard EU-sovereignty requirement, and the in-house ability to run it — is common enough that a lot of teams are quietly in the same position and defaulting to managed anyway. If that's you, the modern data stack self-hosts cleanly, and the pieces are all open source and genuinely good now in a way they weren't a few years ago.
Frequently asked questions
Is a self-hosted data stack really cheaper than Snowflake or Fabric?
On raw infrastructure, dramatically — under €200/month here versus €1,000+ quoted. But that number ignores your own time. If you have to hire or divert an engineer to run it, the managed premium can be the cheaper option. It's cheaper in money, not necessarily in total cost.
Why ClickHouse instead of just Postgres?
Postgres is a row store; analytics queries scan columns across millions of rows, which is exactly what a column store like ClickHouse is built for. For transactional workloads Postgres wins; for aggregations over large tables ClickHouse is often orders of magnitude faster.
Do I need Kubernetes for this?
No. I use it because I already ran a cluster and it makes the orchestration and BI pods easy to manage, but you can run the same stack with Docker Compose on a couple of machines. The query engine deliberately sits on dedicated hardware either way.
Why Dagster over Airflow?
Airflow models tasks; Dagster models data assets and their lineage. For an analytics pipeline where the real question is "is this table fresh and where did it come from", the asset model answers it natively. Airflow is the safer choice if your team already knows it well.
