HostUpCloudHostUpCloudDocs

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

  1. During instance creation, enter your script in the Cloud-Init section
  2. The script is stored as a PVE snippet on the hypervisor
  3. On first boot, cloud-init picks up the snippet and executes it alongside the system's default cloud-init stages
  4. 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 docker

Install LAMP Stack

#cloud-config
packages:
  - apache2
  - mysql-server
  - php
  - php-mysql
runcmd:
  - systemctl enable apache2 mysql
  - systemctl start apache2 mysql

Add Swap Space

#cloud-config
swap:
  filename: /swapfile
  size: 2G

Install 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 20

Harden 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 sshd

Limitations

ConstraintDetail
Maximum size16 KB per script
ExecutionRuns once on first boot only
ErrorsFailures do not prevent the instance from starting — check /var/log/cloud-init.log for errors
Supported imagesAll 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:

  1. SSH into the instance (or use the web console)
  2. Check the cloud-init log:
sudo cat /var/log/cloud-init.log
  1. Check the output log for your runcmd commands:
sudo cat /var/log/cloud-init-output.log
  1. Verify cloud-init status:
cloud-init status --long

Next 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

On this page