15 min read

Learning Objectives

By the end of this module, you will be able to:

  • Provision multi-tier LANs and attach server NICs declaratively using the `ionoscloud_lan` and `ionoscloud_nic` Terraform resources
  • Configure outbound internet access for private LANs with `ionoscloud_natgateway` and SNAT rules
  • Establish site-to-site hybrid connectivity using the `ionoscloud_vpn_ipsec_gateway` resource
  • Manage DNS zones and records as code with `ionoscloud_dns_zone` and `ionoscloud_dns_record`
  • Compose LANs, NICs, NAT, and DNS into a reproducible three-tier network stack for the TaskBoard application

Unit 2.2: Network and Connectivity as Code

Introduction

In Unit 2.1 you provisioned TaskBoard's compute tier: an API server and a worker, each booted with cloud-init. Those servers are useless until they can talk to each other, reach the internet for package updates, and resolve a hostname that points back to the API endpoint. That is the job of the network layer, and on IONOS Cloud every part of it is a Terraform resource.

This unit builds the network stack underneath TaskBoard. You will segment traffic into a public tier, an application tier, and a database tier using separate LANs, wire servers into those LANs with NICs, give the private tiers controlled outbound access through a NAT gateway, and publish a DNS record so clients can find the API. Every resource is declarative, every dependency is resolved by Terraform's graph, and the whole topology is reproducible from a single terraform apply.

1. LANs for Network Segmentation

The ionoscloud_lan resource is the foundation of network segmentation inside a VDC. A LAN is a layer-2 broadcast domain scoped to one data center. You create one LAN per tier so that traffic between tiers must traverse a controlled boundary rather than sharing a flat network.

The single most important argument is public. A public LAN is connected to the IONOS internet gateway and its NICs receive a routable public IPv4 address from the platform DHCP service. A private LAN (public = false) has no internet path of its own, which is exactly what you want for application and database tiers.

1.1 Defining the three tiers

TaskBoard needs three LANs: a public LAN for ingress, a private application LAN for the API and worker, and a private database LAN. Each LAN belongs to the datacenter you provisioned in Module 1.

resource "ionoscloud_lan" "public" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-public"
  public        = true
}

resource "ionoscloud_lan" "app" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-app"
  public        = false
}

resource "ionoscloud_lan" "db" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-db"
  public        = false
}

Terraform assigns each LAN a numeric ID within the datacenter once created. You reference these IDs from NIC resources rather than hardcoding them, which keeps the configuration portable across environments.

1.2 Addressing inside a LAN

The platform allocates each LAN a private subnet with a /24 default subnet size, so a single LAN gives you up to 254 usable host addresses. Private LANs use RFC 1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). The standard Ethernet MTU is 1500 bytes, which matters when you tune application-level packet sizes or VPN payloads later in this unit.

You do not configure the subnet on the LAN itself. Addresses are assigned at the NIC level, either automatically by DHCP or as explicit static IPs, which the next section covers.

2. Attaching Servers with NICs

A ionoscloud_nic connects a server to a LAN. A server can have multiple NICs, and that is precisely how a host participates in more than one tier. TaskBoard's API server, for example, needs a NIC on the public LAN (to receive ingress) and a NIC on the app LAN (to reach the worker and database tiers).

The NIC is where you decide whether an interface gets a DHCP-assigned address or a static one, and whether the platform firewall is active on that interface.

2.1 DHCP and static addressing

Set dhcp = true to let the platform DHCP service assign an address. On a public LAN this also assigns a routable public IPv4 address automatically. For private tiers where you want predictable addresses, supply an explicit ips list from the LAN's RFC 1918 range.

# Public-facing NIC on the API server: DHCP-assigned public IP
resource "ionoscloud_nic" "api_public" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  server_id     = ionoscloud_server.api.id
  lan           = ionoscloud_lan.public.id
  name          = "api-public"
  dhcp          = true
  firewall_active = true
}

# Private NIC on the API server: static address on the app LAN
resource "ionoscloud_nic" "api_app" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  server_id     = ionoscloud_server.api.id
  lan           = ionoscloud_lan.app.id
  name          = "api-app"
  dhcp          = false
  ips           = ["10.7.1.10"]
}

