Skip to content

[core]: cloud‐init.func

CanbiZ edited this page Dec 1, 2025 · 1 revision

Cloud-Init.func Wiki

VM cloud-init configuration and first-boot setup module for Proxmox VEs, providing automatic system initialization, network configuration, user account setup, and SSH key management for virtual machines.


📋 Table of Contents


Overview

Cloud-init.func provides VM first-boot automation infrastructure:

  • ✅ Cloud-init drive creation (IDE2 or SCSI fallback)
  • ✅ User account and password configuration
  • ✅ SSH public key injection
  • ✅ Network configuration (DHCP or static IP)
  • ✅ DNS and search domain setup
  • ✅ Interactive whiptail-based configuration
  • ✅ Credential file generation and display
  • ✅ Support for Debian nocloud and Ubuntu cloud-init
  • ✅ System package upgrade on first boot

Integration Pattern

# In Proxmox VM creation scripts
source <(curl -fsSL .../cloud-init.func)

# Basic setup:
setup_cloud_init "$VMID" "$STORAGE" "$HOSTNAME" "yes"

# Interactive setup:
configure_cloud_init_interactive "root"
setup_cloud_init "$VMID" "$STORAGE" "$HOSTNAME" "$CLOUDINIT_ENABLE"

First-Boot Sequence

VM Power On
    ↓
Cloud-init boot phase
    ↓
Read cloud-init config
    ↓
Create/modify user account
    ↓
Install SSH keys
    ↓
Configure network
    ↓
Set DNS/search domain
    ↓
Upgrade packages (if configured)
    ↓
Boot completion

Cloud-Init Fundamentals

What is Cloud-Init?

Cloud-init is a system that runs on the first boot of a VM/Instance to:

  • Create user accounts
  • Set passwords
  • Configure networking
  • Install SSH keys
  • Run custom scripts
  • Manage system configuration

Proxmox Cloud-Init Integration

Proxmox VE supports cloud-init natively via:

  • Cloud-init drive: IDE2 or SCSI disk with cloud-init data
  • QEMU parameters: User, password, SSH keys, IP configuration
  • First-boot services: systemd services that execute on first boot

Nocloud Data Source

Proxmox uses the nocloud data source (no internet required):

  • Configuration stored on local cloud-init drive
  • No external network call needed
  • Works in isolated networks
  • Suitable for private infrastructure

Main Configuration Functions

setup_cloud_init()

Purpose: Configures Cloud-init for automatic VM first-boot setup.

Signature:

setup_cloud_init()

Parameters:

  • $1 - VMID (required, e.g., 100)
  • $2 - Storage name (required, e.g., local, local-lvm)
  • $3 - Hostname (optional, default: vm-${VMID})
  • $4 - Enable Cloud-Init (yes/no, default: no)
  • $5 - User (optional, default: root)
  • $6 - Network mode (dhcp/static, default: dhcp)
  • $7 - Static IP (optional, CIDR format: 192.168.1.100/24)
  • $8 - Gateway (optional)
  • $9 - Nameservers (optional, space-separated, default: 1.1.1.1 8.8.8.8)

Returns: 0 on success, 1 on failure; exits if not enabled

Behavior:

# If enable="no":
# Returns immediately (skips all configuration)

# If enable="yes":
# 1. Create cloud-init drive (IDE2, fallback to SCSI1)
# 2. Set user account
# 3. Generate random password
# 4. Configure network
# 5. Set DNS servers
# 6. Add SSH keys (if available)
# 7. Save credentials to file
# 8. Export variables for calling script

Operations:

Operation Command Purpose
Create drive qm set $vmid --ide2 $storage:cloudinit Cloud-init data disk
Set user qm set $vmid --ciuser $ciuser Initial user
Set password qm set $vmid --cipassword $cipassword Auto-generated
SSH keys qm set $vmid --sshkeys $SSH_KEYS_FILE Pre-injected
DHCP network qm set $vmid --ipconfig0 ip=dhcp Dynamic IP
Static network qm set $vmid --ipconfig0 ip=192.168.1.100/24,gw=192.168.1.1 Fixed IP
DNS qm set $vmid --nameserver $servers 1.1.1.1 8.8.8.8
Search domain qm set $vmid --searchdomain local Local domain

Environment Variables Set:

  • CLOUDINIT_USER - Username configured
  • CLOUDINIT_PASSWORD - Generated password (in memory only)
  • CLOUDINIT_CRED_FILE - Path to credentials file

Usage Examples:

# Example 1: Basic DHCP setup
VMID=100
STORAGE="local-lvm"
setup_cloud_init "$VMID" "$STORAGE" "myvm" "yes"
# Result: VM configured with DHCP, random password, root user

# Example 2: Static IP configuration
setup_cloud_init "$VMID" "$STORAGE" "myvm" "yes" "root" \
  "static" "192.168.1.100/24" "192.168.1.1" "1.1.1.1 8.8.8.8"
# Result: VM configured with static IP, specific DNS

# Example 3: Disabled (no cloud-init)
setup_cloud_init "$VMID" "$STORAGE" "myvm" "no"
# Result: Function returns immediately, no configuration

