Skip to content
Search RunEntLinux

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

    Browse this documentation

    Enterprise Linux distributions (AlmaLinux, Rocky Linux) provide pre-built cloud images distributed in qcow2 format. These images are small in size, boot quickly, come with Cloud-Init pre-installed, and are well-suited for rapid VM deployment on KVM/libvirt, OpenStack, or other virtualization platforms.

    Terminal window
    # AlmaLinux 10 / 9 / 8 GenericCloud images (just change the version; the 10 path returns 200)
    wget https://repo.almalinux.org/almalinux/10/cloud/x86_64/images/AlmaLinux-10-GenericCloud-latest.x86_64.qcow2
    wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2
    # AlmaLinux 8 GenericCloud image
    wget https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-latest.x86_64.qcow2
    # Verify download integrity
    wget https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/CHECKSUM
    sha256sum -c CHECKSUM --ignore-missing
    Terminal window
    # Rocky Linux 9 GenericCloud image
    wget https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2
    # Rocky Linux 8 GenericCloud image
    wget https://download.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-GenericCloud.latest.x86_64.qcow2
    # Verify checksums
    wget https://download.rockylinux.org/pub/rocky/9/images/x86_64/CHECKSUM
    sha256sum -c CHECKSUM --ignore-missing
    Terminal window
    # Install the qemu-img tool
    sudo dnf install -y qemu-img
    # View image information
    qemu-img info AlmaLinux-9-GenericCloud-latest.x86_64.qcow2

    Example output:

    image: AlmaLinux-9-GenericCloud-latest.x86_64.qcow2
    file format: qcow2
    virtual size: 10 GiB (10737418240 bytes)
    disk size: 582 MiB
    cluster_size: 65536
    Format specific information:
    compat: 1.1
    compression type: zlib
    lazy refcounts: false

    virt-customize is a tool provided by libguestfs-tools that can modify disk images without booting a virtual machine.

    Terminal window
    sudo dnf install -y libguestfs-tools
    Terminal window
    # Make a copy of the image for customization (preserve the original)
    cp AlmaLinux-9-GenericCloud-latest.x86_64.qcow2 almalinux9-custom.qcow2
    # Set root password
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --root-password password:YourSecurePassword123
    # Install packages
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --install vim-enhanced,htop,tmux,git,wget,curl
    # Enable services
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --run-command 'systemctl enable firewalld'
    # Write files
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --write '/etc/motd:Welcome to Custom AlmaLinux 9 Image'
    # Upload a local file into the image
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --upload /path/to/local/sshd_config:/etc/ssh/sshd_config
    # Set hostname
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --hostname custom-server
    # Set timezone
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --timezone Asia/Shanghai
    # Run custom scripts
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --run-command 'echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/99-custom.conf'
    # Set SELinux relabel (recommended after modifying files)
    sudo virt-customize -a almalinux9-custom.qcow2 \
    --selinux-relabel
    #!/bin/bash
    # customize-image.sh - Batch customize cloud images
    SOURCE_IMAGE="AlmaLinux-9-GenericCloud-latest.x86_64.qcow2"
    CUSTOM_IMAGE="almalinux9-production.qcow2"
    # Copy the original image
    cp "${SOURCE_IMAGE}" "${CUSTOM_IMAGE}"
    # Expand disk to 30GB
    qemu-img resize "${CUSTOM_IMAGE}" 30G
    # Perform customization
    sudo virt-customize -a "${CUSTOM_IMAGE}" \
    --root-password password:TempPassword123 \
    --timezone Asia/Shanghai \
    --install epel-release \
    --install vim-enhanced,htop,tmux,git,wget,curl,net-tools,bind-utils \
    --install firewalld,fail2ban,chrony \
    --run-command 'systemctl enable firewalld' \
    --run-command 'systemctl enable fail2ban' \
    --run-command 'systemctl enable chronyd' \
    --write '/etc/sysctl.d/99-custom.conf:net.core.somaxconn = 65535
    net.ipv4.tcp_max_syn_backlog = 65535
    vm.swappiness = 10' \
    --run-command 'echo "PermitRootLogin prohibit-password" >> /etc/ssh/sshd_config.d/99-custom.conf' \
    --selinux-relabel
    echo "Image customization complete: ${CUSTOM_IMAGE}"
    qemu-img info "${CUSTOM_IMAGE}"

    Before distributing a custom image, you should clean out instance-specific information:

    Terminal window
    # Clean the image (removes SSH keys, machine IDs, logs, etc.)
    sudo virt-sysprep -a almalinux9-custom.qcow2
    # Specify which cleanup operations to perform
    sudo virt-sysprep -a almalinux9-custom.qcow2 \
    --operations defaults,-ssh-userdir
    # List all available cleanup operations
    sudo virt-sysprep --list-operations

    Creating Cloud Instances with virt-install

    Section titled “Creating Cloud Instances with virt-install”
    Terminal window
    # Install KVM and management tools
    sudo dnf install -y @virtualization
    sudo systemctl enable --now libvirtd
    # Verify virtualization support
    virt-host-validate
    Terminal window
    # Create a working directory
    mkdir -p /var/lib/libvirt/cloud-init
    # Create meta-data
    cat > /var/lib/libvirt/cloud-init/meta-data << 'EOF'
    instance-id: vm-001
    local-hostname: web-server-01
    EOF
    # Create user-data
    cat > /var/lib/libvirt/cloud-init/user-data << 'EOF'
    #cloud-config
    hostname: web-server-01
    fqdn: web-server-01.example.com
    users:
    - default
    - name: admin
    groups: wheel
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
    - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... admin@workstation
    package_update: true
    packages:
    - vim-enhanced
    - htop
    - nginx
    runcmd:
    - systemctl enable --now nginx
    EOF
    # Create network-config (optional, for static IP configuration)
    cat > /var/lib/libvirt/cloud-init/network-config << 'EOF'
    version: 2
    ethernets:
    eth0:
    dhcp4: true
    EOF
    Terminal window
    # Install genisoimage
    sudo dnf install -y genisoimage
    # Generate the seed ISO
    genisoimage -output /var/lib/libvirt/cloud-init/seed.iso \
    -volid cidata \
    -joliet -rock \
    /var/lib/libvirt/cloud-init/user-data \
    /var/lib/libvirt/cloud-init/meta-data \
    /var/lib/libvirt/cloud-init/network-config
    Terminal window
    # Prepare the disk image (copy cloud image as the VM disk)
    VM_NAME="web-server-01"
    IMAGE_DIR="/var/lib/libvirt/images"
    sudo cp almalinux9-custom.qcow2 "${IMAGE_DIR}/${VM_NAME}.qcow2"
    # Resize the disk
    sudo qemu-img resize "${IMAGE_DIR}/${VM_NAME}.qcow2" 30G
    # Create the VM with virt-install
    sudo virt-install \
    --name "${VM_NAME}" \
    --memory 2048 \
    --vcpus 2 \
    --disk "${IMAGE_DIR}/${VM_NAME}.qcow2",format=qcow2 \
    --disk /var/lib/libvirt/cloud-init/seed.iso,device=cdrom \
    --os-variant almalinux9 \
    --network bridge=virbr0,model=virtio \
    --graphics none \
    --console pty,target_type=serial \
    --import \
    --noautoconsole
    # Check VM status
    sudo virsh list --all
    # View VM IP address
    sudo virsh domifaddr "${VM_NAME}"
    #!/bin/bash
    # create-vms.sh - Batch create virtual machines
    BASE_IMAGE="/var/lib/libvirt/images/almalinux9-custom.qcow2"
    IMAGE_DIR="/var/lib/libvirt/images"
    CI_DIR="/var/lib/libvirt/cloud-init"
    SSH_KEY="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... admin@workstation"
    VMS=("web-01" "web-02" "db-01")
    MEMORY=2048
    VCPUS=2
    DISK_SIZE="30G"
    for VM_NAME in "${VMS[@]}"; do
    echo "=== Creating VM: ${VM_NAME} ==="
    # Prepare the disk
    sudo cp "${BASE_IMAGE}" "${IMAGE_DIR}/${VM_NAME}.qcow2"
    sudo qemu-img resize "${IMAGE_DIR}/${VM_NAME}.qcow2" "${DISK_SIZE}"
    # Generate cloud-init configuration
    WORK_DIR="${CI_DIR}/${VM_NAME}"
    mkdir -p "${WORK_DIR}"
    cat > "${WORK_DIR}/meta-data" << METAEOF
    instance-id: ${VM_NAME}
    local-hostname: ${VM_NAME}
    METAEOF
    cat > "${WORK_DIR}/user-data" << USEREOF
    #cloud-config
    hostname: ${VM_NAME}
    users:
    - default
    - name: admin
    groups: wheel
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
    - ${SSH_KEY}
    package_update: true
    packages:
    - vim-enhanced
    - htop
    USEREOF
    # Generate ISO
    genisoimage -output "${WORK_DIR}/seed.iso" \
    -volid cidata -joliet -rock \
    "${WORK_DIR}/user-data" \
    "${WORK_DIR}/meta-data"
    # Create the VM
    sudo virt-install \
    --name "${VM_NAME}" \
    --memory ${MEMORY} \
    --vcpus ${VCPUS} \
    --disk "${IMAGE_DIR}/${VM_NAME}.qcow2",format=qcow2 \
    --disk "${WORK_DIR}/seed.iso",device=cdrom \
    --os-variant almalinux9 \
    --network bridge=virbr0,model=virtio \
    --graphics none \
    --console pty,target_type=serial \
    --import \
    --noautoconsole
    echo "${VM_NAME} created successfully"
    done
    echo ""
    echo "=== All VM Status ==="
    sudo virsh list --all
    Terminal window
    # View current size
    qemu-img info /var/lib/libvirt/images/web-server-01.qcow2
    # Expand to 50GB (VM must be shut down)
    sudo virsh shutdown web-server-01
    sudo qemu-img resize /var/lib/libvirt/images/web-server-01.qcow2 50G
    sudo virsh start web-server-01

    After the VM boots, you still need to expand the partition and filesystem:

    Terminal window
    # Execute the following after logging into the VM
    # View disk layout
    lsblk
    # If using LVM (typical for cloud images)
    sudo growpart /dev/vda 4 # Expand the partition (partition number may vary)
    sudo pvresize /dev/vda4 # Expand the PV
    sudo lvextend -l +100%FREE /dev/mapper/almalinux-root # Expand the LV
    sudo xfs_growfs / # Expand the XFS filesystem
    # Or: sudo resize2fs /dev/mapper/almalinux-root # For ext4
    # If not using LVM (simple partitioning)
    sudo growpart /dev/vda 1
    sudo xfs_growfs /
    Terminal window
    # Shut down the VM
    sudo virsh shutdown web-server-01
    # Compress the qcow2 image (remove sparse space)
    sudo qemu-img convert -O qcow2 -c \
    /var/lib/libvirt/images/web-server-01.qcow2 \
    /var/lib/libvirt/images/web-server-01-compressed.qcow2
    # Use virt-sparsify for more thorough compression
    sudo virt-sparsify --in-place /var/lib/libvirt/images/web-server-01.qcow2
    Terminal window
    # qcow2 to raw format
    qemu-img convert -f qcow2 -O raw image.qcow2 image.raw
    # raw to qcow2 format
    qemu-img convert -f raw -O qcow2 image.raw image.qcow2
    # qcow2 to vmdk (VMware format)
    qemu-img convert -f qcow2 -O vmdk image.qcow2 image.vmdk
    # qcow2 to vdi (VirtualBox format)
    qemu-img convert -f qcow2 -O vdi image.qcow2 image.vdi
    Terminal window
    # List all virtual machines
    sudo virsh list --all
    # Start/Shutdown/Reboot
    sudo virsh start web-server-01
    sudo virsh shutdown web-server-01
    sudo virsh reboot web-server-01
    # Force shutdown
    sudo virsh destroy web-server-01
    # Connect to the VM console
    sudo virsh console web-server-01
    # Exit console: Ctrl+]
    # View VM information
    sudo virsh dominfo web-server-01
    sudo virsh domifaddr web-server-01
    # Create a snapshot
    sudo virsh snapshot-create-as web-server-01 \
    --name "before-upgrade" \
    --description "Snapshot before upgrade"
    # List snapshots
    sudo virsh snapshot-list web-server-01
    # Revert to a snapshot
    sudo virsh snapshot-revert web-server-01 before-upgrade
    # Delete a VM (keep disks)
    sudo virsh undefine web-server-01
    # Delete a VM (remove disks as well)
    sudo virsh undefine web-server-01 --remove-all-storage

    Using official cloud images with Cloud-Init and virt-install allows you to rapidly create standardized VM instances in a local KVM environment. This experience is very similar to creating ECS instances on a public cloud, making it an ideal solution for building private clouds and development/testing environments.