The firewall_active flag turns on the per-NIC firewall. When activated, the NIC firewall blocks all incoming traffic by default and only permits traffic on explicitly enabled ports. The NIC firewall is the per-interface security control; detailed rule provisioning and Network Security Groups are covered in Unit 2.3.

2.2 Multi-NIC servers and tier membership

The worker only needs to reach the app and database tiers, so it gets two private NICs and no public interface at all.

resource "ionoscloud_nic" "worker_app" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  server_id     = ionoscloud_server.worker.id
  lan           = ionoscloud_lan.app.id
  name          = "worker-app"
  dhcp          = false
  ips           = ["10.7.1.20"]
}

resource "ionoscloud_nic" "worker_db" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  server_id     = ionoscloud_server.worker.id
  lan           = ionoscloud_lan.db.id
  name          = "worker-db"
  dhcp          = false
  ips           = ["10.7.2.20"]
}

Because Terraform sees that each NIC references both a server and a LAN, it automatically orders creation: datacenter first, then LANs and servers, then NICs. You never write explicit depends_on for this chain. With no public NIC, the worker is unreachable from the internet, which is the isolation you want for a backend process.

3. Outbound Access with a NAT Gateway

A private LAN has no internet route. That is good for inbound security but a problem when your API server needs to pull OS updates, fetch dependencies, or call an external SaaS API. The ionoscloud_natgateway resource solves this by providing controlled, outbound-only connectivity.

The NAT gateway is SNAT-only (Source NAT). It rewrites the source address of outbound traffic from private hosts to a public IP that the gateway owns, and it blocks all unsolicited inbound traffic automatically. A single gateway can serve up to 6 private networks.

3.1 Provisioning the gateway

The gateway requires one or more reserved public IPv4 addresses, which you allocate as an IP block. Attach the gateway to the LANs whose hosts need egress, and define the gateway-internal IP it uses on each LAN.

resource "ionoscloud_ipblock" "nat" {
  location = ionoscloud_datacenter.taskboard.location
  size     = 1
  name     = "taskboard-nat-ip"
}

resource "ionoscloud_natgateway" "taskboard" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-nat"
  public_ips    = ionoscloud_ipblock.nat.ips

  lans {
    id           = ionoscloud_lan.app.id
    gateway_ips  = ["10.7.1.1/24"]
  }
}

The underlying API call maps to POST /datacenters/{datacenterId}/natgateways. Like all IONOS provisioning operations, this is asynchronous; the Terraform provider polls the request until it completes before reporting the resource as created.

3.2 SNAT rules

The gateway does not pass traffic until you define rules. A NAT gateway rule specifies which private source addresses are translated and over which protocol. Rules map to POST /datacenters/{datacenterId}/natgateways/{natGatewayId}/rules.

resource "ionoscloud_natgateway_rule" "app_egress" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  natgateway_id = ionoscloud_natgateway.taskboard.id
  name          = "app-tier-egress"
  type          = "SNAT"
  protocol      = "ALL"
  source_subnet = "10.7.1.0/24"
  public_ip     = ionoscloud_ipblock.nat.ips[0]
}

The protocol can be restricted (for example to TCP or UDP) when you want tighter control, but ALL is appropriate for a general egress rule. Note that the default route is not auto-injected: hosts on the private LAN must use the gateway IP (10.7.1.1 above) as their default gateway, which you typically set through cloud-init or DHCP options on the NIC.

4. Site-to-Site Connectivity with IPSec VPN

When TaskBoard needs to reach a resource in your on-premises data center, such as an internal identity provider, you bridge the two networks with the ionoscloud_vpn_ipsec_gateway resource. The VPN Gateway supports IPSec and WireGuard; this section uses IPSec for a classic site-to-site tunnel.

IPSec on IONOS uses IKEv2 and authenticates tunnels with a pre-shared key (PSK). The VPN API is regional: each gateway lives behind a region-specific host such as https://vpn.de-fra.ionos.com, with IPSec gateways exposed at the /ipsecgateways resource path and their tunnels nested under /ipsecgateways/{gatewayId}/tunnels.

