Terraform + UpCloud — UpCloud Resources¶
OWNER: arjan
ALSO_USED_BY: gerco, thijmen, rutger
LAST_VERIFIED: 2026-03-26
GE_STACK_VERSION: terraform ~> 1.14.x, upcloud provider ~> 5.0
Overview¶
UpCloud resource types used in GE, managed via the Terraform provider.
Covers server types, managed Kubernetes, storage, networking, firewalls,
and load balancers. All resources are EU-hosted (ISO 27001 A.8.31).
Cloud Server Types¶
UpCloud offers four server plan families:
| Plan Family | Use Case in GE | vCPU | Memory | Storage |
|---|---|---|---|---|
| General Purpose | Admin UI, wiki, monitoring | 1-20 | 1-128 GB | MaxIOPS SSD |
| High CPU | Agent executors, orchestrator | 2-20 | 4-64 GB | MaxIOPS SSD |
| High Memory | PostgreSQL, Redis | 2-20 | 8-256 GB | MaxIOPS SSD |
| Cloud Native | K8s worker nodes (cost-optimised) | 2-16 | 4-32 GB | MaxIOPS SSD |
resource "upcloud_server" "executor" {
hostname = "ge-${terraform.workspace}-executor"
zone = "nl-ams1"
plan = "HICPU-2xCPU-4GB"
template {
storage = "Ubuntu Server 24.04 LTS"
size = 50
}
network_interface {
type = "private"
}
labels = {
"ge.zone" = terraform.workspace
"ge.managed-by" = "terraform"
"ge.component" = "compute"
}
lifecycle {
ignore_changes = [template[0].storage]
}
}
IF: selecting a server plan
THEN: match the workload profile to the plan family
THEN: start small, monitor, scale up — UpCloud supports hot resize
Managed Kubernetes (UKS)¶
Used for Zones 2 (staging) and 3 (production).
Control plane is free. Worker nodes billed as Cloud Servers.
resource "upcloud_kubernetes_cluster" "main" {
name = "ge-${terraform.workspace}-cluster"
network = upcloud_network.private.id
network_cidr = "172.16.0.0/24"
zone = "nl-ams1"
plan = "production"
version = "1.34"
private_node_groups = true
labels = {
"ge.zone" = terraform.workspace
"ge.managed-by" = "terraform"
}
}
resource "upcloud_kubernetes_node_group" "workers" {
cluster = upcloud_kubernetes_cluster.main.id
name = "ge-${terraform.workspace}-workers"
node_count = local.config.node_count
plan = local.config.server_plan
labels = {
"ge.zone" = terraform.workspace
"ge.managed-by" = "terraform"
"ge.component" = "kubernetes"
}
}
CHECK: private_node_groups = true — worker nodes not publicly accessible
CHECK: version matches GE pinned Kubernetes version
CHECK: lifecycle.prevent_destroy = true on production clusters
Key UKS features:
- CNCF conformance certified
- Up to 30 data plane nodes per cluster
- Integrated with UpCloud Block Storage CSI
- UpCloud Managed Load Balancer integration
- Private networking between nodes
Storage¶
Block Storage (MaxIOPS SSD)¶
resource "upcloud_storage" "data" {
title = "ge-${terraform.workspace}-data"
zone = "nl-ams1"
size = 100
tier = "maxiops"
lifecycle {
prevent_destroy = true
}
}
CHECK: tier = "maxiops" for all production storage
CHECK: prevent_destroy = true on all data volumes
Object Storage¶
UpCloud offers managed object storage for static assets.
GE uses bunny.net Edge Storage instead for CDN-integrated storage.
READ_ALSO: wiki/docs/stack/bunnynet/edge.md
Networking¶
Private Network¶
All GE services communicate over private networking.
Public interfaces only on load balancers and bastion hosts.
resource "upcloud_network" "private" {
name = "ge-${terraform.workspace}-private"
zone = "nl-ams1"
ip_network {
address = "10.0.0.0/24"
dhcp = true
dhcp_default_route = false
family = "IPv4"
}
}
CHECK: database and executor servers have ONLY private network interfaces
CHECK: public access goes through load balancer, never directly to servers
Router¶
resource "upcloud_router" "main" {
name = "ge-${terraform.workspace}-router"
static_route {
name = "to-kubernetes"
nexthop = "10.0.0.1"
route = "172.16.0.0/24"
}
}
Firewalls¶
UpCloud firewall rules attached to servers:
resource "upcloud_firewall_rules" "executor" {
server_id = upcloud_server.executor.id
firewall_rule {
action = "accept"
direction = "in"
family = "IPv4"
protocol = "tcp"
destination_port_start = 6381
destination_port_end = 6381
source_address_start = "10.0.0.0"
source_address_end = "10.0.0.255"
comment = "Redis from private network"
}
firewall_rule {
action = "drop"
direction = "in"
family = "IPv4"
comment = "Default deny"
}
}
CHECK: default deny rule is LAST in the list
CHECK: only necessary ports are open
CHECK: source addresses restricted to private network where possible
Load Balancers¶
UpCloud Managed Load Balancer for Zones 2+3:
resource "upcloud_loadbalancer" "frontend" {
configured_status = "started"
name = "ge-${terraform.workspace}-lb"
plan = "production-small"
zone = "nl-ams1"
networks {
name = "private"
type = "private"
family = "IPv4"
network = upcloud_network.private.id
}
networks {
name = "public"
type = "public"
family = "IPv4"
}
labels = {
"ge.zone" = terraform.workspace
"ge.managed-by" = "terraform"
}
}
UpCloud Essentials includes 1 free load balancer node with 1,000 concurrent sessions.
Production may need larger plans based on traffic.
Pricing Reference¶
All EU-hosted. Prices approximate (check upcloud.com/pricing for current):
| Resource | Starting Price | Notes |
|---|---|---|
| Cloud Server (1 vCPU, 1 GB) | ~$5/month | Hourly billing |
| Managed K8s control plane | Free | Up to 30 nodes |
| K8s worker node | Same as Cloud Server | Per node plan |
| Block Storage (MaxIOPS) | ~$0.11/GB/month | |
| Managed Load Balancer | Free (1 node) | Essentials tier |
| Managed Database (PostgreSQL) | ~$15/month | Smallest plan |
| Data transfer (egress) | Free | Zero egress fees |
UpCloud Regions¶
GE uses EU data centres only:
| Region | Code | Use |
|---|---|---|
| Amsterdam, NL | nl-ams1 |
Primary (all zones) |
| Frankfurt, DE | de-fra1 |
Failover candidate |
| Helsinki, FI | fi-hel1 |
UpCloud HQ region |
CHECK: all resources deployed to nl-ams1 unless failover
CHECK: never deploy to non-EU regions
Cross-References¶
READ_ALSO: wiki/docs/stack/terraform-upcloud/index.md
READ_ALSO: wiki/docs/stack/terraform-upcloud/patterns.md
READ_ALSO: wiki/docs/stack/terraform-upcloud/pitfalls.md
READ_ALSO: wiki/docs/stack/terraform-upcloud/checklist.md
READ_ALSO: wiki/docs/stack/kubernetes/index.md