Provision with cloud-init
cloud-init is the industry-standard multi-distribution method for cross-platform cloud instance initialization. Garden Linux uses cloud-init on all major cloud platforms (AWS, Azure, GCP, OpenStack) for first-boot provisioning and configuration.
What is cloud-init?
cloud-init is a widely-adopted provisioning tool that automates the initialization of cloud instances. It runs on every boot (not just first boot) and performs tasks including:
- Creating users and groups
- Adding SSH authorized keys
- Configuring networking (DHCP, static IPs)
- Writing files to disk
- Running custom shell scripts
- Installing packages
- Configuring system services
cloud-init fetches its configuration from the cloud platform's metadata service or via user-data mechanisms specific to each platform.
When to Use cloud-init
Use cloud-init for cloud platform deployments (AWS, Azure, GCP, OpenStack, VMware). All Garden Linux cloud platform images include cloud-init by default.
For bare-metal and PXE deployments, use Ignition instead, as Ignition is designed for first-boot-only metal provisioning.
Prerequisites
- Garden Linux cloud platform image (aws, azure, gcp, openstack, vmware flavors automatically include cloud-init)
- Access to the cloud platform's user-data mechanism (varies by platform)
Configuration Format and Basic Structure
cloud-init supports two user-data formats: cloud-config (YAML) and shell scripts.
cloud-config (YAML Format)
Start with #cloud-config. Provides declarative configuration:
#cloud-config
users: []
write_files: []
runcmd: []Shell Scripts (Shebang Format)
Start with a shebang (#!/usr/bin/env bash). cloud-init executes the script directly:
#!/usr/bin/env bash
systemctl enable --now ssh
echo "Hello from Garden Linux" > /etc/motdUse shell scripts for simple tasks and procedural logic. Use cloud-config for declarative user and file management.
Common Provisioning Tasks
Create Users
Add a new user with passwordless sudo access:
#cloud-config
users:
- name: gardenlinux
groups: wheel
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALLThe wheel group provides passwordless sudo access in Garden Linux.
Add SSH Authorized Keys
Add SSH public keys for user authentication:
#cloud-config
users:
- name: gardenlinux
groups: wheel
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExamplePublicKeyHere user@hostConfigure Networking
Configure a static IP address using systemd-networkd:
#cloud-config
write_files:
- path: /etc/systemd/network/10-eth0.network
content: |
[Match]
Name=eth0
[Network]
Address=192.168.1.100/24
Gateway=192.168.1.1
DNS=9.9.9.9
owner: root:root
permissions: '0644'
runcmd:
- systemctl restart systemd-networkdMost cloud platforms handle networking automatically via DHCP and metadata services. Use custom network configuration only when required.
Install Packages
Install additional software packages on first boot:
#cloud-config
runcmd:
- mount -o remount,rw /usr
- apt-get update
- apt-get install -y htop vim curl
- mount -o remount,ro /usrGarden Linux mounts /usr read-only by default. Remount it read-write before installing packages, then remount read-only afterward.
Alternatively, use a shell script for package installation:
#!/usr/bin/env bash
# Install additional packages
mount -o remount,rw /usr
apt-get update
apt-get install -y htop vim curl
mount -o remount,ro /usrEnable and Start Services
Enable and start systemd units:
#cloud-config
runcmd:
- systemctl enable --now ssh
- systemctl enable --now my-appFor custom service definitions, write the unit file and enable it:
#cloud-config
write_files:
- path: /etc/systemd/system/my-app.service
content: |
[Unit]
Description=My Application Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my-app
Restart=on-failure
[Install]
WantedBy=multi-user.target
owner: root:root
permissions: '0644'
runcmd:
- systemctl daemon-reload
- systemctl enable --now my-appWrite Configuration Files
Write custom configuration files to disk:
#cloud-config
write_files:
- path: /etc/hostname
content: |
my-server.example.com
owner: root:root
permissions: '0644'
- path: /opt/myapp/config.yaml
content: |
database:
host: db.example.com
port: 5432
owner: root:root
permissions: '0600'Run Custom Scripts
Execute custom shell scripts during instance initialization:
#!/usr/bin/env bash
set -euo pipefail
# Enable SSH
systemctl enable --now ssh
# Create application directory
mkdir -p /opt/myapp/data
# Write configuration
cat > /opt/myapp/config.yaml <<EOF
database:
host: db.example.com
port: 5432
EOF
echo "Setup completed"Or use cloud-config to write and execute a script:
#cloud-config
write_files:
- path: /usr/local/bin/setup.sh
content: |
#!/usr/bin/env bash
set -euo pipefail
# Create application directory
mkdir -p /opt/myapp/data
# Write configuration
cat > /opt/myapp/config.yaml <<EOF
database:
host: db.example.com
port: 5432
EOF
echo "Setup completed"
owner: root:root
permissions: '0755'
runcmd:
- /usr/local/bin/setup.shDeliver the Configuration
How you deliver cloud-init configuration depends on the cloud platform. Each platform provides its own mechanism for attaching user-data to instances:
| Platform | Delivery Mechanism | Official Documentation | Garden Linux Guide |
|---|---|---|---|
| AWS | EC2 user-data | EC2 User Data | Install on AWS |
| Azure | Custom data | Azure Custom Data | Install on Azure |
| GCP | Metadata | GCP cloud-init | Install on GCP |
| OpenStack | ConfigDrive or metadata service | OpenStack User Data | Install on OpenStack |
For step-by-step deployment walkthroughs including instance creation with user-data, see the platform-specific tutorials:
Default Usernames per Platform
Garden Linux cloud images include a platform-specific default user:
| Platform | Default Username |
|---|---|
| AWS | ec2-user |
| Azure | azureuser |
| GCP | gardenlinux |
| OpenStack | admin |
These users are pre-configured with SSH key access via the cloud platform's metadata service. You only need to enable SSH to access them.
Advanced Configuration
Multi-Part MIME User-Data
Combine multiple user-data formats (cloud-config + shell script):
#!/usr/bin/env python3
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
combined = MIMEMultipart()
# Part 1: cloud-config
cloud_config = """#cloud-config
users:
- name: gardenlinux
groups: wheel
"""
combined.attach(MIMEText(cloud_config, 'cloud-config'))
# Part 2: shell script
shell_script = """#!/usr/bin/env bash
systemctl enable --now ssh
"""
combined.attach(MIMEText(shell_script, 'x-shellscript'))
print(combined.as_string())Conditional Execution
Run commands only on specific platforms:
#cloud-config
runcmd:
- [ sh, -c, 'if [ "$(cloud-init query platform)" = "aws" ]; then echo "Running on AWS"; fi' ]Fetch External Configuration
Download and apply configuration from external sources:
#cloud-config
runcmd:
- curl -o /tmp/setup.sh https://example.com/setup.sh
- bash /tmp/setup.shTroubleshooting
View cloud-init Logs
Check cloud-init execution logs:
# View cloud-init status
cloud-init status --long
# View cloud-init logs
journalctl -u cloud-init
# View detailed logs
cat /var/log/cloud-init.log
cat /var/log/cloud-init-output.logValidate Configuration Syntax
Test cloud-config syntax before deploying:
# Validate cloud-config YAML
cloud-init schema --config-file user_data.yaml
# Dry-run cloud-init
cloud-init devel schema --config-file user_data.yamlCommon Errors
- SSH not enabled — Ensure user-data includes
systemctl enable --now ssh - User not created — Verify cloud-config YAML syntax and check
/var/log/cloud-init.log - Metadata not fetched — Confirm the instance has network connectivity to the metadata service
- Script not executed — Ensure shell scripts start with a valid shebang (
#!/usr/bin/env bash)
Reference
- cloud-init Documentation
- cloud-init Examples
- cloud-init Module Reference
- cloud-init Network Configuration
- AWS EC2 User Data
- Azure Custom Data
- GCP cloud-init
- OpenStack User Data