Cloud-Init & User Data
Automate instance configuration with cloud-init scripts and user data.
Cloud-Init & User Data
Cloud-init is an industry-standard tool that runs on first boot to automatically configure your instance. Instead of manually SSHing in to install packages, create users, or set up services, you provide a script at creation time and cloud-init handles the rest.
How It Works
- During instance creation, enter your script in the Cloud-Init section
- The script is stored as a PVE snippet on the hypervisor
- On first boot, cloud-init picks up the snippet and executes it alongside the system's default cloud-init stages
- Your instance is fully configured by the time you log in
Cloud-init runs once on first boot only. If you need to re-run a script, you must rebuild the instance or execute the commands manually via SSH or the web console.
Script Formats
Cloud-init accepts two formats. Choose the one that fits your use case.
Cloud-Config (YAML)
Prefix your script with #cloud-config to use the declarative YAML format. This is the recommended approach for most tasks — it's structured, idempotent, and well-documented.
#cloud-config
packages:
- nginx
- certbot
users:
- name: deploy
groups: sudo
shell: /bin/bash
ssh_authorized_keys:
- ssh-ed25519 AAAA... your-key
runcmd:
- systemctl enable nginx
- systemctl start nginx
write_files:
- path: /etc/motd
content: "Welcome to my server"Bash Script
Prefix your script with #!/bin/bash for raw shell commands. Use this when you need full control over execution order or when your setup logic is complex.
#!/bin/bash
apt update && apt install -y nginx certbot python3-certbot-nginx
systemctl enable nginx
ufw allow 'Nginx Full'Examples
Install Docker
#cloud-config
packages:
- docker.io
runcmd:
- systemctl enable docker
- systemctl start dockerInstall LAMP Stack
#cloud-config
packages:
- apache2
- mysql-server
- php
- php-mysql
runcmd:
- systemctl enable apache2 mysql
- systemctl start apache2 mysqlAdd Swap Space
#cloud-config
swap:
filename: /swapfile
size: 2GInstall Node.js via NVM
#!/bin/bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm install 20
nvm alias default 20Harden SSH
#cloud-config
runcmd:
- sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
- sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
- systemctl restart sshdLimitations
| Constraint | Detail |
|---|---|
| Maximum size | 16 KB per script |
| Execution | Runs once on first boot only |
| Errors | Failures do not prevent the instance from starting — check /var/log/cloud-init.log for errors |
| Supported images | All Linux images include cloud-init. Windows images use cloudbase-init with limited compatibility |
Keep your cloud-init script under 16 KB. Scripts exceeding this limit will be rejected during instance creation. For larger setup routines, use cloud-init to download and execute an external script.
Debugging
If your cloud-init script does not behave as expected:
- SSH into the instance (or use the web console)
- Check the cloud-init log:
sudo cat /var/log/cloud-init.log- Check the output log for your
runcmdcommands:
sudo cat /var/log/cloud-init-output.log- Verify cloud-init status:
cloud-init status --longNext Steps
- Set up SSH Keys for secure access to your configured instances
- Use Labels & Tags to organize instances by environment or project
- Review Getting Started for a full walkthrough of instance creation