Back to Deployment Guides
    Infrastructure as Code

    HashiCorp Packer

    Automate machine image creation for multiple platforms from a single source configuration. Build consistent, reproducible images for AWS, Azure, Docker, and more.

    Multi-Cloud
    HCL2 Templates
    Parallel Builds

    What is Packer?

    Packer is an open-source tool developed by HashiCorp that automates the creation of machine images for multiple platforms from a single source configuration. It enables infrastructure teams to build identical machine images for various environments, ensuring consistency across development, staging, and production systems.

    Packer creates machine images by:

    • Starting from a base image or installation media
    • Provisioning the system with software, configurations, and updates
    • Creating a reusable image for your target platform
    • Supporting parallel builds for multiple platforms simultaneously

    Key Benefits

    Multi-Platform Support

    Build for AWS, Azure, GCP, Docker, VMware, VirtualBox, and more

    Fast Deployment

    Pre-configured images eliminate runtime provisioning overhead

    Version Control

    Store image configurations as code in version control

    Testing

    Validate images before deployment to production

    Consistency

    Ensure identical configurations across all environments

    Installation

    1

    Install on Ubuntu/Debian

    Install from HashiCorp repository
    # Add HashiCorp GPG key
    wget -O- https://apt.releases.hashicorp.com/gpg | \
      sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
    
    # Add repository
    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
    
    # Install Packer
    sudo apt update && sudo apt install packer
    2

    Verify Installation

    Check Packer version
    packer version
    Expected output
    Packer v1.12.0

    Core Concepts

    Templates (HCL2)

    Define image builds using HCL2 format for better readability

    Basic template structure
    packer {
      required_plugins {
        amazon = {
          version = ">= 1.0.0"
          source  = "github.com/hashicorp/amazon"
        }
      }
    }
    
    source "amazon-ebs" "example" {
      # Builder configuration
    }
    
    build {
      sources = ["source.amazon-ebs.example"]
    
      provisioner "shell" {
        # Provisioner configuration
      }
    }

    Builders

    Create machine images for specific platforms

    BuilderDescriptionUse Case
    amazon-ebsCreates Amazon Machine ImagesAWS EC2 instances
    azure-armCreates Azure managed imagesAzure VMs
    googlecomputeCreates GCE imagesGCP instances
    dockerCreates Docker container imagesContainers
    virtualbox-isoCreates VirtualBox imagesLocal testing

    Provisioners

    Install and configure software on the machine image

    • shell: Execute shell scripts
    • file: Upload files from local machine
    • ansible: Run Ansible playbooks
    • chef: Execute Chef cookbooks
    • puppet: Apply Puppet manifests

    Example: Building an AWS AMI

    1

    Create AWS Ubuntu Template

    aws-ubuntu.pkr.hcl
    packer {
      required_plugins {
        amazon = {
          version = ">= 1.0.0"
          source  = "github.com/hashicorp/amazon"
        }
      }
    }
    
    variable "aws_region" {
      type    = string
      default = "us-east-1"
    }
    
    source "amazon-ebs" "ubuntu" {
      ami_name      = "packer-ubuntu-{{timestamp}}"
      instance_type = "t2.micro"
      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"]
      }
      ssh_username = "ubuntu"
    }
    
    build {
      sources = ["source.amazon-ebs.ubuntu"]
    
      provisioner "shell" {
        inline = [
          "sudo apt-get update",
          "sudo apt-get upgrade -y",
          "sudo apt-get install -y nginx",
        ]
      }
    }

    Build the image:

    Build AWS AMI
    # Initialize plugins
    packer init aws-ubuntu.pkr.hcl
    
    # Validate configuration
    packer validate aws-ubuntu.pkr.hcl
    
    # Build the image
    packer build aws-ubuntu.pkr.hcl

    Example: Building a Docker Image

    1

    Create Docker Template

    docker-nginx.pkr.hcl
    packer {
      required_plugins {
        docker = {
          version = ">= 1.0.0"
          source  = "github.com/hashicorp/docker"
        }
      }
    }
    
    source "docker" "nginx" {
      image  = "nginx:latest"
      commit = true
    }
    
    build {
      sources = ["source.docker.nginx"]
    
      provisioner "shell" {
        inline = [
          "apt-get update",
          "apt-get install -y curl",
        ]
      }
    
      post-processor "docker-tag" {
        repository = "my-nginx"
        tags       = ["latest", "v1.0"]
      }
    }
    Build Docker image
    packer init docker-nginx.pkr.hcl
    packer build docker-nginx.pkr.hcl

    Using Variables

    Define Variables

    Variables in template
    variable "app_version" {
      type    = string
      default = "1.0.0"
    }
    
    variable "environment" {
      type = string
    }

    Pass variables at build time:

    Build with variables
    packer build -var="environment=production" -var="app_version=2.0.0" template.pkr.hcl

    Or use a variables file:

    production.pkrvars.hcl
    environment = "production"
    app_version = "2.0.0"
    Build with var file
    packer build -var-file="production.pkrvars.hcl" template.pkr.hcl

    Working with Provisioners

    External Scripts

    Using external scripts
    provisioner "shell" {
      scripts = [
        "scripts/update-system.sh",
        "scripts/install-docker.sh",
        "scripts/configure-firewall.sh"
      ]
    }

    Uploading Files

    File provisioner
    provisioner "file" {
      source      = "configs/nginx.conf"
      destination = "/tmp/nginx.conf"
    }
    
    provisioner "shell" {
      inline = ["sudo mv /tmp/nginx.conf /etc/nginx/nginx.conf"]
    }

    Best Practices

    Version Control

    • • Store templates in Git
    • • Use semantic versioning for image tags
    • • Document changes in commit messages

    Security

    • • Never hardcode credentials
    • • Use environment variables for secrets
    • • Scan images for vulnerabilities

    Template Organization

    • • Use HCL2 format for readability
    • • Keep scripts in a scripts/ directory
    • • Use variables for environment values

    Performance

    • • Use parallel builds for multi-platform
    • • Enable SSH pipelining
    • • Minimize image size

    Troubleshooting

    SSH Connection Failures

    • Verify security groups allow SSH (port 22)
    • Check ssh_username matches the AMI's default user
    • Increase ssh_timeout value

    Plugin Errors

    Fix plugin issues
    # Run packer init to install required plugins
    packer init template.pkr.hcl
    
    # Clear plugin cache if needed
    rm -rf ~/.packer.d/plugins/

    Enable Debug Mode

    Debug logging
    export PACKER_LOG=1
    export PACKER_LOG_PATH="packer.log"
    packer build template.pkr.hcl

    Quick Command Reference

    CommandDescription
    packer initDownload and install required plugins
    packer validateCheck template syntax and configuration
    packer fmtFormat template files to canonical style
    packer buildBuild image from template
    packer inspectShow template components without building
    packer consoleInteractive console for testing expressions

    Additional Resources