4.1 Gateway and tunnel

The gateway needs a reserved public IP and a connection into the LAN it serves. The tunnel defines the remote peer and the traffic selectors.

resource "ionoscloud_ipblock" "vpn" {
  location = ionoscloud_datacenter.taskboard.location
  size     = 1
  name     = "taskboard-vpn-ip"
}

resource "ionoscloud_vpn_ipsec_gateway" "hybrid" {
  name       = "taskboard-hybrid"
  location   = "de/fra"
  gateway_ip = ionoscloud_ipblock.vpn.ips[0]

  connections {
    datacenter_id = ionoscloud_datacenter.taskboard.id
    lan_id        = ionoscloud_lan.app.id
    ipv4_cidr     = "10.7.1.5/24"  # the gateway's own host address on the app LAN, not the network address
  }
}

resource "ionoscloud_vpn_ipsec_tunnel" "onprem" {
  gateway_id    = ionoscloud_vpn_ipsec_gateway.hybrid.id
  location      = "de/fra"
  name          = "to-onprem"
  remote_host   = "203.0.113.10"

  auth {
    method = "PSK"
    psk {
      key = var.vpn_psk
    }
  }

  cloud_network_cidrs = ["10.7.1.0/24"]
  peer_network_cidrs  = ["192.168.50.0/24"]
}

Keep the PSK out of source control. Declare it as a sensitive variable (variable "vpn_psk" { sensitive = true }) and supply it through an environment variable or a secret store, never as a literal in the .tf file.

4.2 Routing across the tunnel

The cloud_network_cidrs and peer_network_cidrs define which subnets are reachable over the tunnel. Traffic from 10.7.1.0/24 destined for 192.168.50.0/24 is encrypted and forwarded; everything else follows normal routing. Match these CIDRs exactly to your on-premises configuration, because a mismatch is the most common reason a tunnel comes up but carries no traffic.

5. DNS as Code

A network stack that nobody can find is incomplete. The ionoscloud_dns_zone and ionoscloud_dns_record resources let you publish DNS entirely from Terraform, so the hostname for TaskBoard's API is versioned alongside the infrastructure that serves it. IONOS Cloud DNS is served from an Anycast network, so records resolve from the nearest point of presence.

5.1 Zones and records

Create the zone, then add records pointing at the public IP allocated to the API server's public NIC.

resource "ionoscloud_dns_zone" "taskboard" {
  name        = "taskboard.example.com"
  description = "TaskBoard application zone"
  enabled     = true
}

resource "ionoscloud_dns_record" "api" {
  zone_id = ionoscloud_dns_zone.taskboard.id
  name    = "api"
  type    = "A"
  content = ionoscloud_nic.api_public.ips[0]
  ttl     = 300
  enabled = true
}

TTL values must fall between 60 and 604800 seconds. Cloud DNS supports a wide set of record types, including A, AAAA, CNAME, ALIAS, MX, NS, SOA, SRV, TXT, CAA, and HTTPS. To create an apex (zone root) record, leave the record name field empty rather than using @.

The corresponding API call posts to https://dns.<region>.ionos.com/zones for zones and to the zone's /records path for records, for example POST https://dns.de-fra.ionos.com/zones/{zoneId}/records.

5.2 Cloud DNS scope and where failover belongs

Cloud DNS is an authoritative DNS service: it publishes and resolves your zones and records. It does not poll your backends or perform health-check-based failover, so a record keeps resolving to its configured target regardless of whether that target is up. Do not rely on DNS to route around a failed backend. For automatic failover, put a managed load balancer in front of your targets and point the DNS record at the load balancer, so health checking and target removal happen at the load-balancing tier introduced in Unit 2.3.

API Reference Quick Card

Key API endpoints for network and connectivity provisioning:

