Guide /

Apache CloudStack Ubuntu/KVM Build Guide for x86_64 & ARM64

Apache CloudStack Ubuntu/KVM Build Guide for x86_64 & ARM64

Build a fully functional private IaaS cloud with Apache CloudStack v4.22 on Ubuntu using KVM — works on both x86_64 and ARM64 hardware.

#self-hosting#infrastructure#cloud#cloudstack#kvm#ubuntu

This guide covers the installation of Apache CloudStack on Ubuntu using KVM, for both x86_64 and ARM64 architectures — from Intel NUCs and Dell rack servers to Raspberry Pi 4/5 and Ampere Ultra servers.

Version: This guide targets Apache CloudStack v4.22 on Ubuntu 24.04 LTS. For newer releases, refer to the official install guide and the KVM host docs.

New to CloudStack? Watch the intro first:

One-liner installer: New users can try the console-based installer at github.com/apache/cloudstack-installer

Getting Started

Install Ubuntu 24.04 LTS on your x86_64 or ARM64 host.

Ensure Intel VT-X or AMD-V is available and enabled on x86_64 hosts — 16 GB RAM or more recommended.
ARM64 hosts (Raspberry Pi 4/5): Ensure 64-bit mode and GIC are enabled in /boot/firmware/config.txt:
[all]
arm_64bit=1
enable_gic=1

# NVME speed
dtparam=nvme
dtparam=pciex1_1=gen3
dtparam=pciex1_gen=3
usb_max_current_enable=1
max_usb_current=1

Ensure the universe repository is enabled in /etc/apt/sources.list, then install basic packages:

apt-get install openntpd openssh-server sudo vim htop tar

Verify KVM is available:

apt-get install cpu-checker
kvm-ok
# INFO: /dev/kvm exists
# KVM acceleration can be used

Optional: install CPU microcode for Intel hosts:

apt-get install intel-microcode

Allow root SSH access — set PermitRootLogin yes in /etc/ssh/sshd_config then restart:

systemctl restart ssh
passwd root

Network Setup

Set up host networking using Linux bridges to handle CloudStack’s management, public, guest, and storage traffic. A single bridge cloudbr0 can handle all traffic types on the same physical network.

apt-get install bridge-utils

This guide assumes a 192.168.1.0/24 network.

Ubuntu Netplan Config

Create /etc/netplan/01-netcfg.yaml (comment out the default 50-cloud-init.yaml first), replacing eno1 with your actual NIC name and 192.168.1.10 with your host’s IP:

network:
  version: 2
  renderer: networkd
  ethernets:
    eno1:
      dhcp4: false
      dhcp6: false
      optional: true
  bridges:
    cloudbr0:
      addresses: [192.168.1.10/24]
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses: [1.1.1.1, 8.8.8.8]
      interfaces: [eno1]
      dhcp4: false
      dhcp6: false
      parameters:
        stp: false
        forward-delay: 0
VXLAN isolation: If using VXLAN-based traffic isolation, increase the MTU of physical NICs by 50 bytes (VXLAN header overhead). Add mtu: 1550 under your ethernets entries.

Apply and reboot:

netplan generate
netplan apply

Repo Setup

Run this on all hosts where CloudStack packages will be installed:

mkdir -p /etc/apt/keyrings
wget -O- http://packages.shapeblue.com/release.asc | gpg --dearmor | sudo tee /etc/apt/keyrings/cloudstack.gpg > /dev/null

echo deb [signed-by=/etc/apt/keyrings/cloudstack.gpg] http://packages.shapeblue.com/cloudstack/upstream/debian/4.22 / > /etc/apt/sources.list.d/cloudstack.list
apt-get update -y

Management Server Setup

apt-get update -y
apt-get install cloudstack-management mysql-server

# Optional: install usage server
apt-get install cloudstack-usage

Database Setup

Configure InnoDB settings in /etc/mysql/mysql.conf.d/mysqld.cnf:

[mysqld]
server_id = 1
sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE,NO_ZERO_IN_DATE,NO_ENGINE_SUBSTITUTION"
innodb_rollback_on_timeout=1
innodb_lock_wait_timeout=600
max_connections=1000
log-bin=mysql-bin
binlog-format = 'ROW'

Restart MySQL and deploy the database:

systemctl restart mysql
cloudstack-setup-databases cloud:cloud@localhost --deploy-as=root: -i 192.168.1.10
The -i flag overrides the management server IP if auto-detection is wrong. The --deploy-as=root: format has root as user with a blank password — adjust if your root password is set.

Storage Setup

apt-get install nfs-kernel-server quota

Create NFS exports:

echo "/export  *(rw,async,no_root_squash,no_subtree_check)" > /etc/exports
mkdir -p /export/primary /export/secondary
exportfs -a
/export can point to a separate disk or external storage — format and mount it via /etc/fstab before running the above.

