Packer builds machine images — cloud images, VM templates, Docker images — from a template. Instead of configuring each new server after it boots, you bake software and settings into an image once, then launch identical servers from it. The approach is called golden images or immutable infrastructure.

When images make sense

Bake an image when:

  • you launch many servers with the same setup (autoscaling, CI runners, test environments);
  • servers must be ready quickly, without minutes of package installs at boot;
  • you want each server version to be identical and testable before release.

Configure at boot (cloud-init + Ansible) when:

  • you run a handful of long-lived servers;
  • your platform doesn’t support custom images.

Most VPS users fall into the second group; teams with autoscaling or private virtualization (Proxmox, VMware, OpenStack) benefit from Packer.

Concepts

  • Source/builder — where the image is built: a cloud API (AWS, GCP, Azure, Hetzner, DigitalOcean…), a hypervisor (QEMU/KVM, Proxmox, VMware, VirtualBox) or Docker.
  • Provisioners — what runs inside the temporary machine: shell scripts, Ansible, file uploads.
  • Post-processors — what happens to the result: compress, upload, tag, create a manifest.

Packer starts a temporary VM from a base image, runs the provisioners, shuts it down and saves a new image.

Install

# after adding the HashiCorp apt repository (see the Terraform guide)
sudo apt install packer
packer version

Example: Ubuntu image with QEMU/KVM

ubuntu.pkr.hcl:

packer {
  required_plugins {
    qemu = {
      source  = "github.com/hashicorp/qemu"
      version = ">= 1.1.0"
    }
    ansible = {
      source  = "github.com/hashicorp/ansible"
      version = ">= 1.1.0"
    }
  }
}

variable "version" {
  type    = string
  default = "2026.11.04"
}

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        = "20G"
  format           = "qcow2"
  accelerator      = "kvm"
  ssh_username     = "packer"
  ssh_private_key_file = "~/.ssh/packer"
  cd_files         = ["./cloud-init/meta-data", "./cloud-init/user-data"]
  cd_label         = "cidata"
  output_directory = "output-${var.version}"
  vm_name          = "ubuntu-web-${var.version}.qcow2"
  headless         = true
}

build {
  sources = ["source.qemu.ubuntu"]

  provisioner "shell" {
    inline = ["cloud-init status --wait", "sudo apt-get update", "sudo apt-get -y upgrade"]
  }

  provisioner "ansible" {
    playbook_file = "./ansible/base.yml"
  }

  provisioner "shell" {
    inline = ["sudo cloud-init clean --logs", "sudo truncate -s 0 /etc/machine-id"]
  }
}

The cloud-init seed (user-data) creates the packer user with the build SSH key. The final step resets cloud-init and the machine ID, so every server launched from the image initialises as a new machine.

packer init .
packer validate .
packer build -var version=2026.11.04 .

Cloud builders look similar: replace the qemu source with the provider’s plugin, which launches a temporary instance via the API and saves a snapshot/image there. Building with QEMU requires a host with KVM — nested virtualization inside a VPS is usually not available, so build on a physical machine, a dedicated server or in the target cloud.

Version and test images

  • Tag images with a version and the Git commit of the template.
  • Test new images automatically (launch, run checks such as Goss or InSpec, destroy) before using them in production.
  • Rebuild regularly (e.g. weekly) to include security updates, and retire old images.

Packer + Terraform

A typical flow: Packer builds web-2026.11.04, Terraform launches servers referencing the latest image, and rolling replacement updates the fleet. Configuration drift disappears because servers are replaced rather than modified. See Terraform basics and infrastructure as code.

Alternatives

  • cloud-init + Ansible at boot — no image pipeline, slower startup; see cloud-init.
  • Docker images — for applications, containers provide the same “bake once, run anywhere” benefit with much less overhead.
  • Proxmox/VMware templates made by hand — quick, but not reproducible.

For most VPS-based projects, containers handle the application layer and Ansible handles the host — Packer becomes worthwhile when you’re launching servers frequently.

  • Packer
  • Infrastructure as Code
  • DevOps