HashiCorp Packer
Build identical machine images for AWS, GCP, Azure, Docker, and more from a single HCL2 template. Bake your application into immutable AMIs and ship faster, safer deployments with zero configuration drift.
Installing Packer
# macOS brew tap hashicorp/tap brew install hashicorp/tap/packer # Linux wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list sudo apt update && sudo apt install packer # Verify packer version
HCL2 Template Structure
A Packer HCL2 template has three top-level blocks: packer (required plugins), source (what to build and where), and build (what to do inside):
# aws-ami.pkr.hcl
# 1. Required plugins
packer {
required_plugins {
amazon = {
version = ">= 1.3.0"
source = "github.com/hashicorp/amazon"
}
}
}
# 2. Variables
variable "aws_region" {
type = string
default = "us-east-1"
}
variable "app_version" {
type = string
}
# 3. Locals (computed values)
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
ami_name = "my-app-{{var.app_version}}-${local.timestamp}"
}
# 4. Source block (what to build)
source "amazon-ebs" "ubuntu" {
ami_name = local.ami_name
instance_type = "t3.medium"
region = var.aws_region
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-jammy-22.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"] # Canonical
}
ssh_username = "ubuntu"
tags = {
Name = local.ami_name
AppVersion = var.app_version
BuildDate = local.timestamp
ManagedBy = "Packer"
}
}
# 5. Build block (provisioners run inside the machine)
build {
name = "my-app-ami"
sources = ["source.amazon-ebs.ubuntu"]
# Provisioner 1: Shell scripts
provisioner "shell" {
inline = [
"sudo apt-get update -y",
"sudo apt-get install -y nginx nodejs npm",
"sudo systemctl enable nginx",
]
}
# Provisioner 2: Copy application files
provisioner "file" {
source = "app/"
destination = "/home/ubuntu/app"
}
# Provisioner 3: Run setup script
provisioner "shell" {
script = "scripts/setup-app.sh"
environment_vars = [
"APP_VERSION=${var.app_version}",
]
}
} Using Ansible Provisioner
# packer {
# required_plugins {
# ansible = {
# version = ">= 1.1.0"
# source = "github.com/hashicorp/ansible"
# }
# }
# }
build {
sources = ["source.amazon-ebs.ubuntu"]
# Packer connects via SSH and runs Ansible against the temporary instance
# No Ansible agent required on the target machine
provisioner "ansible" {
playbook_file = "ansible/site.yml"
user = "ubuntu"
use_proxy = false
extra_arguments = [
"--extra-vars", "app_version=${var.app_version} env=production",
"--tags", "app,nginx",
]
ansible_env_vars = [
"ANSIBLE_HOST_KEY_CHECKING=False",
]
}
} Building and Running Packer
# Initialize (download required plugins) packer init . # Validate the template (syntax + variable checks) packer validate -var "app_version=1.4.2" aws-ami.pkr.hcl # Build the AMI (requires AWS credentials in env) export AWS_ACCESS_KEY_ID="AKIA..." export AWS_SECRET_ACCESS_KEY="..." # or use an IAM instance profile if running in AWS packer build -var "app_version=1.4.2" aws-ami.pkr.hcl # Output: # --> amazon-ebs.ubuntu: AMI: ami-0a1b2c3d4e5f67890 # Build 'amazon-ebs.ubuntu' finished after 8 minutes. # Build multiple targets (parallel) packer build -parallel-builds=2 . # builds all .pkr.hcl files in directory
GitHub Actions CI for Image Builds
# .github/workflows/build-ami.yml
name: Build AMI
on:
push:
branches: [main]
paths:
- 'packer/**'
- 'ansible/**'
- 'scripts/**'
permissions:
id-token: write # for OIDC (keyless AWS auth)
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsPackerRole
aws-region: us-east-1
- uses: hashicorp/setup-packer@main
with:
version: "latest"
- name: Packer Init
run: packer init packer/
- name: Packer Validate
run: packer validate -var "app_version=${{ github.sha }}" packer/
- name: Packer Build
run: packer build -var "app_version=${{ github.sha }}" packer/
- name: Store AMI ID in SSM
run: |
AMI_ID=$(cat packer-manifest.json | jq -r '.builds[0].artifact_id' | cut -d: -f2)
aws ssm put-parameter --name /my-app/ami-id --value $AMI_ID --type String --overwrite Packer vs Docker for Immutable Infrastructure
| Aspect | Packer (Machine Images) | Docker (Container Images) |
|---|---|---|
| Output format | AMI, GCP Image, Azure VHD, OVF | OCI container image |
| Runtime | Full VM (kernel + OS) | Shared kernel (namespaces) |
| Startup time | Minutes (VM boot) | Seconds (container start) |
| Isolation | Full hardware isolation | Process isolation |
| Best for | EC2 fleets, legacy apps, compliance | Microservices, Kubernetes |
| Multi-platform build | Yes (same template, multiple clouds) | Yes (multi-arch via buildx) |
Frequently Asked Questions
What is HashiCorp Packer and why build machine images?▼
HashiCorp Packer is an open-source tool that automates the creation of identical machine images for multiple platforms (AWS AMI, GCP image, Azure VHD, Docker, VMware, VirtualBox) from a single configuration. The 'immutable infrastructure' philosophy it enables means: instead of SSHing into servers to update software (mutable), you build a new image with the changes baked in and replace running instances. Benefits: servers in production are identical to what was tested, deployments are faster (no configuration at boot time), rollbacks mean launching the previous image version, and there are no configuration drift or snowflake server problems.
What is the difference between Packer HCL2 and JSON templates?▼
Packer originally used JSON templates (.json) but introduced HCL2 templates (.pkr.hcl) in Packer 1.7. HCL2 is now the recommended format. HCL2 advantages: variables with validation, for_each for building multiple images, locals blocks for computed values, native Packer functions, better error messages, and support for multiple source blocks in a single file. JSON templates still work but are legacy. Use HCL2 for new projects. The packer hcl2_upgrade command converts existing JSON templates to HCL2.
How does Packer integrate with Terraform?▼
A common pattern: Packer builds an AMI with your application pre-installed, then Terraform provisions EC2 instances using that AMI ID. Packer outputs the artifact ID (e.g., ami-0abc123456) after a successful build. You can pass this ID to Terraform via a data source (aws_ami with filter on tags), a Terraform variable in your CI/CD pipeline (TF_VAR_ami_id=$PACKER_AMI_ID), or by storing it in SSM Parameter Store and reading it in Terraform with aws_ssm_parameter. This fully automates the image โ infrastructure pipeline.
What provisioners does Packer support?▼
Packer provisioners run inside the temporary machine during the build to install software and configure the image. Key provisioners: shell (run bash/PowerShell scripts), ansible (run an Ansible playbook against the machine, no agent needed), file (copy files into the image), chef-solo, puppet, salt-masterless, and powershell (for Windows). The ansible provisioner is popular for teams already using Ansible โ you can reuse existing playbooks to build both running servers and pre-baked machine images with the same codebase.
How do I build Packer images in a CI/CD pipeline (GitHub Actions)?▼
Store your Packer templates in Git. In GitHub Actions, use the hashicorp/setup-packer action to install Packer, then run packer init, packer validate, and packer build. Pass AWS credentials via environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY or use OIDC for keyless auth). Tag the resulting AMI with the Git commit SHA and branch name for traceability. Trigger image builds only when relevant files change (e.g., packer/ directory or application code), not on every commit, to avoid unnecessary builds.
Can Packer build Docker images? How does it differ from a Dockerfile?▼
Yes, Packer can build Docker images using the docker builder. The key difference: Packer uses the same provisioner model (shell, Ansible, file) regardless of the target platform, so you can build an AWS AMI and a Docker image with the same provisioner scripts, ensuring they are identical. Dockerfiles use a different syntax and layer model optimized for Docker specifically. For teams building both VM images and Docker images, Packer provides consistency. For Docker-only workflows, Dockerfiles are simpler and better supported by the Docker ecosystem (multi-stage builds, BuildKit cache, etc.).
Related Products
on GumroadPremium Workflow Pack
Production-grade workflows, architecture guides, and deployment scripts for teams that ship. 10 advanced CI/CD workflows (blue-green, canary, E2E, security), 5 architecture deep-dives, code review checklist, PR process guide, and 5 battle-tested Bash deployment scripts.
Buy on Gumroad โGitHub Actions CI/CD Templates Pack
12 production-ready GitHub Actions workflow templates covering Node.js, Python, Docker, multi-environment deployments, security scanning, and auto-changelog. Copy, paste, ship.
Buy on Gumroad โDeveloper Productivity Bundle
Stop rebuilding the same setup every project. 51 VSCode snippets, 40 Git aliases, 5 Docker Compose stacks, 5 GitHub Actions workflows, 5 README templates, and a dotfiles kit. Set up a professional dev environment in under an hour.
Buy on Gumroad โYou Might Also Need
AWS CDK Infrastructure as Code Guide
Complete AWS CDK guide for TypeScript/Python IaC. Learn constructs (L1/L2/L3), stacks, CDK Pipelines, diff/deploy workflow, and comparison with Terraform and CloudFormation.
Open free tool โ
Packer Machine Image Builder Guide
Complete HashiCorp Packer guide for building machine images. Learn HCL2 templates, AWS AMI builders, shell and Ansible provisioners, post-processors, and immutable infrastructure.
Open free tool โ
AWS IAM Policy Generator
Visual AWS IAM policy JSON builder with action and resource selectors. Generate least-privilege IAM policies for S3, EC2, Lambda, DynamoDB, and more.
Open free tool โ
More Free Tools
Related Articles
Pulumi vs Terraform 2026: Which IaC Tool Should You Choose?
A deep comparison of Pulumi and Terraform for infrastructure as code in 2026. Covers syntax, state management, provider ecosystem, team adoption, and when to choose each tool.
Terraform Infrastructure as Code: Complete 2024 Guide
Master Terraform for infrastructure as code. Learn HCL syntax, providers, state management, modules, and best practices to automate your cloud infrastructure.
Terraform vs Pulumi vs AWS CDK: Infrastructure as Code Comparison 2026
Complete comparison of Terraform, Pulumi, and AWS CDK for infrastructure as code in 2026. Covers language support, state management, learning curve, ecosystem, cloud support, pricing, and when to use each tool.
Terraform vs Pulumi 2026: Infrastructure as Code Comparison with Code Examples
Complete Terraform vs Pulumi comparison for 2026 โ syntax, state management, multi-cloud support, testing, pricing, and when to choose each IaC tool.
FinOps Engineering: A Developer's Guide to Cloud Cost Optimization in 2026
A practical guide to FinOps for engineers: AWS, GCP, and Azure cost tools, Infracost for Terraform, right-sizing, spot instances, reserved capacity, auto-scaling, and the FinOps maturity model. With code examples for CI/CD cost gates.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- โก AI Code Review, Doc Generator & SQL Builder
- โก All premium templates & early access
- โก Member discounts on Gumroad products
$9/mo after trial ยท Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products โ grab them all at once and save big.
MIT licensed ยท Instant download ยท No subscription
See what's included โ Browse all deals โ