cloud-init
cloud-init is the industry-standard tool for cloud instance initialization, automatically completing system configuration on first boot. Ubuntu 26.04 Server comes with cloud-init pre-installed and supports major cloud platforms such as AWS, Azure, GCP, and others.
How It Works
cloud-init runs through the following stages during instance startup:
- Generator - Detects whether the instance is running in a cloud environment
- Local - Obtains network configuration from local data sources
- Network - Fetches metadata and user-data
- Config - Executes configuration modules
- Final - Executes final modules (e.g., running scripts)
# Check cloud-init version
cloud-init --version
# Check cloud-init status
cloud-init status
# View detailed status
cloud-init status --longData Sources
Metadata
Instance metadata provided by the cloud platform, including instance ID, IP address, hostname, etc.:
# Retrieve metadata on AWS EC2 (IMDSv2)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/
# Retrieve metadata on GCP
curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/
# Retrieve metadata on Azure
curl -H "Metadata: true" "http://169.254.169.254/metadata/instance?api-version=2021-02-01"User-data
User-defined initialization configuration that supports multiple formats.
user-data Configuration
Cloud-config Format (Most Common)
A YAML file starting with #cloud-config:
#cloud-config
# Set hostname
hostname: web-server-01
fqdn: web-server-01.example.com
# Create users
users:
- name: deploy
groups: sudo, docker
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAA... deploy@workstation
- name: monitor
groups: []
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA... monitor@management
# Set timezone
timezone: America/New_York
# NTP configuration
ntp:
enabled: true
servers:
- pool.ntp.org
- time.google.com
# Locale
locale: en_US.UTF-8
# Change APT mirror
apt:
primary:
- arches: [default]
uri: http://archive.ubuntu.com/ubuntu
# Install packages
packages:
- nginx
- docker.io
- fail2ban
- htop
- curl
- vim
# Upgrade all packages
package_update: true
package_upgrade: true
package_reboot_if_required: true
# Write files
write_files:
- path: /etc/nginx/sites-available/default
content: |
server {
listen 80;
server_name _;
root /var/www/html;
index index.html;
}
owner: root:root
permissions: '0644'
- path: /etc/sysctl.d/99-custom.conf
content: |
net.ipv4.ip_forward = 1
net.core.somaxconn = 65535
owner: root:root
permissions: '0644'
# Run commands (executed during cloud-init config stage)
runcmd:
- sysctl -p /etc/sysctl.d/99-custom.conf
- systemctl enable --now nginx
- systemctl enable --now fail2ban
- ufw allow ssh
- ufw allow 80/tcp
- ufw allow 443/tcp
- ufw --force enable
# Enable/disable SSH password login
ssh_pwauth: false
# SSH key configuration
ssh_keys:
ed25519_private: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
ed25519_public: ssh-ed25519 AAAA...
# Set passwords (hashed)
chpasswd:
expire: false
users:
- name: deploy
type: RANDOM
# Final message
final_message: "Cloud-init complete! Took $UPTIME seconds."
# Post-boot action
power_state:
mode: reboot
message: "cloud-init configuration complete, rebooting..."
timeout: 30
condition: trueShell Script Format
Starting with #!/bin/bash:
#!/bin/bash
set -euxo pipefail
# Update the system
apt update && apt upgrade -y
# Install base tools
apt install -y nginx docker.io
# Configure the firewall
ufw allow ssh
ufw allow 80/tcp
ufw --force enable
# Custom script
echo "Setup complete at $(date)" > /var/log/cloud-init-custom.logMulti-Part Format (MIME Multi-Part)
Combine multiple formats:
# Use the cloud-init tool to create a multi-part message
cloud-init devel make-mime \
--attach script.sh:text/x-shellscript \
--attach config.yaml:text/cloud-config \
> combined-userdata.txtCommon Use Cases
Initialize a Docker Environment
#cloud-config
packages:
- docker.io
- docker-compose-v2
groups:
- docker
users:
- name: deploy
groups: sudo, docker
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAA...
write_files:
- path: /etc/docker/daemon.json
content: |
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
runcmd:
- systemctl enable --now docker
- docker pull nginx:alpineInitialize Disks
#cloud-config
disk_setup:
/dev/vdb:
table_type: gpt
layout: true
overwrite: false
fs_setup:
- label: data
filesystem: ext4
device: /dev/vdb1
overwrite: false
mounts:
- ["/dev/vdb1", "/data", "ext4", "defaults,noatime", "0", "2"]
runcmd:
- mkdir -p /dataJoin a Cluster
#cloud-config
runcmd:
- curl -sfL https://get.k3s.io | K3S_URL=https://master:6443 K3S_TOKEN=mytoken sh -Local Testing
Using Multipass
# Install Multipass
sudo snap install multipass
# Launch an instance with cloud-config
multipass launch 26.04 --name test --cloud-init cloud-config.yaml
# Enter the instance
multipass shell test
# View cloud-init logs
multipass exec test -- cat /var/log/cloud-init-output.logUsing LXD
# Test cloud-init via LXD
lxc launch ubuntu:26.04 test --config=user.user-data="$(cat cloud-config.yaml)"
lxc exec test -- cloud-init status --wait
lxc exec test -- cat /var/log/cloud-init-output.logValidate Configuration Locally
# Validate cloud-config syntax
cloud-init schema --config-file cloud-config.yaml
# Use the devel subcommand for debugging
cloud-init devel render /path/to/userdata.yamlLogs and Debugging
# Main log file
sudo cat /var/log/cloud-init.log
# Output log
sudo cat /var/log/cloud-init-output.log
# View executed modules
sudo cat /run/cloud-init/result.json
# View user data
sudo cat /var/lib/cloud/instance/user-data.txt
# View instance data
sudo cloud-init query --all
# Query specific fields
sudo cloud-init query instance_id
sudo cloud-init query region
sudo cloud-init query local_hostnameRe-run cloud-init
# Clear cloud-init state (for testing)
sudo cloud-init clean
# Clear and reboot to re-execute
sudo cloud-init clean --reboot
# Clear logs only
sudo cloud-init clean --logs
# Manually trigger specific stages
sudo cloud-init init
sudo cloud-init modules --mode config
sudo cloud-init modules --mode finalDisable cloud-init
In non-cloud environments, you can disable cloud-init:
# Method 1: Create a disable marker file
sudo touch /etc/cloud/cloud-init.disabled
# Method 2: Via GRUB kernel parameter
# Add the following to GRUB_CMDLINE_LINUX in /etc/default/grub:
# cloud-init=disabled
# Method 3: Uninstall
sudo apt remove cloud-init -y
sudo rm -rf /etc/cloud /var/lib/cloudLast updated on