Skip to content

Cost comparison between gcp and hetzner configuration

TMS applications — GCP resource & cost inventory

Apps covered: tms-booking, tms-drivers, tms-feedback, tms-planner, tms-reports (plus tms-fleet, which shares the same infrastructure).

Projects covered:

  • attracs-services-195009 — compute, networking, Pub/Sub, MySQL (attracs-sql)
  • pg-databases-stable — PostgreSQL instance hosting most TMS databases

Billing window: last 30 days, 2026-05-262026-06-24. Source: attracs-billing.attracs_billing_dataset.gcp_billing_export_resource_v1_01FE17_D9735C_415ACA Currency: EUR. Net = gross cost + credits.


TL;DR

Bucket Net EUR / 30d
TMS-named resources in attracs-services-195009 2,771.36
attracs-sql (MySQL, hosts tms_integration_service + Superset for tms-reports) 366.43
pg-1-stable + read replica + backups in pg-databases-stable (shared, 3 of ~30 DBs are TMS) 2,024.11
Conservative TMS-attributable total ~5,162
Whole-project totals for context (attracs-services-195009 + pg-databases-stable) 9,635.17

Per-app cost cannot be split cleanly — the 5 TMS apps share the same VMs, the same PostgreSQL instance, the same load balancer, and the same Spanner. Only Pub/Sub topics are app-named, so only Pub/Sub is split per-app below.


1. Compute & networking — attracs-services-195009

1.1 TMS backend VMs (shared by all 5 apps via path routing)

Resource Type Status Service Net EUR / 30d
tms-confidential-instance-stable-1 n2d-standard-16 RUNNING (zone a) Compute + Confidential 453.58
tms-confidential-instance-stable-2 n2d-standard-16 RUNNING (zone b) Compute + Confidential 456.44
tms-confidential-instance-stable-3 n2d-standard-16 RUNNING (zone c) Compute + Confidential 450.27
tms-confidential-instance-stable-4 n2d-standard-16 TERMINATED Compute 9.25
tms-backend-5 n2-highcpu-16 TERMINATED Compute 15.72
tms-backend-6 n2-highcpu-16 TERMINATED Compute 31.44
attracs-tms-backends-multi-zone-2zdb (MIG VM) TERMINATED Compute 15.72
attracs-tms-backends-multi-zone-zq8q (MIG VM) TERMINATED Compute 15.72
tms-valhalla-1 e2-standard-4 TERMINATED Compute 9.25

1.2 Reservations / committed-use backing the running VMs above

Resource SKU Net EUR / 30d
tms-confidential-instance-stable-zone-a Reserved N2D AMD Core/RAM (Finland) 557.99
tms-confidential-instance-stable-zone-b Reserved N2D AMD Core/RAM (Finland) 278.99
tms-confidential-instance-stable-zone-c Reserved N2D AMD Core/RAM (Finland) 278.99

1.3 External / reserved IPs

Resource Status Service Net EUR / 30d
attracs-tms-backend-1 RESERVED (unused) Compute 6.70
attracs-tms-backend-2 RESERVED (unused) Compute 6.70
tms-backend-5-external RESERVED (unused) Compute 6.70
tms-backend-6-external RESERVED (unused) Compute 6.71
attracs-services-195009-lb2-address (34.120.155.57) IN_USE (LB) Compute shared
attracs-services-195009-lb2n-address (34.96.79.30) RESERVED (unused) Compute shared
lb-ipv4-1 (34.120.86.122) IN_USE (attracslb) Compute shared

1.4 Load balancing (shared across TMS + non-TMS, not split out separately in billing)

1.5 Pub/Sub (split per TMS app via topic naming)

App Net EUR / 30d
tms-planner (tms.planner.*.trips) 82.60
tms-drivers (tms.drivers.*.vehicles) 41.60
tms-booking (tms.booking.*.bookings) 17.42
tms-notification (cross-app: tms.notification.*) 1.04
tms-fleet 0.03
tms-feedback 0 (no Pub/Sub topics)
tms-reports 0 (no Pub/Sub topics)

1.6 Storage (TMS-named buckets)

Resource Service Net EUR / 30d
attracs-tms Cloud Storage 1.59
attracs-tms-dev Cloud Storage 0.00
attracs-tms-feedback Cloud Storage 0.00

1.7 attracs-services-195009 — sub-totals by service (TMS-named resources only)

Service Net EUR / 30d
Compute Engine 2,380.73
Confidential Computing 246.26
Cloud Pub/Sub 142.77
Cloud Storage 1.60
Subtotal — TMS-named 2,771.36

1.8 Shared resources in attracs-services-195009 (used by TMS + others — cost not splittable here)

