Virtual machine scripts
Which VM scripts exist, what each one creates, and how they differ from container scripts.
A VM script runs on the host, asks a few questions, downloads a disk image or an
installer ISO, and creates a virtual machine from it. On Proxmox VE that means
qm; on Incus it means incus init --vm.
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh)"Run it in the Proxmox VE host shell as root. The script needs a terminal of at
least 60x18 for its dialogs, and a host with /dev/kvm.
- Anatomy of a VM script walks through a real script line by line.
- The VM engine documents the functions behind it.
How VM scripts differ from container scripts
Container script (ct/) | VM script (vm/) | |
|---|---|---|
| Files per app | ct/<app>.sh on the host plus install/<app>-install.sh in the guest | One file, vm/<name>-vm.sh. There is no install script. |
| Engine loaded | core/build.func | pve/vm-core.func (Proxmox VE) or incus/vm-core.func (Incus) |
| What is created | An LXC container from a template | A QEMU/KVM guest from a vendor cloud image, an appliance image, or an installer ISO |
| How software gets in | The install script runs inside the running container | The image is modified offline with virt-customize before import, a first-boot unit is staged into it, or the user finishes an installer in the console |
| Settings | var_* variables, defaults files, the shared wizard | The script's own default_settings and advanced_settings functions; unattended runs use VM_* variables |
| Update | Run the ct/ script again inside the container | No update function. The guest is updated with its own tools. |
| One script for both platforms | Yes, core/build.func picks the backend | No. Proxmox VE and Incus each have their own vm/ directory. |
| Failure after creation | The container is removed | Depends on the script: see failure handling |
Proxmox VE scripts
These are the scripts in ProxmoxVE/vm/.
Architecture. The engine's arch_check stops every script on a host that is
not amd64 unless the script sets var_arm64="yes". Two scripts do. Several others
already build an arm64 image URL with vm_arch_resolve, but still stop at that
check; they are listed as amd64.
Cloud-Init. "Optional" means the script asks. "Required" means the image has no other way in, so the script only asks for the credentials. "No" means the VM is never given a Cloud-Init drive.
| Script | What it creates | Image source | Architecture | Cloud-Init |
|---|---|---|---|---|
allstarlink-vm.sh | Debian 13 or 12 with AllStarLink (asl3) installed into the image, optionally Allmon3 | Debian nocloud qcow2, cloud.debian.org | amd64 | No |
almalinux-vm.sh | AlmaLinux 10, 9 or 8, with a TPM 2.0 state disk | GenericCloud qcow2, repo.almalinux.org | amd64 | Required |
alpine-vm.sh | Alpine (latest stable), with a TPM 2.0 state disk | generic_alpine-*-x86_64-uefi-cloudinit qcow2, dl-cdn.alpinelinux.org | amd64 | Required |
archlinux-vm.sh | Empty disk plus the Arch Linux installer ISO | archlinux-x86_64.iso, geo.mirror.pkgbuild.com | amd64 | No |
cachyos-vm.sh | Empty disk plus the CachyOS desktop installer ISO | SourceForge, cachyos-arch project | amd64 | No |
debian-vm.sh | Debian 13, 12 or 11 | genericcloud qcow2 with Cloud-Init, nocloud without, cloud.debian.org | amd64 | Optional |
docker-vm.sh | Debian 13/12 or Ubuntu 26.04/24.04/22.04 with Docker installed into the image | Debian generic or nocloud qcow2; Ubuntu server-cloudimg | amd64 and arm64 | Optional on Debian, always on for Ubuntu |
fedora-vm.sh | Newest Fedora release, with a TPM 2.0 state disk | Fedora-Cloud-Base-Generic qcow2, dl.fedoraproject.org | amd64 | Required |
haos-vm.sh | Home Assistant OS, stable, beta or dev | haos_ova-*.qcow2.xz, GitHub releases (dev: os-artifacts.home-assistant.io) | amd64 | No |
mikrotik-routeros.sh | MikroTik RouterOS CHR, no EFI | chr-*.img.zip, download.mikrotik.com | amd64 | No |
nextcloud-vm.sh | Empty disk plus the TurnKey Nextcloud installer ISO, SeaBIOS | mirror.turnkeylinux.org | amd64 | No |
openwrt-vm.sh | OpenWrt with a LAN and a WAN NIC, configured through qm sendkey | generic-ext4-combined.img.gz, downloads.openwrt.org | amd64 | No |
opnsense-vm.sh | A FreeBSD VM that is turned into OPNsense by the upstream bootstrap, typed in through qm sendkey | FreeBSD qcow2.xz VM image, download.freebsd.org | amd64 | No |
owncloud-vm.sh | Empty disk plus the TurnKey ownCloud installer ISO, SeaBIOS | mirror.turnkeylinux.org | amd64 | No |
pimox-haos-vm.sh | Home Assistant OS for ARM hosts, machine type virt | haos_generic-aarch64-*.qcow2.xz, GitHub releases | arm64 only | No |
truenas-vm.sh | Empty SATA disk plus a TrueNAS installer ISO; can pass through onboard disks | download.truenas.com | amd64 | No |
ubuntu-vm.sh | Ubuntu 26.04, 24.04 or 22.04 | server-cloudimg image, cloud-images.ubuntu.com | amd64 | Optional |
umbrel-os-vm.sh | Empty disk plus the umbrelOS installer ISO | download.umbrel.com | amd64 | No |
unifi-os-server-vm.sh | Debian 13 or Ubuntu 24.04 with the UniFi OS Server installer staged to run on first boot | Debian generic qcow2 or Ubuntu server-cloudimg | amd64 | Required |
zimaos-vm.sh | Empty disk plus the ZimaOS installer ISO | GitHub releases, IceWhaleTech/ZimaOS | amd64 | No |
Scripts that attach an installer ISO leave the installation to you: open the VM
console, install onto the empty disk, then detach the ISO with
qm set <vmid> --ide2 none. The boot order already puts the disk before the ISO
(scsi0;ide2, on TrueNAS sata0;ide2), so the disk wins as soon as something is
on it.
Scripts in testing (DevScripts)
DevScripts/vm/
holds VM scripts that are still being tested. They use the same engine.
| Script | What it creates | Image source | Architecture | Cloud-Init |
|---|---|---|---|---|
blissos-vm.sh | Empty disk plus the BlissOS (Android x86) installer ISO | SourceForge, blissos-x86 project | amd64 | No |
k3s-vm.sh | Debian or Ubuntu with K3s and Helm installed into the image | Debian or Ubuntu cloud image | amd64 | Optional on Debian, required on Ubuntu |
netbird-server.sh | Debian or Ubuntu with Docker; NetBird is set up on first boot | Debian or Ubuntu cloud image | amd64 | Optional on Debian, required on Ubuntu |
nextcloud-aio-vm.sh | Debian or Ubuntu with Docker; Nextcloud AIO is set up on first boot | Debian or Ubuntu cloud image | amd64 | Optional on Debian, required on Ubuntu |
owncloud-ocis-vm.sh | Debian or Ubuntu with Docker; ownCloud Infinite Scale is set up on first boot | Debian or Ubuntu cloud image | amd64 | Optional on Debian, required on Ubuntu |
waydroid-vm.sh | Ubuntu 24.04 or Debian 13 with Waydroid | Ubuntu noble-server-cloudimg or Debian generic qcow2 | amd64 | Required |
owncloud-vm.sh, pimox-haos-vm.sh and umbrel-os-vm.sh exist in both
repositories.
Incus scripts
The Incus repository
has its own vm/ directory. Those scripts load incus/vm-core.func and end in
one call, incus_vm_create.
| Kind | Scripts | Image |
|---|---|---|
Launched from the images: remote | almalinux-vm.sh, alpine-vm.sh, alt-vm.sh, archlinux-vm.sh, debian-vm.sh, fedora-vm.sh, freebsd-vm.sh, gentoo-vm.sh, nixos-vm.sh, openeuler-vm.sh, opensuse-vm.sh, openwrt-vm.sh, rockylinux-vm.sh, ubuntu-vm.sh | var_image, for example images:debian/13 |
| Downloaded and imported as an Incus image | docker-vm.sh, haos-vm.sh, mikrotik-routeros-vm.sh, umbrel-os-vm.sh | A vendor disk image, fetched with vm_fetch_image |
There is no Incus version of the installer-ISO scripts or of the two scripts that
type into the console with qm sendkey. The
engine page
lists what exists on which platform.