Network Configuration
Configuring networking via cloud-init and Netplan
Network Configuration via Cloud-Init
Cloud-init supports two network configuration formats. The format is specified in the network-config data provided by the datasource or by the user (e.g., NoCloud's network-config file).
Datasource
Provides network-config (or user overrides it)
cloud-init-local
Reads network-config in init-local stage
Renderer
Converts to Netplan, ENI, or networkd format
Networking Up
systemd-networkd or NetworkManager applies config
Note
Key distinction: Cloud-init's network-config format (v1/v2) is separate from Netplan YAML, even though v2 looks nearly identical. Cloud-init's v2 network-config is rendered into Netplan YAML (on Ubuntu) or directly into systemd-networkd/ENI files (on other distros). The renderer depends on what the distro uses.
V1 vs V2 Network Config
Modeled after /etc/network/interfaces. Still supported but deprecated in favor of v2.
network:
version: 1
config:
- type: physical
name: eth0
mac_address: "aa:bb:cc:dd:ee:f0"
subnets:
- type: static
address: 10.0.0.10/24
gateway: 10.0.0.1
dns_nameservers:
- 8.8.8.8
- 1.1.1.1
- type: nameserver
address:
- 8.8.8.8
search:
- internal.corp- Flat list of config entries
- Each entry has a
type(physical, bond, vlan, bridge, nameserver) - Subnets are nested under interfaces
- Verbose and less intuitive
Follows Netplan's schema. The recommended format for all new configurations.
network:
version: 2
ethernets:
eth0:
match:
macaddress: "aa:bb:cc:dd:ee:f0"
addresses:
- 10.0.0.10/24
routes:
- to: default
via: 10.0.0.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
search: [internal.corp]- Structured by interface type (ethernets, bonds, vlans, bridges)
- Cleaner, more readable
- Direct mapping to Netplan YAML
- Supports all modern features (routes, routing-policy, etc.)
Tip
Always use v2. V1 exists for backward compatibility with old OpenStack deployments and legacy images. Any new configuration should use v2 — it's more expressive, better documented, and directly maps to Netplan.
Netplan V2 Configuration Examples
Static IP
network:
version: 2
ethernets:
eth0:
addresses:
- 10.0.0.10/24
routes:
- to: default
via: 10.0.0.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
search: [internal.corp, prod.internal.corp]Note
Note: gateway4 is deprecated in Netplan 0.104+. Use a routes entry with to: default instead. Cloud-init v2 follows the same deprecation — always use the routes syntax.
DHCP
network:
version: 2
ethernets:
eth0:
dhcp4: true
dhcp6: false
dhcp4-overrides:
use-dns: false # ignore DHCP-provided DNS, use our own
use-routes: true
nameservers:
addresses: [10.0.0.2] # internal DNS resolverBonds
Bonding two interfaces for redundancy or throughput. Common in bare-metal and dedicated host scenarios.
network:
version: 2
ethernets:
eth0:
dhcp4: false
eth1:
dhcp4: false
bonds:
bond0:
interfaces: [eth0, eth1]
addresses:
- 10.0.0.10/24
routes:
- to: default
via: 10.0.0.1
parameters:
mode: 802.3ad # LACP
lacp-rate: fast
mii-monitor-interval: 100
transmit-hash-policy: layer3+4
nameservers:
addresses: [8.8.8.8]Common Bond Modes
| Mode | Name | Use Case |
|---|---|---|
active-backup |
Mode 1 | Simple failover — one active, one standby. Works without switch support. |
802.3ad |
Mode 4 (LACP) | Link aggregation. Requires switch LACP support. Best for throughput + redundancy. |
balance-alb |
Mode 6 | Adaptive load balancing. No switch support needed. Good for mixed environments. |
VLANs
network:
version: 2
ethernets:
eth0:
dhcp4: false
vlans:
eth0.100:
id: 100
link: eth0
addresses:
- 10.100.0.10/24
routes:
- to: 10.100.0.0/16
via: 10.100.0.1
eth0.200:
id: 200
link: eth0
addresses:
- 10.200.0.10/24
nameservers:
addresses: [10.200.0.2]Bridges
Bridge interfaces are essential for hosting VMs (libvirt/QEMU) or containers that need L2 network access.
network:
version: 2
ethernets:
eth0:
dhcp4: false
bridges:
br0:
interfaces: [eth0]
addresses:
- 10.0.0.10/24
routes:
- to: default
via: 10.0.0.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
parameters:
stp: false # disable STP for single-host bridges
forward-delay: 0 # no forwarding delay
mtu: 9000Tip
Proxmox/libvirt note: When provisioning a hypervisor via cloud-init, bridged networking is the standard pattern. The physical NIC (eth0) becomes a bridge member with no IP, and the bridge (br0) gets the IP. Guest VMs then attach to br0 for direct L2 access to the network.
Static Routes
network:
version: 2
ethernets:
eth0:
addresses:
- 10.0.0.10/24
routes:
- to: default
via: 10.0.0.1
- to: 10.10.0.0/16 # route to internal network
via: 10.0.0.254
metric: 100
- to: 172.16.0.0/12 # route to VPN network
via: 10.0.0.253
metric: 200
eth1: # management interface, separate route table
addresses:
- 192.168.1.10/24
routes:
- to: 192.168.0.0/16
via: 192.168.1.1
table: 100 # policy routing table
routing-policy:
- from: 192.168.1.10
table: 100DNS Configuration
network:
version: 2
ethernets:
eth0:
dhcp4: true
nameservers:
addresses:
- 10.0.0.2 # primary internal DNS
- 10.0.0.3 # secondary internal DNS
- 8.8.8.8 # fallback public DNS
search:
- prod.internal.corp # short names resolve here first
- internal.corp
- corpMTU Configuration
network:
version: 2
ethernets:
eth0: # standard internet-facing interface
dhcp4: true
mtu: 1500
eth1: # internal network with jumbo frames
addresses:
- 10.0.0.10/24
mtu: 9001 # AWS max MTU for placement group / ENANote
AWS MTU: EC2 instances within the same VPC support up to 9001 bytes MTU (jumbo frames) when using ENA. Cross-VPC and internet traffic is limited to 1500. Setting MTU 9001 on internal interfaces reduces CPU overhead for high-throughput workloads (fewer packets per byte transferred). Always verify with ping -M do -s 8972 <target> (8972 + 28 bytes IP/ICMP header = 9000).
Netplan: Ubuntu's Network Configuration Layer
Netplan is not a network daemon — it's a YAML abstraction layer that sits between you and the actual networking backend. Understanding this architecture is key.
Netplan YAML
/etc/netplan/*.yaml
netplan generate
Renders YAML to backend config
Backend
systemd-networkd OR NetworkManager
Network Up
Interfaces configured, routes applied
- Lightweight, headless, no GUI dependencies
- Default on Ubuntu Server, cloud images
- Config rendered to
/run/systemd/network/ - Managed via
networkctl - Ideal for servers, containers, and VMs
network:
version: 2
renderer: networkd # explicit (default on server)
ethernets:
eth0:
dhcp4: true- Feature-rich, GUI integration (GNOME, KDE)
- Default on Ubuntu Desktop
- Config rendered to
/etc/NetworkManager/system-connections/ - Managed via
nmcli/nmtui - Better for WiFi, VPN, user-switching scenarios
network:
version: 2
renderer: NetworkManager
ethernets:
eth0:
dhcp4: trueKey Netplan Commands
| Command | Description |
|---|---|
netplan generate |
Render YAML to backend config files (dry-run — does not apply) |
netplan apply |
Generate + apply immediately. Danger: if config is wrong, you lose connectivity. |
netplan try |
Apply temporarily, auto-revert after 120 seconds if not confirmed. Safe for remote changes. |
netplan get |
Show the merged/active configuration (Netplan 0.106+) |
netplan status |
Show interface status with Netplan context (Netplan 0.106+) |
Warning
Never use netplan apply over SSH without a fallback. If your config has an error that kills networking, you're locked out. Always use netplan try — it applies the config and starts a 120-second timer. If you don't press Enter to confirm, it rolls back to the previous config automatically. This is your safety net for remote network changes.
Cloud-Init Network Config Sources
Where does cloud-init get its network configuration? The source depends on the datasource and can be overridden by the user.
- Datasource default — The cloud provider's metadata service provides a default network config. On AWS, this is DHCP. On OpenStack, it might be static IPs from Neutron. Cloud-init applies this by default if no override exists.
- User override (NoCloud) — For local VMs, you provide a
network-configfile alongsideuser-dataandmeta-dataon the seed ISO or directory. This completely replaces the datasource default. - Drop-in files —
/etc/cloud/cloud.cfg.d/can containnetwork:configuration that overrides the datasource. Useful for baked images where you want specific network settings regardless of the cloud. - Disabling cloud-init networking — Create
/etc/cloud/cloud.cfg.d/99-disable-network-config.cfgwithnetwork: {config: disabled}. Cloud-init will not touch networking at all, and you manage Netplan directly.
NoCloud Seed Directory Example
/var/lib/cloud/seed/nocloud/
meta-data # instance-id, local-hostname
user-data # #cloud-config YAML or script
network-config # v2 network YAML
Or as a seed ISO (for libvirt/Proxmox):
$ genisoimage -output seed.iso -volid cidata -joliet -rock \
meta-data user-data network-config
# Or with cloud-localds (from cloud-image-utils package):
$ cloud-localds --network-config=network-config seed.iso user-data meta-data
Tip
Proxmox integration: Proxmox VE has native cloud-init support. When you add a Cloud-Init Drive to a VM template, Proxmox generates the NoCloud seed ISO automatically from the VM's cloud-init settings (IP config, user, SSH keys, DNS) in the Proxmox UI/API. No manual ISO creation needed.
Debugging Cloud-Init & Networking
When cloud-init networking doesn't work as expected, here's your debugging toolkit. These commands should be muscle memory.
cloud-init query: Inspect Resolved Configuration
# Show all resolved instance data
$ cloud-init query --all
{
"v1": {
"cloud_name": "aws",
"instance_id": "i-0abc123def456",
"region": "us-east-1",
...
},
"ds": { ... }
}
# Query specific fields
$ cloud-init query v1.instance_id
i-0abc123def456
$ cloud-init query ds.meta_data.network.interfaces
{...}
cloud-init schema: Validate Configuration
# Validate the running system's cloud-config
$ cloud-init schema --system
Valid schema /var/lib/cloud/instance/cloud-config.txt
# Validate a local file before deploying
$ cloud-init schema --config-file my-cloud-config.yaml
Valid cloud-config: my-cloud-config.yaml
# Check network-config schema
$ cloud-init schema --config-file network-config.yaml --schema-type network-config
Valid network-config: network-config.yaml
cloud-init analyze: Boot Performance
# Show how long each stage took
$ cloud-init analyze show
-- Boot Record 01 --
00.00000s (timestamp) Starting stage: init-local
00.51200s (timestamp) Finished stage: (init-local) 00.51200 seconds
01.23400s (timestamp) Starting stage: init-network
03.45600s (timestamp) Finished stage: (init-network) 02.22200 seconds
03.50000s (timestamp) Starting stage: modules-config
15.78900s (timestamp) Finished stage: (modules-config) 12.28900 seconds
15.80000s (timestamp) Starting stage: modules-final
25.12300s (timestamp) Finished stage: (modules-final) 09.32300 seconds
Total Time: 24.35400 seconds
# Blame: which modules took the longest
$ cloud-init analyze blame
-- Boot Record 01 --
09.12300s (modules-final/config-scripts-user)
08.45600s (modules-config/config-apt-configure)
03.21000s (modules-config/config-package-update-upgrade-install)
01.02300s (init-network/consume-user-data)
00.51200s (init-local/search-datasource)
Note
Performance insight: The blame output is invaluable for optimizing boot time. If apt-configure takes 8+ seconds, it's doing apt-get update on every boot (check package_update). If scripts-user takes 9+ seconds, your runcmd is slow — consider moving heavy work to a background systemd service instead.
cloud-init status: Check Current State
$ cloud-init status --long
status: done
extended_status: done
boot_status_code: enabled-by-generator
last_update: Tue, 25 Mar 2026 14:23:45 +0000
detail:
DataSourceEc2Local
# Wait for cloud-init to finish (useful in scripts)
$ cloud-init status --wait
..............done
status: done
Log Files
The main log. Contains detailed output from every cloud-init module, including:
- Datasource detection and probing
- Configuration merging and rendering
- Module execution (start/stop/errors)
- Network config rendering
- Schema validation results
This is where you find why something failed.
Captures stdout/stderr from user scripts and runcmd commands. Contains:
- Output from shell scripts in user-data
- Output from
runcmdcommands - Package installation output
- Any
echoorprintffrom your scripts
This is where you find what your code printed.
# Common debugging workflow
# 1. Check overall status
$ cloud-init status --long
# 2. If errors, check the main log
$ grep -i "error\|warning\|traceback" /var/log/cloud-init.log | tail -20
# 3. Check script output
$ tail -50 /var/log/cloud-init-output.log
# 4. See what user-data was received
$ cat /var/lib/cloud/instance/user-data.txt
# 5. See the merged cloud-config
$ cat /var/lib/cloud/instance/cloud-config.txt
# 6. See rendered network config (Ubuntu/Netplan)
$ cat /etc/netplan/50-cloud-init.yaml
# 7. See what datasource was detected
$ cat /run/cloud-init/ds-identify.log | tail -5
# 8. Collect everything for support
$ cloud-init collect-logs
Wrote /tmp/cloud-init.tar.gz
Networking-Specific Debugging
# Check what Netplan config cloud-init generated
$ cat /etc/netplan/50-cloud-init.yaml
# Verify Netplan can parse it
$ netplan generate 2>&1
# Check if cloud-init is managing networking
$ grep "network:" /etc/cloud/cloud.cfg.d/*.cfg
# Check rendered systemd-networkd config
$ ls /run/systemd/network/
# Check active network state
$ networkctl status eth0
# Check DNS resolution config
$ resolvectl status
# If cloud-init networking is fighting you, disable it:
$ cat > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg <<EOF
network: {config: disabled}
EOF
Warning
Common gotcha: Cloud-init writes /etc/netplan/50-cloud-init.yaml on every boot (by default). If you manually edit a Netplan file, cloud-init will overwrite your changes on next boot. To prevent this: either disable cloud-init networking (as shown above) or put your config in a higher-priority file like /etc/netplan/99-custom.yaml (Netplan merges files in lexicographic order, higher wins).
Quick Reference: Debugging Commands
| Command | Purpose |
|---|---|
cloud-init status --long |
Check if cloud-init finished, and if there were errors |
cloud-init status --wait |
Block until cloud-init finishes (useful in scripts) |
cloud-init query --all |
Dump all resolved instance data (metadata, user-data, etc.) |
cloud-init schema --system |
Validate the running instance's cloud-config against schema |
cloud-init analyze show |
Show timing for each boot stage |
cloud-init analyze blame |
Show slowest modules (like systemd-analyze blame) |
cloud-init clean --logs |
Reset cloud-init state — next boot will re-run everything |
cloud-init collect-logs |
Bundle all logs/config into a tarball for debugging |
cloud-init single --name cc_write_files |
Re-run a single module (for testing) |
netplan try |
Apply Netplan config with 120s auto-revert (safe for remote) |
netplan generate |
Render Netplan YAML to backend config (dry run) |