Resource Service Net EUR / 30d TMS usage
attracs-sql Cloud SQL (MySQL 5.7, db-n1-standard-4) 366.43 hosts tms_integration_service + superset_* DBs used by tms-reports
attracs-spanner-01 Cloud Spanner (500 PU, regional europe-north1) 793.89 location* DBs — vehicle/trip location data for tms-drivers / tms-planner
attracs-repository Artifact Registry (Docker, ~813 GB) 66.09 all attracs Docker images, incl. TMS services
Networking (egress / LB / interconnect) Networking 1,011.44 TMS share unknown

1.9 attracs-services-195009 — whole-project cost by service (last 30d, for context)

Service Net EUR / 30d
Compute Engine 3,903.43
Networking 1,011.44
Cloud Spanner 793.89
Cloud SQL 673.31
Confidential Computing 287.31
Cloud Logging 271.55
Cloud Monitoring 246.29
Cloud Pub/Sub 143.57
Cloud KMS 76.04
Artifact Registry 66.09
BigQuery 45.55
Cloud Storage 7.42
Directions API 5.92
Cloud Vision API 1.91
Network Security 0.64
Geocoding API 0.40
Cloud Run Functions 0.11
Project total 7,534.88

2. PostgreSQL — pg-databases-stable

2.1 Instances

Resource Tier Zone Status Net EUR / 30d
pg-1-stable db-custom-30-32768 (Postgres 14) europe-north1-a RUNNABLE 1,689.78
pg-1-stable-read-replica db-custom-4-7680 (Postgres 14) europe-north1-c RUNNABLE 196.68
pg-1-stable-master Postgres 9.6 (placeholder/legacy entry) europe-north1 0

2.2 Backup storage

Resource Net EUR / 30d
Automated backups (~70 snapshot rows, a-380684204244-s-…-backup-*) 137.65
Aggregated other Cloud SQL storage rows small

2.3 TMS databases hosted on pg-1-stable

Cloud SQL bills per instance, not per database, so the figures above are for the whole shared instance. TMS-specific databases on this instance:

Database Likely served app
tms_booking tms-booking
tms_drivers tms-drivers
tms_feedback tms-feedback

Other databases on the same instance (cost shared with these): contracts, palbassador, ai_booking_assistant, dex, adi_digital_manifest, fleet_manager, cbookings, driver_guides, ao-lime-integration, customer_portal, resource, platoon, scania_connector, logistics_pm, emissions_calculator, road_tower, tailorframe_admin, city_taxi_tower, partner_portal, reporting_engine, road_integrations, greasing_log_stable, road_truck_tower, shiftplanner, daimler_connector (~30 DBs total).

No tms_planner or tms_reports databases here. tms-planner likely uses Spanner (location*) and/or talks to other services; tms-reports likely uses the superset_* MySQL databases in attracs-sql.

2.4 pg-databases-stable — whole-project cost by service (last 30d)

Service Net EUR / 30d
Cloud SQL 2,100.29
Cloud Logging 2.71
Cloud Monitoring 2.04
Cloud Storage 0.06
Project total 2,100.29

3. Optimisation candidates (worth verifying before deleting)

Item Est. monthly savings EUR Note
Delete persistent disks of TERMINATED VMs (tms-backend-5, tms-backend-6, tms-confidential-instance-stable-4, attracs-tms-backends-multi-zone-{2zdb,zq8q}, tms-valhalla-1) ~97 Disks keep billing while VMs are stopped
Release reserved-but-unused static IPs (attracs-tms-backend-1/2, tms-backend-{5,6}-external, attracs-services-195009-lb2n-address) ~27 RESERVED unused IPs bill ~€2.5–6/month each
Review reservation sizing — tms-confidential-instance-stable-4 is terminated; if commit covered 4 VMs but only 3 run, ~25% of €1,116 reservation cost is potentially idle up to ~280 Verify commitment term and target VM count
Stale Cloud SQL backups in pg-databases-stable (~70 snapshots, oldest from 2023) partial of 137.65 Tighten retention policy

4. Caveats

  1. No per-app labels. The TMS VMs carry service=tms, customer_environment=tms, deployment_environment=stable — but no app=tms-booking / app=tms-drivers label. The only per-app split available today is via Pub/Sub topic names.
  2. Cloud SQL / Spanner bill per instance, not per database. The attracs-sql MySQL and pg-1-stable PostgreSQL each host many non-TMS databases. The figures above show the full instance cost; the TMS share is some unknown fraction.
  3. Networking €1,011 in attracs-services-195009 includes egress and LB charges across all apps in the project; no resource-level attribution to TMS without deeper SKU/path analysis.
  4. Figures are taken from the resource-level billing export. The Cloud Billing console UI may show slightly different totals because it applies presentation-level rounding and credit grouping.

