cloud-init
Instance initialization system - user-data configuration, network setup, and Proxmox integration.
cloud-init Basics
Check cloud-init status
cloud-init status --long
status: done time: Thu, 10 Apr 2026 08:15:32 +0000 detail: DataSourceNoCloud [seed=/dev/sr0][dsmode=net]
Validate user-data before deploying
cloud-init schema --config-file user-data.yml
View rendered configuration
cloud-init query userdata
Analyze boot timing
cloud-init analyze blame
-- Boot Record 01 --
3.42s (modules-config/config-runcmd)
1.87s (modules-config/config-apt-configure)
0.52s (init-network/config-growpart)
Re-run cloud-init (testing)
cloud-init clean --logs
cloud-init init
cloud-init modules --mode=config
cloud-init modules --mode=final
GOTCHA: cloud-init only runs on first boot by default. Use cloud-init clean to reset.
User-Data Configuration
Minimal user-data
#cloud-config
hostname: web-01
fqdn: web-01.inside.domusdigitalis.dev
manage_etc_hosts: true
users:
- name: ansible
groups: wheel
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA... ansible@controller
package_update: true
package_upgrade: true
packages:
- vim
- htop
- qemu-guest-agent
runcmd:
- systemctl enable --now qemu-guest-agent
Network configuration (v2 / Netplan)
#cloud-config
network:
version: 2
ethernets:
eth0:
addresses:
- 10.50.1.101/24
gateway4: 10.50.1.1
nameservers:
addresses:
- 10.50.1.50
search:
- inside.domusdigitalis.dev
Write files
#cloud-config
write_files:
- path: /etc/motd
content: |
*** Managed by Ansible ***
Unauthorized access prohibited.
permissions: '0644'
- path: /etc/sysctl.d/99-hardening.conf
content: |
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
permissions: '0644'
Disk setup and mounts
#cloud-config
disk_setup:
/dev/vdb:
table_type: gpt
layout: true
fs_setup:
- device: /dev/vdb1
filesystem: ext4
label: data
mounts:
- [/dev/vdb1, /data, ext4, "defaults,noatime", "0", "2"]
Proxmox Integration
Terraform + cloud-init on Proxmox
resource "proxmox_vm_qemu" "node" {
name = "web-01"
target_node = "pve"
clone = "rocky9-template"
agent = 1
os_type = "cloud-init"
# cloud-init parameters
ciuser = "ansible"
sshkeys = file("~/.ssh/id_ed25519.pub")
ipconfig0 = "ip=10.50.1.101/24,gw=10.50.1.1"
nameserver = "10.50.1.50"
searchdomain = "inside.domusdigitalis.dev"
}
Packer template prep (clean before converting)
# Run inside Packer provisioner before template conversion
cloud-init clean --logs --seed
truncate -s 0 /etc/machine-id
rm -f /var/lib/dbus/machine-id
PIPELINE: Packer builds template with cloud-init installed → Terraform clones template → cloud-init configures on first boot.
Debugging
Log locations
# Primary log
cat /var/log/cloud-init.log
# Output from runcmd and scripts
cat /var/log/cloud-init-output.log
# Instance data
cat /run/cloud-init/instance-data.json | jq '.v1.instance_id'
Query instance metadata
cloud-init query instance_id
cloud-init query ds.meta_data
cloud-init query userdata
DEBUG PATTERN: cloud-init analyze show reveals which module is slow or failing.
Quick Reference
| Task | Command |
|---|---|
Validate user-data |
|
Check status |
|
Re-run cloud-init |
|
Show rendered config |
|
View boot log |
|
List modules run |
|
Debug boot time |
|
Dump instance metadata |
|