# Packer Deployment & Usage Guide
Complete guide for building, configuring, and deploying HashiCorp Packer to create identical machine images across multiple platforms.
## 1. Prerequisites
### Required
- **Go 1.21+** — For building from source (matches `go.mod` requirements)
- **Git** — For cloning and plugin management
- **Make** — For using build automation (standard HashiCorp Makefile expected)
### Optional (Platform-Specific)
- **Docker** — Required for Docker builder workflows
- **AWS CLI** — For Amazon EBS/EC2 builders (configure credentials via `~/.aws/credentials` or env vars)
- **Azure CLI** — For Azure builders
- **GCP SDK** — For Google Cloud builders
- **HCP Account** — For HCP Packer Registry integration (requires service principal or user credentials)
## 2. Installation
### Option A: Install from Source (Development)
```bash
# Clone the repository
git clone https://github.com/hashicorp/packer.git
cd packer
# Build the binary
go build -o packer .
# Or install to $GOPATH/bin
go install .
# Verify installation
./packer version
Option B: Download Release Binary
# Download from HashiCorp releases (replace X.Y.Z with version)
curl -O https://releases.hashicorp.com/packer/X.Y.Z/packer_X.Y.Z_linux_amd64.zip
unzip packer_X.Y.Z_linux_amd64.zip
sudo mv packer /usr/local/bin/
Option C: Homebrew (macOS/Linux)
brew tap hashicorp/tap
brew install hashicorp/tap/packer
Plugin Installation
Packer uses external plugins for platform support. Initialize plugins defined in your config:
packer init .
Or install specific plugins manually (example for AWS):
packer plugins install github.com/hashicorp/amazon
3. Configuration
HCL2 Configuration Structure
Create a main.pkr.hcl file with required blocks:
# Define required plugins with version constraints
packer {
required_plugins {
amazon = {
version = ">= 1.0.0"
source = "github.com/hashicorp/amazon"
}
docker = {
version = ">= 1.0.0"
source = "github.com/hashicorp/docker"
}
}
}
# Input variables (type constraints supported in HCL2)
variable "aws_region" {
type = string
default = "us-east-1"
}
# Local variables for computed values
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
}
# Source configuration (previously "builders" in JSON)
source "amazon-ebs" "example" {
region = var.aws_region
source_ami = "ami-12345678"
instance_type = "t2.micro"
ssh_username = "ubuntu"
ami_name = "packer-example-${local.timestamp}"
}
# Build block defining provisioners and post-processors
build {
sources = ["source.amazon-ebs.example"]
provisioner "shell" {
inline = ["echo 'Provisioning...'", "sudo apt-get update"]
}
post-processor "manifest" {
output = "manifest.json"
}
}
Environment Variables
| Variable | Description |
|---|---|
PACKER_LOG | Set to 1 for debug logging |
PACKER_LOG_PATH | Path to log file |
PACKER_PLUGIN_PATH | Custom directory for plugins |
PACKER_CONFIG_DIR | Packer configuration directory (default: ~/.config/packer/) |
HCP_CLIENT_ID | HCP service principal ID (for registry) |
HCP_CLIENT_SECRET | HCP service principal secret |
HCP Packer Registry Configuration
To publish artifacts to HCP Packer, add to your build block:
build {
hcp_packer_registry {
bucket_name = "my-application"
description = "Machine image for web servers"
bucket_labels = {
"os" = "ubuntu-20.04"
}
build_labels = {
"build-time" = timestamp()
}
}
sources = ["source.amazon-ebs.example"]
}
Authenticate via:
export HCP_CLIENT_ID="your-client-id"
export HCP_CLIENT_SECRET="your-client-secret"
Migrating from JSON
If upgrading legacy JSON templates:
packer hcl2_upgrade my-template.json
This generates my-template.json.pkr.hcl. Note: Avoid mixing Go templating ({{ }}) with HCL2 expressions (${ }) as they execute at different phases.
4. Build & Run
Development Workflow
# Format HCL files
packer fmt .
# Validate configuration
packer validate .
# Build with debug mode (keeps resources on failure for inspection)
packer build -debug .
# Build with specific variable overrides
packer build -var="aws_region=us-west-2" .
# Build with variable file
packer build -var-file="variables.pkrvars.hcl" .
Local Development Build
# Run directly from source without installing
go run . build .
# Run with race detector (development only)
go run -race . build .
Core Execution Options
When using Packer as a library (from packer/core.go):
import "github.com/hashicorp/packer/packer"
config := &packer.CoreConfig{
Template: tpl, // *template.Template
Variables: map[string]string{"aws_region": "us-east-1"},
Version: "1.9.0",
}
core, err := packer.NewCore(config)
if err != nil {
log.Fatal(err)
}
Debugging Builds
Enable detailed logging:
PACKER_LOG=1 PACKER_LOG_PATH=packer.log packer build .
5. Deployment
CI/CD Integration
GitHub Actions Example:
name: Build Image
on: [push]
jobs:
packer:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Packer
uses: hashicorp/setup-packer@main
with:
version: "latest"
- name: Init plugins
run: packer init .
- name: Validate
run: packer validate .
- name: Build
run: packer build .
env:
HCP_CLIENT_ID: ${{ secrets.HCP_CLIENT_ID }}
HCP_CLIENT_SECRET: ${{ secrets.HCP_CLIENT_SECRET }}
GitLab CI Example:
build:
image: hashicorp/packer:latest
script:
- packer init .
- packer build .
variables:
PACKER_LOG: "1"
Docker Usage
Run Packer in Docker without local installation:
docker run --rm -v $(pwd):/workspace -w /workspace \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
hashicorp/packer:latest build .
HCP Packer Registry Publishing
Images are automatically published to HCP when hcp_packer_registry block is configured. The registry tracks:
- Buckets: Logical groupings of images (e.g., "web-server", "database")
- Versions: Individual builds with metadata
- Channels: Mutable pointers to specific versions (e.g., "latest", "production")
Heartbeat signals are sent every 2 minutes during builds to indicate active status.
Plugin Distribution
For air-gapped environments, pre-download plugins:
# Download plugins to local directory
packer plugins install --path ./plugins github.com/hashicorp/amazon
# Archive for transfer
tar -czf packer-plugins.tar.gz ./plugins
# On target system, extract and set plugin path
export PACKER_PLUGIN_PATH=/path/to/plugins
packer build .
6. Troubleshooting
Plugin Version Compatibility
Error: incompatible API version or protocol version mismatch
Solution: Check plugin SDK compatibility. From BinaryInstallationOptions, verify:
APIVersionMajorandAPIVersionMinormatch between Packer core and plugin- Update plugin to version supporting your Packer version:
packer plugins install github.com/hashicorp/amazon@latest
HCL2 Syntax Errors
Error: Unsupported block type or Argument or block definition required
Solution:
- Run
packer fmt .to fix formatting - Ensure file suffix is
.pkr.hcl(not.hclalone) - Check that variable references use
var.namenot{{user \name`}}` (JSON syntax)
HCP Authentication Failures
Error: unable to authenticate with HCP or 401 Unauthorized
Solution:
- Verify
HCP_CLIENT_IDandHCP_CLIENT_SECRETare exported (notHCP_API_KEY) - Check service principal has
contributorrole on HCP Packer registry - For organization-level buckets, ensure proper resource naming conventions
Build Failures with Source Images
Error: Source image not found or Invalid AMI
Solution:
- Verify source AMI exists in specified region
- Check IAM permissions for
ec2:DescribeImages(AWS) or equivalent - Use
most_recentfilter in source block:source_ami_filter { filters = { name = "ubuntu/images/*ubuntu-focal-20.04-amd64-server-*" virtualization-type = "hvm" } owners = ["099720109477"] # Canonical most_recent = true }
Plugin Installation Issues
Error: Failed to install plugin or checksum validation failures
Solution:
- Clear plugin cache:
rm -rf ~/.config/packer/plugins - Check network access to
releases.hashicorp.com - For custom plugins, verify
packer plugins installpath includes protocol version subdirectory
HCL2 Upgrade Issues
Error: mixed go templating and HCL2 calls after upgrade
Solution:
- Review generated
.pkr.hclfile - Replace Go template syntax
{{ timestamp }}with HCL2timestamp() - Move complex logic to
localsblocks rather than inline in builder definitions
Debugging Plugin Crashes
Enable plugin-specific logging:
PACKER_LOG=1 PACKER_LOG_PATH=packer.log packer build -debug .
Check logs for panic in plugin subprocesses. Plugin logs appear in /tmp/packer-plugin-* during execution when PACKER_LOG=1 is set.