Method Endpoint Description
POST /datacenters/{datacenterId}/lans Create a LAN
POST /datacenters/{datacenterId}/servers/{serverId}/nics Attach a NIC to a server
POST /datacenters/{datacenterId}/natgateways Create a NAT gateway
POST /datacenters/{datacenterId}/natgateways/{natGatewayId}/rules Create a SNAT rule
POST /ipsecgateways Create an IPSec VPN gateway (regional host)
POST /zones Create a DNS zone (regional DNS host)
POST /zones/{zoneId}/records Create a DNS record

Base URL (Cloud API): https://api.ionos.com/cloudapi/v6 Regional DNS host: https://dns.de-fra.ionos.com Regional VPN host: https://vpn.de-fra.ionos.com Authentication: Authorization: Bearer <token>

Code Lab

Objective: Build TaskBoard's three-tier network stack with Terraform: public, app, and database LANs, server NICs, a NAT gateway for private egress, and a DNS record for the API. Then verify connectivity between tiers.

Prerequisites:

  • IONOS Cloud account with API token (IONOS_TOKEN exported)
  • Terraform with the ionoscloud provider installed
  • The TaskBoard datacenter and servers from Unit 2.1 already in state

Step 1: Define the three LANs

resource "ionoscloud_lan" "public" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-public"
  public        = true
}
resource "ionoscloud_lan" "app" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-app"
  public        = false
}
resource "ionoscloud_lan" "db" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-db"
  public        = false
}

Expected output:

Plan: 3 to add, 0 to change, 0 to destroy.

Step 2: Attach the API server NICs

resource "ionoscloud_nic" "api_public" {
  datacenter_id   = ionoscloud_datacenter.taskboard.id
  server_id       = ionoscloud_server.api.id
  lan             = ionoscloud_lan.public.id
  dhcp            = true
  firewall_active = true
}
resource "ionoscloud_nic" "api_app" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  server_id     = ionoscloud_server.api.id
  lan           = ionoscloud_lan.app.id
  dhcp          = false
  ips           = ["10.7.1.10"]
}

Expected output:

ionoscloud_nic.api_public: Creation complete after 1m12s

Step 3: Reserve an IP block and create the NAT gateway

resource "ionoscloud_ipblock" "nat" {
  location = ionoscloud_datacenter.taskboard.location
  size     = 1
  name     = "taskboard-nat-ip"
}
resource "ionoscloud_natgateway" "taskboard" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  name          = "taskboard-nat"
  public_ips    = ionoscloud_ipblock.nat.ips
  lans {
    id          = ionoscloud_lan.app.id
    gateway_ips = ["10.7.1.1/24"]
  }
}

Expected output:

ionoscloud_natgateway.taskboard: Creation complete after 2m03s

Step 4: Add the SNAT egress rule

resource "ionoscloud_natgateway_rule" "app_egress" {
  datacenter_id = ionoscloud_datacenter.taskboard.id
  natgateway_id = ionoscloud_natgateway.taskboard.id
  name          = "app-tier-egress"
  type          = "SNAT"
  protocol      = "ALL"
  source_subnet = "10.7.1.0/24"
  public_ip     = ionoscloud_ipblock.nat.ips[0]
}

Expected output:

ionoscloud_natgateway_rule.app_egress: Creation complete

Step 5: Publish the DNS record

resource "ionoscloud_dns_zone" "taskboard" {
  name    = "taskboard.example.com"
  enabled = true
}
resource "ionoscloud_dns_record" "api" {
  zone_id = ionoscloud_dns_zone.taskboard.id
  name    = "api"
  type    = "A"
  content = ionoscloud_nic.api_public.ips[0]
  ttl     = 300
  enabled = true
}

Expected output:

ionoscloud_dns_record.api: Creation complete

Step 6: Apply and verify egress from a private host

terraform apply -auto-approve
# SSH to the API server via its public IP, then test outbound through NAT:
ssh root@$(terraform output -raw api_public_ip)
curl -s https://ifconfig.io   # should return the NAT gateway public IP

Expected output:

<the public IP from ionoscloud_ipblock.nat>

Step 7: Verify tier connectivity

# From the API server, reach the worker on the app LAN:
ping -c 2 10.7.1.20