Configure and restart NFS:

sed -i -e 's/^RPCMOUNTDOPTS="--manage-gids"$/RPCMOUNTDOPTS="-p 892 --manage-gids"/g' /etc/default/nfs-kernel-server
sed -i -e 's/^STATDOPTS=$/STATDOPTS="--port 662 --outgoing-port 2020"/g' /etc/default/nfs-common
echo "NEED_STATD=yes" >> /etc/default/nfs-common
sed -i -e 's/^RPCRQUOTADOPTS=$/RPCRQUOTADOPTS="-p 875"/g' /etc/default/quota
service nfs-kernel-server restart
ARM64 hosts: Seed an ARM64 systemvm template manually from the management server before launching a zone. For CloudStack 4.22:
wget http://download.cloudstack.org/systemvm/4.22/systemvmtemplate-4.22.0-aarch64-kvm.qcow2.bz2
/usr/share/cloudstack-common/scripts/storage/secondary/cloud-install-sys-tmplt \
    -m /export/secondary -f systemvmtemplate-4.22.0-aarch64-kvm.qcow2.bz2 -h kvm \
    -o localhost -r cloud -d cloud

Future CloudStack releases will automate ARM64 systemvm template installation.


KVM Host Setup

Configure the CloudStack repo on your KVM host if it's separate from the management server.
apt-get install qemu-kvm cloudstack-agent

Enable VNC for console proxy:

sed -i -e 's/\#vnc_listen.*$/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf

On Ubuntu 22.04/24.04, add LIBVIRTD_ARGS="--listen" to /etc/default/libvirtd:

echo LIBVIRTD_ARGS=\"--listen\" >> /etc/default/libvirtd

Mask the socket-based listeners (required for CloudStack compatibility):

systemctl mask libvirtd.socket libvirtd-ro.socket libvirtd-admin.socket libvirtd-tls.socket libvirtd-tcp.socket
systemctl restart libvirtd

Set legacy connection mode in /etc/libvirt/libvirt.conf (important on Ubuntu 24.04):

remote_mode="legacy"

Configure TCP listener in /etc/libvirt/libvirtd.conf:

echo 'listen_tls=0' >> /etc/libvirt/libvirtd.conf
echo 'listen_tcp=1' >> /etc/libvirt/libvirtd.conf
echo 'tcp_port = "16509"' >> /etc/libvirt/libvirtd.conf
echo 'mdns_adv = 0' >> /etc/libvirt/libvirtd.conf
echo 'auth_tcp = "none"' >> /etc/libvirt/libvirtd.conf
systemctl restart libvirtd
This plain TCP config is only for initial setup. CloudStack will automatically reconfigure the host to use mTLS once added to a zone.
ARM64 required settings — add to /etc/cloudstack/agent/agent.properties:
guest.cpu.arch=aarch64
guest.cpu.mode=host-passthrough
host.cpu.manual.speed.mhz=1500
host.reserved.mem.mb=800

host.cpu.manual.speed.mhz is required because Linux on ARM64 cannot always report the correct CPU speed. Adjust host.reserved.mem.mb as needed (default is 1024 MB).

Hosts running Docker or other services: Add to /etc/sysctl.conf and run sysctl -p:
net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-call-iptables = 0
Non-standard vendor / virtual environment: Set a unique host UUID for libvirt if needed:
apt-get install uuid
UUID=$(uuid)
echo host_uuid = \"$UUID\" >> /etc/libvirt/libvirtd.conf
systemctl restart libvirtd

Firewall Setup

If ufw is active (ufw status), open the required ports:

ufw allow mysql
ufw allow proto tcp from any to any port 22
ufw allow proto tcp from any to any port 1798
ufw allow proto tcp from any to any port 16509
ufw allow proto tcp from any to any port 16514
ufw allow proto tcp from any to any port 5900:6100
ufw allow proto tcp from any to any port 49152:49216

Or configure iptables for your management network (192.168.1.0/24):

NETWORK=192.168.1.0/24
iptables -A INPUT -s $NETWORK -m state --state NEW -p udp --dport 111 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 111 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 2049 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 32803 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p udp --dport 32769 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 892 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 875 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 662 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 8250 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 8080 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 9090 -j ACCEPT
iptables -A INPUT -s $NETWORK -m state --state NEW -p tcp --dport 16514 -j ACCEPT
apt-get install iptables-persistent

Disable AppArmor for libvirt:

ln -s /etc/apparmor.d/usr.sbin.libvirtd /etc/apparmor.d/disable/
ln -s /etc/apparmor.d/usr.lib.libvirt.virt-aa-helper /etc/apparmor.d/disable/
apparmor_parser -R /etc/apparmor.d/usr.sbin.libvirtd
apparmor_parser -R /etc/apparmor.d/usr.lib.libvirt.virt-aa-helper