configure_cloud_init_interactive()

Purpose: Interactive whiptail-based configuration prompts for user preferences.

Signature:

configure_cloud_init_interactive()

Parameters:

  • $1 - Default user (optional, default: root)

Returns: 0 on success, 1 if whiptail unavailable; exports configuration variables

Environment Variables Exported:

  • CLOUDINIT_ENABLE - Enable (yes/no)
  • CLOUDINIT_USER - Username
  • CLOUDINIT_NETWORK_MODE - dhcp or static
  • CLOUDINIT_IP - Static IP (if static mode)
  • CLOUDINIT_GW - Gateway (if static mode)
  • CLOUDINIT_DNS - DNS servers (space-separated)

User Prompts (5 questions):

  1. Enable Cloud-Init? (yes/no)
  2. Username? (default: root)
  3. Network Mode? (DHCP or static)
  4. Static IP? (if static, CIDR format)
  5. Gateway IP? (if static)
  6. DNS Servers? (default: 1.1.1.1 8.8.8.8)

Fallback Behavior:

  • If whiptail unavailable: Shows warning and returns 1
  • Auto-defaults to DHCP if error occurs
  • Non-interactive: Can be skipped in scripts

Implementation Pattern:

configure_cloud_init_interactive() {
  local default_user="${1:-root}"

  # Check whiptail availability
  if ! command -v whiptail >/dev/null 2>&1; then
    echo "Warning: whiptail not available"
    export CLOUDINIT_ENABLE="no"
    return 1
  fi

  # Ask enable
  if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \
    --yesno "Enable Cloud-Init for VM configuration?" 16 68); then
    export CLOUDINIT_ENABLE="no"
    return 0
  fi

  export CLOUDINIT_ENABLE="yes"

  # Username
  CLOUDINIT_USER=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \
    "Cloud-Init Username" 8 58 "$default_user" --title "USERNAME" 3>&1 1>&2 2>&3)
  export CLOUDINIT_USER="${CLOUDINIT_USER:-$default_user}"

  # Network mode
  if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK MODE" \
    --yesno "Use DHCP for network configuration?" 10 58); then
    export CLOUDINIT_NETWORK_MODE="dhcp"
  else
    export CLOUDINIT_NETWORK_MODE="static"
    # ... prompt for static IP and gateway ...
  fi

  # DNS servers
  CLOUDINIT_DNS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox \
    "DNS Servers (space-separated)" 8 58 "1.1.1.1 8.8.8.8" --title "DNS" 3>&1 1>&2 2>&3)
  export CLOUDINIT_DNS
}

Usage Examples:

# Example 1: Interactive configuration
configure_cloud_init_interactive "root"
# Prompts user for all settings interactively
# Exports variables for use in setup_cloud_init()

# Example 2: With custom default user
configure_cloud_init_interactive "debian"
# Suggests "debian" as default username

# Example 3: In script workflow
configure_cloud_init_interactive "$DEFAULT_USER"
setup_cloud_init "$VMID" "$STORAGE" "$HOSTNAME" "$CLOUDINIT_ENABLE" "$CLOUDINIT_USER"
# User configures interactively, then script sets up VM

Configuration Parameters

VMID (Virtual Machine ID)

  • Type: Integer (100-2147483647)
  • Required: Yes
  • Example: 100
  • Validation: Must be unique, >= 100 on Proxmox

Storage

  • Type: String (storage backend name)
  • Required: Yes
  • Examples: local, local-lvm, ceph-rbd
  • Validation: Must exist in Proxmox
  • Cloud-init Drive: Placed on this storage

Hostname

  • Type: String (valid hostname)
  • Required: No (defaults to vm-${VMID})
  • Example: myvm, web-server-01
  • Format: Lowercase, alphanumeric, hyphens allowed

User

  • Type: String (username)
  • Required: No (defaults: root)
  • Example: root, ubuntu, debian
  • Cloud-init: User account created on first boot

Network Mode

  • Type: Enum (dhcp, static)
  • Default: dhcp
  • Options:
    • dhcp - Dynamic IP from DHCP server
    • static - Manual IP configuration

Static IP

  • Format: CIDR notation (192.168.1.100/24)
  • Example: 192.168.1.50/24, 10.0.0.5/8
  • Validation: Valid IP and netmask
  • Required: If network mode = static

Gateway

  • Format: IP address (192.168.1.1)
  • Example: 192.168.1.1, 10.0.0.1
  • Validation: Valid IP
  • Required: If network mode = static

Nameservers

  • Format: Space-separated IPs
  • Default: 1.1.1.1 8.8.8.8
  • Example: 1.1.1.1 8.8.8.8 9.9.9.9

DNS Search Domain

  • Type: String
  • Default: local
  • Example: example.com, internal.corp

Data Formats

Cloud-Init Credentials File

Generated at: /tmp/${hostname}-${vmid}-cloud-init-credentials.txt

Format:

========================================
Cloud-Init Credentials
========================================
VM ID:    100
Hostname: myvm
Created:  Tue Dec 01 10:30:00 UTC 2024

