Skip to content

Private network: grintest

The non-prod VMs (grintest-dev-01, future grintest-uat-01) share a single Hetzner Cloud private network. This is preparation for inter-VM traffic (uat ↔ monitoring, future split of services across VMs, future Hetzner Load Balancer attachment) and a hard isolation boundary for internal services.

Topology

Field Value
Network name grintest
IP range 10.0.0.0/16
Subnet (EU Central) 10.0.1.0/24
Network gateway 10.0.0.1
Provider zone eu-central

VM assignments

VM Private IP Public IPv4 Public IPv6 Role
grintest-dev-01 10.0.1.1 178.104.246.247 2a01:4f8:1c19:d159::1 dev
grintest-uat-01 (future) (future) (future) uat

Interface naming

The private network interface name is hardware-dependent and assigned by the kernel via systemd-udev's predictable network interface naming. On the current dev VM (CX23) it is enp7s0. On other VM types it may be ens10, enp1s0, etc.

Always confirm per-VM with ip -4 -br addr show rather than hardcoding the name in service configs. Where possible, bind services to the IP address (10.0.1.x), not the interface name.

Service-binding convention

Internal services bind only to the private interface (ens10, 10.0.1.x), never to 0.0.0.0. This applies to:

  • PostgreSQL
  • Redis
  • Kafka brokers (if reintroduced)
  • OpenTelemetry Collector (gRPC 4317, HTTP 4318)
  • Any future internal queue / cache

The convention is enforced at three layers:

  1. Service config — bind to 10.0.1.2 explicitly in compose / Coolify.
  2. Hetzner Cloud Firewall — drops the service ports from the public internet (firewall.md).
  3. Host ufw — same allow-list as the cloud firewall, last line of defence.

External access for developers (pgAdmin, RedisInsight, psql, etc.) goes through an SSH tunnel — see db-access.md. No internal service is exposed on the public internet.

Terraform shape (forward-looking)

When the YAML/Markdown here migrates to Terraform, the network resource maps to:

resource "hcloud_network" "grintest" {
  name     = "grintest"
  ip_range = "10.0.0.0/16"
}

resource "hcloud_network_subnet" "eu_central" {
  network_id   = hcloud_network.grintest.id
  type         = "cloud"
  network_zone = "eu-central"
  ip_range     = "10.0.1.0/24"
}

resource "hcloud_server_network" "dev" {
  server_id  = hcloud_server.dev.id
  network_id = hcloud_network.grintest.id
  ip         = "10.0.1.1"
}