Start Control Plane

cloudstack-setup-management
systemctl status cloudstack-management
tail -f /var/log/cloudstack/management/management-server.log

Once the management server is up, log in at http://192.168.1.10:8080/client with username admin and password password.

CloudMonkey CLI: Use CloudStack's official CLI cmk to interact with the control plane. See the Getting Started guide.

Deploy Your Private Cloud

The following sets up an advanced zone (no security groups) in a typical 192.168.1.0/24 network.

Deploy DataCenter — Step by Step

1. Create Zone

Go to Infrastructure > Zone, click Add Zone, select Advanced Zone:

Name         - any name
Public DNS 1 - 8.8.8.8
Internal DNS - 192.168.1.1
Hypervisor   - KVM

2. Setup Network

Use the default VLAN isolation on a single physical NIC (carries all traffic types). If iproute2 is installed and MTUs are configured, VXLAN can also be used.

Public traffic:

Gateway  - 192.168.1.1
Netmask  - 255.255.255.0
VLAN/VNI - (leave blank for untagged)
Start IP - 192.168.1.20
End IP   - 192.168.1.50

3. Pod Configuration

Name                           - any name
Gateway                        - 192.168.1.1
Start/end reserved system IPs  - 192.168.1.51–192.168.1.80

4. Guest Traffic

VLAN/VNI range: 700-900

5. Create Cluster

Name       - any name
Hypervisor - KVM

6. Add First Host

Hostname - 192.168.1.10
Username - root
Password - <root password or leave blank for public key>
The recommended approach is SSH public-key access. Add the management server's key (/var/cloudstack/management/.ssh/id_rsa.pub) to the KVM host's /root/.ssh/authorized_keys.

7. Add Primary Storage

Name     - any name
Scope    - zone-wide
Protocol - NFS
Server   - 192.168.1.10
Path     - /export/primary

8. Add Secondary Storage

Provider - NFS
Name     - any name
Server   - 192.168.1.10
Path     - /export/secondary

9. Launch Zone

Click Launch Zone — CloudStack will:

  • Create the zone and physical networks
  • Add traffic types, enable network providers and virtual router elements
  • Create the pod, configure public and guest traffic
  • Add the host, mount primary storage
  • Set up secondary storage and complete zone creation

10. Enable Zone

Confirm and enable the zone. Wait for System VMs to appear under Infrastructure → System VMs, then your IaaS cloud is ready.


Register Templates

Register cloud-init enabled guest OS images to deploy VMs:

Distrox86_64 TemplateARM64 Template
Ubuntu 24.04x86_64arm64
Ubuntu 22.04x86_64arm64
Debian 12x86_64arm64
AlmaLinux 9x86_64arm64
OpenSUSE 15x86_64arm64
Before deploying instances, register your SSH public key under Compute → SSH Key Pairs so cloud-init injects it automatically.

Add Networks

Once System VMs are up and running, you may create isolated network or VPC for your account for the built-in admin account or any new user account you may created that can be later used to deploy instances.

For simplicity, you may also create a shared network in the unused IP address space of your home/private network. For example, create shared network using API/CLI or UI under Network > Guest Networks > Add Network > Shared using:

Physical Network - cloudbr0
VLAN/VNI         - untagged
IPv4 Gateway     - 192.168.1.1
IPv4 Netmask     - 255.255.255.0
IPv4 Start IP    - 192.168.1.81
IPv4 End IP      - 192.168.1.100
DNS1             - 192.168.1.1
DNS2             - 1.1.1.1

Deploy an Instance

Go to Compute → Instances → Add Instance (or use the top-nav Create button):

Zone             - select your zone
Arch             - x86_64 or ARM64
Image            - your registered template
Compute Offering - select or create an offering
Networks         - one or more guest networks
SSH Key Pairs    - select your key pair
Name             - (optional)

Hit Launch Instance — your instance will be created!

Integrations

NameLink
CloudMonkey CLIgithub.com/apache/cloudstack-cloudmonkey
Terraform Providergithub.com/apache/cloudstack-terraform-provider
Ansible Collectiongithub.com/ngine-io/ansible-collection-cloudstack
Kubernetes Providergithub.com/apache/cloudstack-kubernetes-provider
Cluster API Providergithub.com/kubernetes-sigs/cluster-api-provider-cloudstack
Go SDKgithub.com/apache/cloudstack-go
Benchmarking Toolgithub.com/apache/cloudstack-csbench

More integrations at cloudstack.apache.org/integrations.


Where to Go Next

Stuck on something?

Further reading:

Comments

// published March 1, 2026