Docker & DevOps

Build reproducible VM images with Packer

Use Packer to build VM templates automatically: write an HCL template, pick a builder (QEMU, Proxmox or a cloud), provision with shell or Ansible, and produce versioned golden images in CI.

IntermediateVirtualization and cloud2 min read

Hand-built VM templates drift and nobody remembers exactly how they were made. Packer builds VM images from a template file: it boots a temporary VM, installs and configures software, then saves the result as an image. This guide covers the basics.

Before you start

  • Packer installed from the official HashiCorp downloads (check its current licence terms).
  • A target platform: local QEMU/KVM, Proxmox VE, or a cloud provider with a Packer plugin.
  • Background: golden image best practices.

How Packer works

  1. Source/builder: creates a temporary VM from an ISO or base image.
  2. Provisioners: run shell scripts, Ansible or file uploads inside it.
  3. Post-processors: compress, upload or register the final image.

A QEMU example from a cloud image

ubuntu.pkr.hcl:

packer {
  required_plugins {
    qemu = {
      source  = "github.com/hashicorp/qemu"
      version = "~> 1"
    }
  }
}

source "qemu" "ubuntu" {
  iso_url          = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
  iso_checksum     = "file:https://cloud-images.ubuntu.com/noble/current/SHA256SUMS"
  disk_image       = true
  disk_size        = "10G"
  format           = "qcow2"
  accelerator      = "kvm"
  headless         = true
  ssh_username     = "packer"
  ssh_private_key_file = "~/.ssh/packer"
  cd_files         = ["./cloud-init/*"]
  cd_label         = "cidata"
  shutdown_command = "sudo shutdown -P now"
  output_directory = "output-ubuntu"
}

build {
  sources = ["source.qemu.ubuntu"]
  provisioner "shell" {
    inline = [
      "sudo apt-get update",
      "sudo apt-get -y upgrade",
      "sudo apt-get -y install qemu-guest-agent",
      "sudo cloud-init clean --logs"
    ]
  }
}

The cloud-init folder contains user-data and meta-data that create the packer user with your public key; see cloud-init user-data.

Build

packer init .
packer validate .
packer build .

The resulting qcow2 can be used as a template on KVM or imported into Proxmox.

Other builders

  • Proxmox: community/official plugins build templates directly in Proxmox VE through its API.
  • Clouds: build provider images (snapshots) for many public clouds.
  • Ansible provisioner: reuse your server playbooks; see Ansible.

Run it in CI

  • Rebuild images on a schedule (e.g. monthly) to include security updates.
  • Version images with a date tag and keep the previous one for rollback.
  • Test new images automatically by booting them and running checks.

Troubleshooting

  • Waiting for SSH forever: cloud-init seed not attached or wrong key; check the console with headless = false.
  • KVM not available: CI runner lacks /dev/kvm; use a runner with virtualization or nested virtualization.
  • Clones share machine IDs: clean /etc/machine-id and SSH host keys at the end of the build.

Frequently asked questions

Packer or Docker?

Docker builds container images; Packer builds VM and cloud machine images.

Packer or manual templates?

Packer makes images reproducible and reviewable as code.

Can Packer build Windows images?

Yes, using WinRM and an unattended install; see Windows Sysprep templates.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.