Skip to content
Search RunEntLinux

Searches general documentation on RunEntLinux. Each distribution has its own documentation search.

    Browse this documentation

    Cloud-Init Configuration

    Cloud-Init is the industry-standard tool for automatically performing initial configuration when a cloud instance boots for the first time. Nearly all major cloud platforms (AWS, Alibaba Cloud, Tencent Cloud, Azure, GCP) as well as local virtualization environments rely on Cloud-Init to handle instance network configuration, user creation, software installation, and more.

    Cloud-Init is a service that runs inside a cloud instance and performs the following operations on first boot:

    • Retrieves instance information from the cloud platform’s Metadata Service
    • Reads user-provided user-data configuration
    • Completes system initialization according to the configuration (creating users, installing software, writing files, etc.)

    In official Enterprise Linux (CentOS/AlmaLinux/Rocky Linux) cloud images, Cloud-Init comes pre-installed.

    Cloud-Init execution is divided into multiple stages:

    StageDescription
    cloud-init-localRuns earliest, applies local datasource configuration
    cloud-initRetrieves metadata and user data, configures networking
    cloud-configExecutes configuration modules from user data
    cloud-finalFinal stage, runs user-defined scripts and commands
    Terminal window
    # Check cloud-init version
    cloud-init --version
    # Check cloud-init runtime status
    cloud-init status
    # Check detailed status (including error messages)
    cloud-init status --long
    # Check the datasource cloud-init identified
    cloud-id

    User-Data is the configuration file passed to Cloud-Init, written in YAML format. The file must begin with #cloud-config.

    #cloud-config
    # Set default user password (for testing only; use SSH keys in production)
    chpasswd:
    list: |
    root:ChangeMe123!
    expire: false
    # Create users
    users:
    - default
    - name: deploy
    groups: wheel
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
    - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... deploy@workstation
    - name: developer
    groups: wheel
    lock_passwd: false
    passwd: $6$rounds=4096$salt$hashedpassword...
    # Allow SSH password authentication (usually disabled by default)
    ssh_pwauth: true
    #cloud-config
    # Update existing packages
    package_update: true
    # Upgrade all packages
    package_upgrade: true
    # Install specific packages
    packages:
    - vim-enhanced
    - git
    - htop
    - tmux
    - nginx
    - python3
    - epel-release
    - firewalld
    # Reboot after installation if required
    package_reboot_if_required: true
    #cloud-config
    yum_repos:
    epel:
    name: Extra Packages for Enterprise Linux
    baseurl: https://dl.fedoraproject.org/pub/epel/$releasever/Everything/$basearch/
    enabled: true
    gpgcheck: true
    gpgkey: https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-$releasever
    docker-ce:
    name: Docker CE Stable
    baseurl: https://download.docker.com/linux/centos/$releasever/$basearch/stable
    enabled: true
    gpgcheck: true
    gpgkey: https://download.docker.com/linux/centos/gpg
    #cloud-config
    write_files:
    # Write Nginx configuration
    - path: /etc/nginx/conf.d/myapp.conf
    owner: root:root
    permissions: '0644'
    content: |
    server {
    listen 80;
    server_name example.com;
    root /var/www/myapp;
    index index.html;
    }
    # Write system tuning parameters
    - path: /etc/sysctl.d/99-custom.conf
    permissions: '0644'
    content: |
    net.core.somaxconn = 65535
    net.ipv4.tcp_max_syn_backlog = 65535
    vm.swappiness = 10
    # Base64-encoded content
    - path: /etc/ssl/certs/myapp.crt
    encoding: b64
    permissions: '0644'
    content: LS0tLS1CRUdJTi...
    #cloud-config
    # Commands run during the configuration stage (early execution)
    bootcmd:
    - echo "Boot command running" > /tmp/bootcmd.log
    - [ cloud-init-per, once, mymkfs, mkfs, -t, ext4, /dev/vdb ]
    # Commands run during the cloud-final stage
    runcmd:
    # Enable and start services
    - systemctl enable --now nginx
    - systemctl enable --now firewalld
    # Configure the firewall
    - firewall-cmd --permanent --add-service=http
    - firewall-cmd --permanent --add-service=https
    - firewall-cmd --reload
    # Apply sysctl parameters
    - sysctl --system
    # Deploy the application
    - mkdir -p /var/www/myapp
    - echo "<h1>Hello from Cloud-Init</h1>" > /var/www/myapp/index.html
    # Custom script
    - |
    #!/bin/bash
    echo "Instance initialized at $(date)" >> /var/log/cloud-init-custom.log
    hostnamectl set-hostname myserver.example.com

    Here is a production-ready complete user-data example:

    #cloud-config
    hostname: web-server-01
    fqdn: web-server-01.example.com
    manage_etc_hosts: true
    users:
    - default
    - name: ops
    groups: wheel
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
    - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... ops@bastion
    package_update: true
    packages:
    - epel-release
    - vim-enhanced
    - git
    - htop
    - nginx
    - firewalld
    - fail2ban
    write_files:
    - path: /etc/fail2ban/jail.local
    permissions: '0644'
    content: |
    [DEFAULT]
    bantime = 3600
    findtime = 600
    maxretry = 5
    [sshd]
    enabled = true
    runcmd:
    - systemctl enable --now nginx
    - systemctl enable --now firewalld
    - systemctl enable --now fail2ban
    - firewall-cmd --permanent --add-service=http
    - firewall-cmd --permanent --add-service=https
    - firewall-cmd --reload
    - echo "Init complete: $(date)" >> /var/log/cloud-init-custom.log
    final_message: "Cloud-Init configuration complete, took $UPTIME seconds"

    In local environments without a cloud platform, you can use the NoCloud datasource to test Cloud-Init configurations.

    Terminal window
    # Create a working directory
    mkdir -p /tmp/cloud-init-test
    # Create the meta-data file
    cat > /tmp/cloud-init-test/meta-data << 'EOF'
    instance-id: test-001
    local-hostname: test-vm
    EOF
    # Create the user-data file
    cat > /tmp/cloud-init-test/user-data << 'EOF'
    #cloud-config
    users:
    - default
    - name: testuser
    groups: wheel
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    packages:
    - vim-enhanced
    - htop
    runcmd:
    - echo "NoCloud test successful" > /tmp/cloud-init-test-result.txt
    EOF
    Terminal window
    # Install genisoimage
    sudo dnf install -y genisoimage
    # Generate the cloud-init seed ISO
    genisoimage -output /tmp/seed.iso \
    -volid cidata \
    -joliet -rock \
    /tmp/cloud-init-test/user-data \
    /tmp/cloud-init-test/meta-data
    Terminal window
    # Download a cloud image (using AlmaLinux 9 as an example)
    wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2
    # Make a copy for testing
    cp AlmaLinux-9-GenericCloud-latest.x86_64.qcow2 /tmp/test-vm.qcow2
    # Expand the disk
    qemu-img resize /tmp/test-vm.qcow2 20G
    # Create a VM using virt-install and attach the seed ISO
    sudo virt-install \
    --name cloud-init-test \
    --memory 2048 \
    --vcpus 2 \
    --disk /tmp/test-vm.qcow2,format=qcow2 \
    --disk /tmp/seed.iso,device=cdrom \
    --os-variant almalinux9 \
    --network bridge=virbr0 \
    --graphics none \
    --console pty,target_type=serial \
    --import \
    --noautoconsole

    Passing NoCloud Data via Kernel Parameters

    Section titled “Passing NoCloud Data via Kernel Parameters”

    In addition to the ISO method, you can pass data via a local directory:

    Terminal window
    # Place the configuration in the designated directory
    sudo mkdir -p /var/lib/cloud/seed/nocloud
    sudo cp /tmp/cloud-init-test/meta-data /var/lib/cloud/seed/nocloud/
    sudo cp /tmp/cloud-init-test/user-data /var/lib/cloud/seed/nocloud/

    Before applying, it is recommended to validate the user-data syntax:

    Terminal window
    # Install cloud-init (if not already installed)
    sudo dnf install -y cloud-init
    # Validate user-data configuration syntax
    cloud-init schema --config-file /tmp/cloud-init-test/user-data
    # View the merged configuration that cloud-init actually uses
    sudo cloud-init query --all

    During debugging, you may need to re-run Cloud-Init:

    Terminal window
    # Clear cloud-init state (makes it think this is the first boot)
    sudo cloud-init clean
    # Clear state and re-run on next boot
    sudo cloud-init clean --reboot
    # Clear logs only
    sudo cloud-init clean --logs
    Terminal window
    # Main log file (most commonly used)
    sudo cat /var/log/cloud-init.log
    # Output log (shows stdout from runcmd, etc.)
    sudo cat /var/log/cloud-init-output.log
    # View logs for specific stages
    sudo journalctl -u cloud-init-local
    sudo journalctl -u cloud-init
    sudo journalctl -u cloud-config
    sudo journalctl -u cloud-final
    # Watch logs in real time
    sudo tail -f /var/log/cloud-init.log

    user-data not executed

    Terminal window
    # Confirm that cloud-init successfully retrieved user-data
    sudo cloud-init query userdata
    # Check if the datasource was correctly identified
    cat /run/cloud-init/ds-identify.log
    # Check cloud-init status
    cloud-init status --long

    Network configuration issues

    Terminal window
    # Cloud-Init generated network configuration
    cat /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
    # To prevent cloud-init from managing the network
    cat > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg << 'EOF'
    network: {config: disabled}
    EOF

    Package installation failures

    Terminal window
    # Search for errors in the logs
    sudo grep -i "error\|fail\|warn" /var/log/cloud-init.log | tail -20
    # Check dnf logs
    sudo cat /var/log/dnf.log

    In some scenarios, you may need to completely disable Cloud-Init:

    Terminal window
    # Method 1: Create a disable marker file
    sudo touch /etc/cloud/cloud-init.disabled
    # Method 2: Disable all cloud-init services
    sudo systemctl disable cloud-init-local cloud-init cloud-config cloud-final
    # Method 3: Completely uninstall
    sudo dnf remove -y cloud-init

    Customizing Cloud-Init Default Configuration

    Section titled “Customizing Cloud-Init Default Configuration”

    Cloud-Init’s global default configuration is located in /etc/cloud/cloud.cfg. You can override defaults by adding custom files in the /etc/cloud/cloud.cfg.d/ directory:

    Terminal window
    # Customize the default user
    sudo cat > /etc/cloud/cloud.cfg.d/10-custom-user.cfg << 'EOF'
    system_info:
    default_user:
    name: admin
    lock_passwd: true
    gecos: Admin User
    groups: [wheel, adm]
    sudo: ["ALL=(ALL) NOPASSWD:ALL"]
    shell: /bin/bash
    EOF
    # Customize datasource priority
    sudo cat > /etc/cloud/cloud.cfg.d/90-datasource.cfg << 'EOF'
    datasource_list: [ NoCloud, AliYun, Ec2, None ]
    EOF

    Cloud-Init is the cornerstone of cloud environment automation. Once you master its configuration, you can achieve zero-touch instance deployment. When combined with Ansible, Terraform, and other tools covered later, you can build a complete automated operations system.