Build One Template, Multiple Architectures: Packer’s Multi‑Arch Builder Simplified
If you need to produce VM or cloud images for both amd64 and arm64, Packer’s new multi‑arch syntax lets you do it in one run. This post shows the exact HCL2 syntax, a working example, and what to watch out for.
26 Apr 2026, 00:49 UTC

The Real Problem
When you need images for both amd64 and arm64—for example, an Ubuntu 20.04 AMI that runs on both x86‑64 and ARM‑based EC2 instances—you’re forced to duplicate almost every line of a Packer template. You create two separate templates, each with its own builders block, and you copy‑paste provisioners, post‑processors, and variables. That duplication is easy to error‑prone and hard to keep in sync.
What Packer 1.8+ Gives You Instead
Starting with version 1.8, Packer supports a platforms array inside a single builds block. The builder automatically spawns a parallel build for each architecture you list. The result is a single, DRY template that produces a separate image per architecture, each named with the architecture suffix.
Key Syntax Elements
builds– top‑level block that can contain multiplebuilddefinitions.builder– the type of builder (e.g.,amazon-ebs,virtualbox-iso).platforms– array of strings like"amd64"or"arm64"that Packer will iterate over.post-processor– applied to each architecture build; you can target specific architectures withplatformsinside the post‑processor too.
Concrete Example: Ubuntu 20.04 AMIs for AWS
Below is a minimal HCL2 template that builds two AMIs—one for amd64 and one for arm64—using the same source ISO, provisioners, and post‑processor. Replace <YOUR-AWS-REGION> and other placeholders with your actual values.
variable "aws_region" {
default = "<YOUR-AWS-REGION>"
}
variable "source_iso" {
default = "ubuntu-20.04.5-live-server-amd64.iso"
}
build {
name = "ubuntu-20.04"
builder {
type = "amazon-ebs"
ami_name = "ubuntu-20.04-${var.platform}"
instance_type = "t2.micro"
source_ami_filter {
filters = {
name = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"]
}
region = var.aws_region
platform = var.platform
}
provisioner "file" {
source = "setup.sh"
destination = "/tmp/setup.sh"
}
provisioner "shell" {
inline = ["chmod +x /tmp/setup.sh", "/tmp/setup.sh"]
}
post-processor "manifest" {
output = "manifest-${var.platform}.json"
}
}
# The multi‑arch loop
locals {
archs = ["amd64", "arm64"]
}
# This block expands into two builds, one per architecture
build {
for_each = local.archs
name = "ubuntu-20.04-${each.key}"
sources = ["build.ubuntu-20.04"]
variables = {
platform = each.key
}
}
Run it with:
packer build -debug template.pkr.hcl
During execution you’ll see two parallel build logs, one for amd64 and one for arm64. After completion, the output directory will contain:
ubuntu-20.04-amd64.json– the manifest for the x86 imageubuntu-20.04-arm64.json– the manifest for the ARM image- Any other post‑processor artifacts, each suffixed with the architecture.
Trade‑offs & Practical Limits
- Builder Support – Not every builder can target every architecture. For example,
virtualbox-isoonly supportsamd64, so attempting to buildarm64will error during thebuilderphase. Always check the builder documentation for supported platforms. - Resource Consumption – Parallel builds run concurrently, which can double or triple CPU and disk I/O on a CI runner. If your environment is constrained, use the
parallelismflag or wrap the builds in afor_eachthat limits concurrency. - Variable Scope – The
platformvariable is injected into the inner build viavariables. If you need to reference the architecture elsewhere (e.g., in a shell script), expose it as an environment variable withenvironment_vars = [{"PLATFORM" = var.platform}]. - Naming Conflicts – Packer automatically appends the architecture suffix to the
ami_nameand any post‑processor output filenames. If you override naming conventions, you may need to manually add the suffix.
Actionable Next Steps
- Verify you’re on Packer 1.8+ by running
packer version. - Start with the example above, replacing placeholders with your own ISO path, AWS credentials, and region.
- Run
packer build -debug template.pkr.hcllocally to confirm that two parallel builds start and finish successfully. - Integrate the template into your CI pipeline. If you’re using GitHub Actions, add a
runs-on: ubuntu-latestjob and setpacker build -parallelism=2to control concurrency. - Monitor resource usage. If you hit limits, consider running the builds sequentially with
packer build -parallelism=1or splitting the template into separate files. - Once the images are built, test them on each target architecture to ensure that provisioners ran correctly and the system boots as expected.
By leveraging Packer’s platforms array, you cut template duplication in half and guarantee that every architecture receives the same configuration. The only real cost is the extra resources needed for parallel execution, which most CI environments can handle with a small tweak to concurrency settings.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.