5. Hetzner equivalent — production-grade design and cost comparison

Goal: replicate the TMS workload on Hetzner with production-grade HA. Self-managed clusters for Postgres (replaces Cloud SQL + the Spanner location* DBs) and RabbitMQ (replaces Pub/Sub). No "cheapest tier" — every cluster has at least 3 nodes for quorum, dedicated CPU and ECC RAM where it matters.

5.1 Target architecture

Scope: strictly TMS-attributable resources only. Shared GCP infrastructure (Spanner attracs-spanner-01, attracs-sql MySQL, the project-wide Networking egress, the shared Artifact Registry, the shared observability stack) is excluded on both sides.

  • App tier — 3 cloud nodes with dedicated vCPU, running the TMS backends (booking, drivers, feedback, planner, reports, fleet) behind 2 HAProxy/Hetzner LBs, mirroring the current 3-zone GCE layout.
  • Postgres cluster — 3 dedicated bare-metal nodes (Patroni + etcd, streaming replication, automatic failover, PgBouncer). Sized to host only the 3 TMS databases (tms_booking, tms_drivers, tms_feedback) — not the other ~27 databases on pg-1-stable. Much smaller than the GCP shared instance.
  • RabbitMQ cluster — 3 cloud nodes with dedicated vCPU (quorum queues / mirrored queues). Replaces the tms.booking.*, tms.drivers.*, tms.planner.*, tms.notification.* topics.
  • Load balancer — 2 Hetzner Cloud Load Balancers in HA, fronting the app tier; TLS terminated at LB.
  • etcd — 3 small nodes dedicated to Patroni coordination (don't co-locate with PG).
  • Object storage — Hetzner Object Storage (S3-compatible) for the attracs-tms* buckets.
  • Backup tier — pgBackRest to a Storage Box for PITR backups.

5.2 Sizing & costs (Hetzner list prices effective 15 June 2026)

⚠️ Price hike alert. Hetzner adjusted prices on 15 June 2026 (source). Cloud servers went up +100% to +170% (CCX43: €124.99 → €275.99/mo, +121%; CCX23: €31.49 → €85.99/mo, +173%; CCX13: €15.99 → €42.99/mo, +169%). Dedicated AX/EX also rose substantially (AX102 ~€110 → €452.30/mo, ~4×). Existing orders placed before 15 June 2026 keep old prices; new orders and rescales use the new prices, so for a fresh migration today (2026-06-25) the new prices apply. Storage Box, Object Storage and Load Balancers were not listed in the announcement and are assumed unchanged.
The table below uses the post-hike prices. The "old €/mo" column is retained so the magnitude of the change is visible.

Component Replaces on GCP Hetzner SKU Spec Qty Old €/mo each New €/mo each Subtotal new €/mo
App tier tms-confidential-instance-stable-{1,2,3} (n2d-standard-16 confidential VMs) CCX43 (dedicated vCPU) 16 vCPU / 64 GB / 360 GB NVMe 3 124.99 275.99 827.97
Postgres cluster (Patroni HA) TMS share of pg-1-stable (3 of ~30 DBs: tms_booking, tms_drivers, tms_feedback) + replica AX42-1 dedicated Ryzen 7 7700 8-core / 64 GB DDR5 / 2× 1 TB NVMe (RAID 1) 3 ~50 187.30 561.90
RabbitMQ cluster Cloud Pub/Sub TMS topics (tms.booking.*, tms.drivers.*, tms.planner.*, tms.notification.*) CCX23 (dedicated vCPU) 4 vCPU / 16 GB / 160 GB NVMe 3 31.49 85.99 257.97
etcd (Patroni coordination) — (managed inside Cloud SQL) CX23 (shared vCPU) 4 vCPU / 8 GB 3 3.99 5.49 16.47
Load balancer (HA) TMS share of attracslb (paths for 6 TMS backends) Hetzner Cloud LB21 10k connections, TLS 2 6 ~6 12
Object storage attracs-tms, attracs-tms-dev, attracs-tms-feedback GCS buckets Hetzner Object Storage 1 TB included 1 6 ~6 6
Postgres backup target TMS share of Cloud SQL backup storage Storage Box BX41 5 TB 1 13 ~13 13
Bandwidth / egress (TMS share excluded — project-wide bill not split) Included (20 TB/server/mo) 0 0 0
Setup fees (dedicated servers, one-off, amortised over 12 mo) AX42-1 setup × 3 ~10 ~10 ~10
Hetzner TOTAL (recurring, TMS-only) (~470) ~1,705

Prices excl. VAT. Verify on hetzner.com/cloud and hetzner.com/dedicated/matrix-ax before committing — Hetzner may adjust again.

5.3 Side-by-side — TMS resources only

Layer GCP resource(s) GCP €/30d Hetzner equivalent Hetzner €/mo Δ
Compute (3 running TMS VMs + reservations + confidential premium + terminated VM disks) tms-confidential-instance-stable-{1,2,3} + zone reservations + Confidential Computing premium + terminated tms-* disks 2,727 3× CCX43 (dedicated 16 vCPU / 64 GB) 828 −1,899
PostgreSQL — TMS share of pg-1-stable (3 of ~30 databases, ≈ 10% by count) pg-1-stable (€1,690 × 10%) + replica (€197 × 10%) + backups (€138 × 10%) ~203 3× AX42-1 dedicated PG cluster (Patroni HA) 562 +359
Messaging TMS Pub/Sub topics (tms.booking.*, tms.drivers.*, tms.planner.*, tms.notification.*) 143 3× CCX23 RabbitMQ cluster 258 +115
Object storage attracs-tms, attracs-tms-dev, attracs-tms-feedback GCS buckets 2 Hetzner Object Storage 1 TB 6 +4
Load balancer + IPs TMS share of attracslb / attracslb-2 + reserved IPs 0 (LB cost not split out) 2× LB21 12 +12
Backup storage (included in PG row above) Storage Box BX41 5 TB 13 +13
etcd coordination — (managed inside Cloud SQL) 0 3× CX23 16 +16
Setup fees (amortised) 0 AX42 setup × 3 10 +10
Total — TMS-only ~3,075 ~1,705 −1,370 (~45%)

Scope note: this side-by-side excludes Spanner attracs-spanner-01 (€794/30d), attracs-sql MySQL, project-wide Networking egress (€1,011/30d), the shared Artifact Registry, and the shared Cloud Logging/Monitoring stack. Those are not strictly TMS-attributable — they serve the whole project. If you later want to retire them too, the Hetzner side would need a Spanner replacement (e.g. CockroachDB cluster, ~+€600–1,000/mo at post-hike pricing), a registry node, and an observability node.
Sensitivity to the 15 June 2026 hike:
Pre-hike, the same Hetzner stack would have cost ~€470/mo → saving was ~€2,600/mo (~85%).
Post-hike (today's prices), the saving is ~€1,370/mo (~45%).
Add 0.5–1 FTE for self-hosted ops (~€3.3–10k/mo loaded cost) and the cost saving disappears entirely. The remaining case for Hetzner is then vendor independence, predictable pricing, zero egress fees, not raw EUR.

5.4 What we lose by moving to Hetzner

  • Confidential VMs (AMD SEV-SNP). Hetzner doesn't offer confidential computing today. If the tms-confidential-instance-stable-* naming reflects an actual compliance requirement (encrypted RAM in use), this is a blocker. If it's "nice-to-have", it's fine to drop.
  • Cross-region SQL strong consistency. Spanner gives globally-consistent SQL with TrueTime. Postgres + Patroni gives single-region HA only. If the location* data needs cross-region writes, CockroachDB self-hosted is the closer match (and would cost more — roughly +3× the PG cluster).
  • Managed backups, PITR, automatic failover for SQL. All self-operated. Plan for pgBackRest + Patroni + monitoring + on-call.
  • Pub/Sub guarantees. GCP Pub/Sub gives at-least-once delivery with multi-region durability out of the box; RabbitMQ quorum queues are good but need careful network and disk sizing to match.
  • GCP IAM, audit logs, Cloud Logging retention. Need to replicate with Authelia/Keycloak + centralised syslog + Loki retention rules.
  • People cost. Self-hosting these clusters is at least 0.5–1 FTE of SRE time. At a €80–120k loaded cost, that's another €3.3–10k/mo — so the apples-to-apples saving is closer to €2–4k/mo once you internalise ops.

5.5 Reasonable next step

Don't migrate everything at once. Lowest-risk wave:

  1. RabbitMQ first (smallest blast radius, clean abstraction). Validate the messaging pattern off GCP, get ops practice. Saves ~€50/mo immediately but more importantly de-risks the next steps.
  2. Stateless app tier next. Containerise on K3s or Nomad on the 3 CCX43 nodes. Cut LB DNS over. Saves ~€2k/mo.
  3. Postgres last. This is where the operational risk concentrates. Run Hetzner PG in parallel with pg-1-stable using logical replication for ≥1 month before cutover.
  4. Spanner — only attempt to replace once you've measured the location* traffic profile. If write throughput is high or geo-distribution matters, leave it on GCP and budget for it.