Choosing Between HCL2 and JSON for Packer Templates
A technical decision guide for choosing between HCL2 and JSON in Packer. Learn why HCL2 is the standard for modern IaC and how to implement variable validation and source blocks.
16 Sept 2025, 16:07 UTC

The Template Format Decision
When configuring HashiCorp Packer, the primary technical decision is whether to use HashiCorp Configuration Language (HCL2) or JSON. Choosing the wrong format leads to excessive code duplication, a lack of input validation, and increased maintenance overhead as your image pipeline scales.
The takeaway: Use HCL2 for all new projects and active migrations. Reserve JSON exclusively for legacy templates or scenarios where templates are generated programmatically by external tools that cannot produce HCL2 syntax.
Comparison of Template Capabilities
| Feature | HCL2 (.pkr.hcl) | JSON (.json) |
|---|---|---|
| Variable Validation | Native (type and condition checks) | None (string-only) |
| Code Reuse | Source blocks & inheritance | Manual duplication |
| Dynamic Logic | Dynamic blocks & functions | Static structure |
| Plugin Management | Declarative required_plugins |
Implicit/External |
| Parseability | Requires HCL parser | Universal (Standard JSON) |
Engineering Trade-offs
HCL2 transforms Packer templates from static configuration files into structured code. The most significant advantage is the source block, which allows you to define the "how" of an image build (e.g., AWS AMI settings) once and reference it across multiple build blocks with different provisioners. This eliminates the "copy-paste" errors common in large JSON files.
However, HCL2 introduces a dependency on the Packer binary for validation and formatting. While JSON can be validated by any standard JSON linter, HCL2 requires packer validate. Additionally, HCL2 requires Packer version 1.5 or later; environments locked to older versions must remain on JSON.
Implementation: Validated HCL2 Source Blocks
The following example demonstrates a reusable AWS source block with built-in variable validation. This prevents the build from starting if an unsupported instance type is provided, saving time and cloud costs.
# variables.pkr.hcl
variable "region" {
type = string
default = "us-east-1"
}
variable "instance_type" {
type = string
description = "The EC2 instance type for the build"
validation {
condition = contains(["t3.micro", "t3.small"], var.instance_type)
error_message = "The instance_type must be either t3.micro or t3.small."
}
}
# build.pkr.hcl
packer {
required_plugins {
amazon = {
version = "~> 1.0"
source = "github.com/hashicorp/amazon"
}
}
}
source "amazon-ebs" "ubuntu" {
ami_name = "ubuntu-base-{{timestamp}}"
instance_type = var.instance_type
region = var.region
source_ami = "ami-0c55b159cbfafe1f0"
ssh_username = "ubuntu"
}
build {
sources = ["source.amazon-ebs.ubuntu"]
provisioner "shell" {
inline = ["sudo apt-get update", "sudo apt-get install -y nginx"]
}
}
Validation and Execution Workflow
To ensure the template is syntactically correct and the variables meet the defined constraints, run these commands on your local workstation or CI runner with the Packer binary installed:
- Canonicalize Format: Run
packer fmt .to ensure consistent indentation and style across the team. - Verify Syntax: Run
packer validate -var-file="prod.pkrvars.hcl" .. This check validates that all required variables are present and that thevalidationblocks in your HCL2 code are satisfied. - Inspect Requirements: Run
packer inspect .to list all variables required by the template before initiating a build.
Risk: Running packer build without packer validate can lead to failures midway through the build process (e.g., after an instance has already been launched) if a variable is missing or invalid.
Migration from JSON to HCL2
If you have existing JSON templates, you can migrate using the following pattern:
- Convert: Use
packer fmton the JSON file to generate a corresponding HCL2 version. - Refactor: Identify repeated builder configurations and move them into standalone
sourceblocks. - Type-Safe: Replace generic JSON variables with HCL2
variableblocks, addingtypeconstraints (string, number, bool, list, map, object) to prevent runtime type errors. - Initialize: Add a
packer { required_plugins { ... } }block to the top of the file and runpacker init .to lock plugin versions.
Rollback: Since converting JSON to HCL2 creates a new file or modifies the existing one, keep a git commit hash of the original JSON template. If your CI/CD parser does not support HCL2, revert to the JSON version and avoid the packer fmt conversion.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.