Username: root
Password: s7k9mL2pQ8wX

Network:  dhcp
DNS:      1.1.1.1 8.8.8.8

========================================
SSH Access (if keys configured):
ssh root@<vm-ip>

Proxmox UI Configuration:
VM 100 > Cloud-Init > Edit
- User, Password, SSH Keys
- Network (IP Config)
- DNS, Search Domain
========================================

Proxmox Cloud-Init Config

Stored in: /etc/pve/nodes/<node>/qemu-server/<vmid>.conf

Relevant Settings:

ide2: local-lvm:vm-100-cloudinit,media=cdrom
ciuser: root
cipassword: (encrypted)
ipconfig0: ip=dhcp
nameserver: 1.1.1.1 8.8.8.8
searchdomain: local

Network Configuration Examples

DHCP:

qm set 100 --ipconfig0 "ip=dhcp"

Static IPv4:

qm set 100 --ipconfig0 "ip=192.168.1.100/24,gw=192.168.1.1"

Static IPv6:

qm set 100 --ipconfig0 "ip6=2001:db8::100/64,gw6=2001:db8::1"

Dual Stack (IPv4 + IPv6):

qm set 100 --ipconfig0 "ip=192.168.1.100/24,gw=192.168.1.1,ip6=2001:db8::100/64,gw6=2001:db8::1"

Best Practices

1. Always Configure SSH Keys

# Ensure SSH keys available before cloud-init setup
CLOUDINIT_SSH_KEYS="/root/.ssh/authorized_keys"

if [ ! -f "$CLOUDINIT_SSH_KEYS" ]; then
  mkdir -p /root/.ssh
  # Generate or import SSH keys
fi

setup_cloud_init "$VMID" "$STORAGE" "$HOSTNAME" "yes"

2. Save Credentials Securely

# After setup_cloud_init():
# Credentials file generated at $CLOUDINIT_CRED_FILE

# Copy to secure location:
cp "$CLOUDINIT_CRED_FILE" "/root/vm-credentials/"
chmod 600 "/root/vm-credentials/$(basename $CLOUDINIT_CRED_FILE)"

# Or display to user:
cat "$CLOUDINIT_CRED_FILE"

3. Use Static IPs for Production

# DHCP - suitable for dev/test
setup_cloud_init "$VMID" "$STORAGE" "$HOSTNAME" "yes" "root" "dhcp"

# Static - suitable for production
setup_cloud_init "$VMID" "$STORAGE" "$HOSTNAME" "yes" "root" \
  "static" "192.168.1.100/24" "192.168.1.1"

4. Validate Network Configuration

# Before setting up cloud-init, ensure:
# - Gateway IP is reachable
# - IP address not in use
# - DNS servers are accessible

ping -c 1 "$GATEWAY" || msg_error "Gateway unreachable"

5. Test First Boot

# After cloud-init setup:
qm start "$VMID"

# Wait for boot
sleep 10

# Check cloud-init status
qm exec "$VMID" cloud-init status

# Verify network configuration
qm exec "$VMID" hostname -I

Troubleshooting

Cloud-Init Not Applying

# Inside VM:
cloud-init status        # Show cloud-init status
cloud-init analyze       # Analyze cloud-init boot
cloud-init query         # Query cloud-init datasource

# Check logs:
tail -100 /var/log/cloud-init-output.log
tail -100 /var/log/cloud-init.log

Network Not Configured

# Verify cloud-init config in Proxmox:
cat /etc/pve/nodes/$(hostname)/qemu-server/100.conf

# Check cloud-init drive:
qm config 100 | grep ide2

# In VM, verify cloud-init wrote config:
cat /etc/netplan/99-cloudinit.yaml

SSH Keys Not Installed

# Verify SSH keys set in Proxmox:
qm config 100 | grep sshkeys

# In VM, check SSH directory:
ls -la /root/.ssh/
cat /root/.ssh/authorized_keys

Password Not Set

# Regenerate cloud-init drive:
qm set 100 --delete ide2        # Remove cloud-init drive
qm set 100 --ide2 local-lvm:vm-100-cloudinit,media=cdrom  # Re-create

# Set password again:
qm set 100 --cipassword "newpassword"

Contributing

Adding New Configuration Options

  1. Add parameter to setup_cloud_init() function signature
  2. Add validation for parameter
  3. Add qm set command to apply configuration
  4. Update documentation with examples
  5. Test on actual Proxmox VE

Enhancing Interactive Configuration

  1. Add new whiptail dialog to configure_cloud_init_interactive()
  2. Export variable for use in setup
  3. Add validation logic
  4. Test with various input scenarios

Supporting New Data Sources

Beyond nocloud, could support:

  • ConfigDrive (cloud-init standard)
  • ESXi (if supporting vSphere)
  • Hyper-V (if supporting Windows)

Notes

  • Cloud-init requires QEMU guest agent for optimal functionality
  • Network configuration applied on first boot only
  • Credentials file contains sensitive information - keep secure
  • SSH keys are persisted and not displayed in credentials file
  • Cloud-init is optional - VMs work without it

Clone this wiki locally