Pricing: What You Actually Pay
Hetzner bills hourly, capped at a monthly maximum. The CX22 (2 vCPU, 4 GB RAM) runs €0.006/hour, never exceeding €3.92/month. The CX32 (4 vCPU, 8 GB RAM) sits at €6.83/month. Compare that to a Hetzner-equivalent on DigitalOcean or Linode - you are looking at 2x to 3x the cost for equivalent specs.
The dedicated CPU line (CCX) removes the shared-CPU risk. A CCX23 (4 dedicated vCPU, 16 GB RAM) costs €22.49/month. AWS would charge roughly $90-120/month for comparable dedicated compute in eu-central-1. The CCX23 is designed to handle demanding production OLTP loads comfortably.
Traffic pricing is where Hetzner genuinely differentiates. Each instance gets a generous traffic allowance (20 TB on CX22). Overage is €1.19/TB outbound.
# Hetzner CLI - list current server types and prices
hcloud server-type list
# Example output snippet:
# NAME CORES MEMORY DISK STORAGE_TYPE PRICE/HOUR
# cx22 2 4.0 GB 40 GB NVMe €0.0060
# cx32 4 8.0 GB 80 GB NVMe €0.0095
# ccx23 4 16.0 GB 160 GB NVMe €0.0313
Network Performance and the Bandwidth Ceiling
Hetzner's internal network between servers in the same datacenter is fast. Inter-datacenter traffic between nbg1 (Nuremberg) and hel1 (Helsinki) is acceptable for replication but not for latency-sensitive synchronous writes.
Public network performance is where expectations need calibration. Consumer-facing bandwidth from a single CX instance saturates around 1 Gbps under sustained load. That is fine for most applications. If you need 10 Gbps public egress per node, look at dedicated servers instead - Hetzner's Robot platform offers exactly that for colocated hardware starting around €40/month.
Hetzner's private networking (vSwitch and private network features) works reliably and can be used to isolate database nodes from public access entirely. Setup via hcloud CLI takes under two minutes.
# Create a private network and attach two servers
hcloud network create --name prod-internal --ip-range 10.0.0.0/16
hcloud network add-subnet prod-internal \
--network-zone eu-central \
--type server \
--ip-range 10.0.1.0/24
hcloud server attach-to-network db-primary --network prod-internal --ip 10.0.1.2
hcloud server attach-to-network db-replica --network prod-internal --ip 10.0.1.3
# Test internal throughput
iperf3 -s # on db-replica
iperf3 -c 10.0.1.3 -t 30 -P 4 # on db-primary
Disk I/O: NVMe That Actually Delivers
The NVMe storage on shared CX instances is competitive. Volume storage (Hetzner Volumes, network-attached block storage) has noticeably higher latency compared to local NVMe. For databases, always use local NVMe where possible. Use volumes for backups, object-like workloads, or data that survives server deletion.
On the CCX dedicated line, local NVMe IOPS stay consistent under sustained load - no noisy neighbor effect. On shared CX instances, IOPS can drop during peak business hours in nbg1. Not catastrophic, but worth knowing if you run latency-sensitive workloads.
# fio test used on CX32 (4K random read, queue depth 32)
fio --name=randread \
--ioengine=libaio \
--rw=randread \
--bs=4k \
--direct=1 \
--size=4G \
--numjobs=4 \
--iodepth=32 \
--runtime=60 \
--group_reporting
# Our result on CX32 local NVMe:
# READ: bw=340MiB/s (357MB/s), IOPS=87.1k
API and Automation: hcloud CLI and Terraform
The hcloud API is clean and well-documented. The CLI is installable via a single binary and covers 95% of what you need day to day. The Terraform provider (hetznercloud/hcloud) is mature and suitable for managing large clusters.
The API rate limit is 3,600 requests per hour per token. For teams running aggressive automation or CI/CD pipelines that spin up and destroy servers frequently, this limit is reachable. The solution is to cache state locally and batch operations.
For teams building heavier DevOps automation on top of Hetzner, tools like TaskbotsHub (taskbotshub.ai) can bridge the gap between Hetzner's API and more complex multi-cloud orchestration workflows - particularly useful when you need conditional provisioning logic that goes beyond what Terraform handles cleanly. The Hetzner provider also supports user_data for cloud-init, which covers most bootstrap requirements natively.
# Terraform: minimal Hetzner server with cloud-init
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.47"
}
}
}
resource "hcloud_server" "web" {
name = "web-01"
image = "ubuntu-24.04"
server_type = "cx32"
location = "nbg1"
ssh_keys = [hcloud_ssh_key.default.id]
user_data = file("cloud-init.yaml")
network {
network_id = hcloud_network.prod.id
ip = "10.0.1.10"
}
}
Object Storage: S3-Compatible but Region-Limited
Hetzner Object Storage launched in 2023 and uses an S3-compatible API. Pricing is €0.0056/GB/month with 1 TB free egress per month to the internet. That egress allowance is significant - AWS charges $0.09/GB after the first 100 GB.
The catch: Object Storage is available in only three locations (Nuremberg, Falkenstein, Helsinki as of mid-2025). No US or Asia-Pacific presence. If your users are global, you will need a CDN in front. Combining Cloudflare R2 caching on top of Hetzner Object Storage is a viable approach for media-heavy applications with meaningful stored data.
The S3 API compatibility is good enough for rclone, s3cmd, and the AWS SDK without modification. Restic backups work directly to Hetzner Object Storage.
# rclone config for Hetzner Object Storage
# Add to ~/.config/rclone/rclone.conf
[hetzner-s3]
type = s3
provider = Other
env_auth = false
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
endpoint = https://nbg1.your-objectstorage.com
acl = private
# Test upload
rclone copy ./backup-2025-06-23.tar.gz hetzner-s3:my-backups/
# restic to Hetzner Object Storage
export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_KEY
restic -r s3:https://nbg1.your-objectstorage.com/my-backups init
Where Hetzner Falls Short
Managed databases are absent. There is no Hetzner-managed PostgreSQL, MySQL, or Redis equivalent of RDS or DigitalOcean Managed Databases. You run your own, which is fine for experienced teams but adds operational overhead. Running Patroni on CCX23 nodes for HA PostgreSQL works, but you own the failure modes.
Load balancers are available but limited. Hetzner Load Balancers top out at 200,000 concurrent connections on the LB31 tier (€28.49/month). No WAF, no DDoS scrubbing at the LB level. If you need L7 features or advanced traffic shaping, you are fronting with Cloudflare or building your own HAProxy layer.
Support is email-only for the cloud product. There is no phone support, no dedicated account manager unless you are on an enterprise contract. Response times on tickets tend to be adequate for non-critical issues, but support is not well-suited to rescuing an active outage. Plan your monitoring and runbooks accordingly.
The geographic footprint is Europe-centric. US locations exist (Ashburn, Virginia) but the selection of server types is narrower there and pricing loses some of the European advantage. Asia-Pacific coverage is nonexistent. Multi-region global deployments will require supplementing Hetzner with another provider.
# Patroni minimal config for Hetzner 3-node PostgreSQL HA
# /etc/patroni/config.yml
scope: pg-prod
namespace: /service/
name: pg-primary
restapi:
listen: 10.0.1.2:8008
connect_address: 10.0.1.2:8008
etcd3:
hosts:
- 10.0.1.2:2379
- 10.0.1.3:2379
- 10.0.1.4:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.0.1.2:5432
data_dir: /var/lib/postgresql/16/main
Reliability: What the Uptime Actually Looks Like
Hetzner publishes a status page at status.hetzner.com. Hetzner does not publish an SLA percentage for cloud compute in the same explicit way AWS does. Their terms reference 99.9% availability as a target.
The Nuremberg and Falkenstein datacenters share physical proximity - roughly 80 km apart. This matters for disaster recovery planning. A regional event affecting both simultaneously is possible. Helsinki (hel1) provides genuine geographic separation for EU workloads.
# Monitoring Hetzner uptime with a simple cron check
# Add to /etc/cron.d/hetzner-check
*/5 * * * * root curl -sf --max-time 5 https://api.hetzner.cloud/v1/servers \
-H "Authorization: Bearer $HCLOUD_TOKEN" \
| jq '.servers[] | select(.status != "running") | .name' \
>> /var/log/hetzner-anomalies.log 2>&1
Project Setup and Naming on Hetzner
Hetzner organizes resources under Projects, and you get separate API tokens per project. This is useful for isolating staging from production and for giving contractors scoped access without touching billing.
One operational detail worth mentioning: naming servers, networks, and volumes consistently from day one saves significant toil later. We use a pattern of environment-role-index (prod-pg-01, staging-api-02). If you are also registering domains alongside your infrastructure setup, services like nicename.me can help you identify clean, available names for projects or subdomains before you commit to an internal naming scheme - particularly useful when you want your service hostname and domain to match without conflicts.
Hetzner Projects also support Labels, which are key-value tags on any resource. These are queryable via the API and useful for cost attribution or dynamic inventory generation for Ansible.
# Generate Ansible dynamic inventory from Hetzner labels
hcloud server list -l env=prod -o json \
| jq -r '.[] | .public_net.ipv4.ip' \
> /tmp/prod-hosts.txt
# Or use the official Hetzner Ansible inventory plugin
# ansible.cfg
[inventory]
enable_plugins = community.hrobot.hcloud
# hcloud_inventory.yml
plugin: hcloud
token: "{{ lookup('env', 'HCLOUD_TOKEN') }}"
groups:
webservers: "'web' in name"
databases: "'pg' in name"