Cloud-init is the industry standard for automating the initial setup of cloud instances. When you deploy a new Breeze, you can provide a user data script that runs automatically on first boot — installing packages, creating users, configuring services, and deploying your application without any manual SSH sessions.
What Is Cloud-Init?
Cloud-init is a multi-distribution package that handles early initialization of a cloud instance. It runs during the first boot and can configure nearly every aspect of the system. Most Linux cloud images (Ubuntu, Debian, AlmaLinux, Rocky) come with cloud-init pre-installed.
Basic User Data Script
The simplest format is a bash script starting with #!:
#!/bin/bash
# This script runs as root on first boot
# Update the system
apt update && apt upgrade -y
# Install essential packages
apt install -y nginx mysql-server php8.3-fpm php8.3-mysql
# Enable and start services
systemctl enable --now nginx
systemctl enable --now mysql
systemctl enable --now php8.3-fpm
# Create a non-root user
useradd -m -s /bin/bash deploy
echo "deploy ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/deploy
# Set up SSH key for the new user
mkdir -p /home/deploy/.ssh
echo "ssh-ed25519 AAAA... your-key" > /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
Cloud-Config YAML Format
For more complex setups, cloud-init supports a YAML-based configuration format:
#cloud-config
# Set the hostname
hostname: web-prod-01
fqdn: web-prod-01.example.com
# Create users
users:
- name: deploy
groups: sudo
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAA... your-key
# Install packages
packages:
- nginx
- certbot
- python3-certbot-nginx
- fail2ban
- ufw
- htop
- git
# Run commands on first boot
runcmd:
- ufw allow ssh
- ufw allow http
- ufw allow https
- ufw --force enable
- systemctl enable --now fail2ban
- systemctl enable --now nginx
# Write configuration files
write_files:
- path: /etc/nginx/sites-available/myapp.conf
content: |
server {
listen 80;
server_name example.com;
root /var/www/myapp/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
owner: root:root
permissions: "0644"
# Set timezone
timezone: America/New_York
# Configure automatic security updates
package_update: true
package_upgrade: true
Advanced: Full LEMP Stack Deployment
#!/bin/bash
set -euo pipefail
# Variables
DB_NAME="myapp"
DB_USER="myapp"
DB_PASS=$(openssl rand -base64 24)
DOMAIN="example.com"
# System setup
apt update && apt upgrade -y
apt install -y nginx mysql-server php8.3-fpm php8.3-mysql
php8.3-curl php8.3-xml php8.3-mbstring php8.3-zip
certbot python3-certbot-nginx fail2ban ufw git
# Firewall
ufw allow ssh
ufw allow http
ufw allow https
ufw --force enable
# MySQL setup
mysql -e "CREATE DATABASE ${DB_NAME};"
mysql -e "CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';"
mysql -e "GRANT ALL ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';"
mysql -e "FLUSH PRIVILEGES;"
# Save credentials securely
cat > /root/.db-credentials > /var/log/cloud-init-custom.log
Debugging Cloud-Init
# Check cloud-init status
cloud-init status
# View the cloud-init log
cat /var/log/cloud-init-output.log
# View detailed cloud-init logs
cat /var/log/cloud-init.log
# Re-run cloud-init (for testing)
sudo cloud-init clean
sudo cloud-init init
sudo cloud-init modules --mode=config
sudo cloud-init modules --mode=final
Best Practices
- Test locally first — Use multipass or LXD to test your cloud-init configs before deploying
- Use set -euo pipefail — Makes bash scripts fail on errors instead of continuing
- Log everything — Redirect output to a log file for debugging
- Don't hardcode secrets — Generate passwords dynamically with openssl rand
- Keep it idempotent — Scripts should be safe to run multiple times
- Start simple — Get the basics working before adding complexity