Expected output:

2 packets transmitted, 2 received, 0% packet loss

Validation Checklist:

  • [ ] Three LANs created (1 public, 2 private)
  • [ ] API server has both a public and an app-tier NIC
  • [ ] Private host reaches the internet through the NAT public IP
  • [ ] API server and worker communicate on the app LAN
  • [ ] DNS A record resolves to the API public IP

Cleanup:

terraform destroy -auto-approve

Common Pitfalls

Developer mistakes to avoid with network provisioning on IONOS Cloud:

  1. Expecting private LAN hosts to reach the internet without a NAT gateway

    • Problem: Your API server on a private LAN cannot apt update or call external APIs, and connections simply time out.
    • Why it happens: A private LAN (public = false) has no internet route. The default route is not auto-injected even after you attach a NAT gateway.
    • Fix: Provision a ionoscloud_natgateway with a SNAT rule, then point the hosts' default gateway at the gateway IP via cloud-init or DHCP options:
    lans {
      id          = ionoscloud_lan.app.id
      gateway_ips = ["10.7.1.1/24"]
    }
    
  2. Mismatched VPN traffic selectors

    • Problem: The IPSec tunnel shows as up, but no packets cross it and on-premises hosts remain unreachable.
    • Why it happens: cloud_network_cidrs and peer_network_cidrs do not exactly match the subnets configured on the remote peer, so traffic is not selected for encryption.
    • Fix: Align the CIDRs on both ends precisely. The IONOS side must list its own LAN subnet and the peer's subnet that mirrors the remote device's configuration:
    cloud_network_cidrs = ["10.7.1.0/24"]
    peer_network_cidrs  = ["192.168.50.0/24"]
    
  3. Using an invalid TTL on a DNS record

    • Problem: Creating a DNS record fails with a validation error on the ttl field.
    • Why it happens: The TTL is outside the allowed range of 60 to 604800 seconds (for example a ttl = 30 carried over from another provider).
    • Fix: Clamp TTL into the supported range:
    resource "ionoscloud_dns_record" "api" {
      ttl = 300   # valid: 60..604800
    }
    

Summary

You can now build a complete, segmented network for an application entirely as code. LANs give you tiered isolation, NICs wire servers into one or more tiers with static or DHCP addressing, a NAT gateway grants controlled outbound access to private hosts, an IPSec gateway bridges to on-premises networks, and DNS records publish the endpoint, all declared in Terraform and reproducible on demand. TaskBoard now has a public ingress tier, a private application tier, and an isolated database tier, with private egress and a resolvable API hostname.

The network stack is the substrate the rest of Module 2 builds on. Unit 2.3 adds load balancers and Network Security Group rules on top of these LANs and NICs, and Module 4 connects application code to the managed databases that will live on the database tier you just created.

Key Points:

  • One ionoscloud_lan per tier; public = true connects to the internet gateway, public = false isolates the tier
  • A server joins multiple tiers by having multiple ionoscloud_nic resources, one per LAN
  • Private LANs need a SNAT-only ionoscloud_natgateway for outbound access, and the default route is not auto-injected
  • IPSec VPN uses IKEv2 with a PSK and requires exactly matching traffic selectors on both ends
  • DNS zones and records are Terraform resources with TTLs constrained to 60 to 604800 seconds, served over an Anycast network

Important Terminology:

  • LAN: A layer-2 broadcast domain scoped to one VDC, the unit of network segmentation; provisioned with ionoscloud_lan.
  • NIC: A network interface attaching a server to a LAN; a server with NICs on multiple LANs participates in multiple tiers.
  • SNAT (Source NAT): The translation the NAT gateway performs, rewriting outbound source addresses to a public IP while blocking unsolicited inbound traffic.
  • Traffic selector: The cloud and peer CIDR pair on an IPSec tunnel that determines which traffic is encrypted and routed across the VPN.
  • Apex record: A DNS record at the zone root, created in Cloud DNS by leaving the record name field empty.

Next Steps

Continue Learning: Unit 2.3: Load Balancing and Security as Code

Related Topics: