Skip to main content

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.

โœ“ Multi-cloud: AWS / GCP / Azure / VMware โœ“ Shell + Ansible provisioners โœ“ HCL2 templates โœ“ Integrates with Terraform

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.).

Share this tool: X / Twitter LinkedIn
โœ“ 60,000+ developers use DevPlaybook โ˜…โ˜…โ˜…โ˜…โ˜… 4.8 / 5 on Gumroad โœ“ MIT licensed ยท Instant download โœ“ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • โšก AI Code Review, Doc Generator & SQL Builder
  • โšก All premium templates & early access
  • โšก Member discounts on Gumroad products
Start Free Trial โ†’

$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.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro โ€” $79 โ†’

MIT licensed ยท Instant download ยท No subscription

See what's included โ†’ Browse all deals โ†’