Packer Image Builder

Packer cheatsheet — build machine images for AWS, Docker, VMware. packer build template.pkr.hcl, packer validate, packer init. Immutable infrastructure from code.

5 min read

What it is

Packer is an open-source tool for creating identical machine images for multiple platforms from a single source configuration. You use it to automate the creation of AMIs, Docker images, VirtualBox VMs, and more.

Installation

Linux

sudo apt update && sudo apt install packer
# or
sudo yum install packer
# or download from releases and add to PATH
wget https://releases.hashicorp.com/packer/1.10.0/packer_1.10.0_linux_amd64.zip
unzip packer_1.10.0_linux_amd64.zip
sudo mv packer /usr/local/bin/

macOS

brew install packer
# or download from releases and add to PATH
wget https://releases.hashicorp.com/packer/1.10.0/packer_1.10.0_darwin_amd64.zip
unzip packer_1.10.0_darwin_amd64.zip
sudo mv packer /usr/local/bin/

Windows

Download the appropriate ZIP file from the Packer releases page. Extract the packer.exe file and place it in a directory that is included in your system’s PATH environment variable.

Core Concepts

  • Builders: Define the platform where the image will be created (e.g., amazon-ebs, docker, virtualbox-iso).
  • Provisioners: Define how the machine image will be configured after the base image is created (e.g., running shell scripts, Ansible playbooks, Chef recipes).
  • Post-processors: Define actions to take after the image is built (e.g., uploading to a repository, tagging).
  • Templates: JSON or HCL files that define the builders, provisioners, and other settings for your image build.

Commands / Usage

Building Images

Validate a template file

packer validate my-template.json

Checks the syntax and structure of your Packer template for errors.

Build an image from a template file

packer build my-template.json

Executes the build process defined in the specified template.

Build an image with a specific builder

packer build -only=amazon-ebs my-template.json

Builds the image using only the builder named amazon-ebs (if multiple builders are defined in the template).

Build an image with variables

packer build -var 'aws_region=us-east-1' my-template.json

Overrides template variables with the values provided via the -var flag.

Build an image with variables from a file

packer build -var-file=variables.json my-template.json

Loads template variables from a JSON file.

Build an image with parallel builds

packer build -parallel-builds=4 my-template.json

Runs up to 4 builds concurrently if your template defines multiple builders.

Inspecting Images

Inspect a template file

packer inspect my-template.json

Outputs a human-readable representation of the template, including all variables and their defaults.

Plugin Management

Install a plugin

packer init .

Downloads and installs Packer plugins required by the template in the current directory.

Debugging

Debug a build

packer build -debug my-template.json

Runs the build in debug mode, pausing before each step and allowing you to interact with the machine.

Other Commands

Version

packer version

Displays the current Packer version.

Plugins

packer plugins list

Lists installed Packer plugins.

Common Patterns

Building an AMI with user data and tags

# my-aws-ami.json
{
  "builders": [
    {
      "type": "amazon-ebs",
{% raw %}
      "ami_name": "my-app-ami-{{timestamp}}",
{% endraw %}
      "instance_type": "t2.micro",
      "region": "us-west-2",
      "source_ami_filter": {
        "filters": {
          "name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
        },
        "owners": ["099720109477"],
        "most_recent": true
      },
      "tags": {
        "Name": "My App Server",
        "Environment": "Production"
      }
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "sudo apt update",
        "sudo apt install -y nginx",
        "echo 'Hello from Packer!' | sudo tee /var/www/html/index.html"
      ]
    }
  ]
}
packer build my-aws-ami.json

This builds an Amazon Machine Image (AMI) using the amazon-ebs builder, starting from a specific Ubuntu 20.04 AMI, installing Nginx, and tagging the resulting AMI.

Building a Docker image with a shell script

# my-docker-image.json
{
  "builders": [
    {
      "type": "docker",
      "image_build_method": "build",
      "tag": "my-docker-repo/my-app:latest"
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "script": "setup.sh"
    }
  ]
}
# setup.sh
#!/bin/bash
apt update
apt install -y curl
echo "Docker image built!" > /app/message.txt
packer build my-docker-image.json

This builds a Docker image, running a setup.sh script within the container to install curl and create a message file.

Using variables for region and instance type

# my-vars.json
{
  "aws_region": "eu-central-1",
  "instance_type": "t3.small"
}
# my-aws-template.json
{
  "variables": {
    "aws_region": "us-east-1",
    "instance_type": "t2.micro"
  },
  "builders": [
    {
      "type": "amazon-ebs",
{% raw %}
      "ami_name": "my-app-ami-{{timestamp}}",
{% endraw %}
{% raw %}
      "instance_type": "{{user `instance_type`}}",
{% endraw %}
{% raw %}
      "region": "{{user `aws_region`}}",
{% endraw %}
      "source_ami_filter": {
        "filters": {
          "name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
        },
        "owners": ["099720109477"],
        "most_recent": true
      }
    }
  ]
}
packer build -var-file=my-vars.json my-aws-template.json

This demonstrates how to use a variables block in the template and override them with a var-file, making your templates more reusable.

Creating multiple AMIs for different regions

# multi-region-template.json
{
  "builders": [
    {
      "type": "amazon-ebs",
{% raw %}
      "ami_name": "my-app-ami-{{timestamp}}",
{% endraw %}
      "region": "us-east-1",
      "source_ami_filter": {
        "filters": { "name": "ami-0c55b159cbfafe1f0" },
        "owners": ["099720109477"]
      }
    },
    {
      "type": "amazon-ebs",
{% raw %}
      "ami_name": "my-app-ami-{{timestamp}}",
{% endraw %}
      "region": "eu-west-1",
      "source_ami_filter": {
        "filters": { "name": "ami-0427785475542462e" },
        "owners": ["099720109477"]
      }
    }
  ]
}
packer build multi-region-template.json

This template defines two builders, each targeting a different AWS region with a specific source AMI, and packer build will create AMIs in both regions.

Gotchas

  • State Management: Packer creates temporary resources (like EC2 instances) during the build process. If a build fails mid-way, these resources might be left behind. Packer attempts to clean them up, but manual intervention might be needed in case of severe failures.
  • Idempotency of Provisioners: Ensure your provisioners are idempotent. Running the same script multiple times should not cause unintended side effects. Packer does not inherently guarantee idempotency across builds for provisioners.
  • Source AMI Updates: If you rely on most_recent: true for your source_ami_filter, be aware that a build might use a newly released, potentially different, base AMI than a previous build, which could lead to unexpected behavior if your provisioning logic is sensitive to subtle OS changes.
  • Credentials: Packer relies on the cloud provider’s credentials configured in your environment (e.g., ~/.aws/credentials for AWS, environment variables for Docker). Ensure these are set correctly before running a build.
  • Builder Specifics: Each builder has its own set of specific configurations and limitations. Always consult the official Packer documentation for the builder you are using (e.g., amazon-ebs, docker, googlecompute) for detailed options. {% raw %}
  • Timestamp Formatting: The {{timestamp}} variable uses a default format. If you need a specific date/time format in your ami_name or other output, you can use {{timestamp "format"}}. For example, {{timestamp "2006-01-02T15:04:05Z"}}. {% endraw %}