Packer, created by HashiCorp, is an open-source tool that automates the creation of identical machine images for multiple platforms from a single source configuration. Whether you need an AWS AMI, a QEMU disk image for local testing, or a VMware template, Packer lets you define the entire build in code using HCL2 templates. In this tutorial you will install Packer on RHEL 8, write HCL2 templates for both an AWS AMI and a local QEMU image, and run full build pipelines using shell and Ansible provisioners.
Prerequisites
- RHEL 8 system with sudo privileges
- Internet access or a configured package mirror
- AWS account and IAM credentials with EC2 permissions (for the AMI step)
- QEMU/KVM installed (
dnf install -y qemu-kvm) for local image builds - Ansible installed on the build host (
dnf install -y ansible)
Step 1 — Add the HashiCorp Repository and Install Packer
HashiCorp maintains an official RPM repository for RHEL-based systems. Add it with dnf config-manager and install Packer.
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo dnf install -y packer
packer version
Expected output: Packer v1.x.x. The binary is placed at /usr/bin/packer.
Step 2 — Write an HCL2 Template for an AWS AMI
Create a project directory and an HCL2 template. The amazon-ebs builder launches a temporary EC2 instance, provisions it, then snapshots it into a reusable AMI.
mkdir -p ~/packer-builds/aws && cd ~/packer-builds/aws
cat > aws-rhel8.pkr.hcl <= 1.0.0"
}
}
}
variable "region" {
default = "us-east-1"
}
source "amazon-ebs" "rhel8" {
ami_name = "rhel8-golden-{{timestamp}}"
instance_type = "t3.micro"
region = var.region
source_ami_filter {
filters = {
name = "RHEL-8.*_HVM-*-x86_64-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["309956199498"] # Red Hat official
}
ssh_username = "ec2-user"
}
build {
sources = ["source.amazon-ebs.rhel8"]
provisioner "shell" {
inline = [
"sudo dnf update -y",
"sudo dnf install -y git curl wget",
"sudo systemctl enable --now sshd"
]
}
provisioner "ansible" {
playbook_file = "./playbook.yml"
}
}
EOF
Step 3 — Write an HCL2 Template for a Local QEMU Image
The qemu builder creates a raw or qcow2 disk image locally — no cloud account needed. This is ideal for testing golden images in your on-premises lab before promoting them to AWS.
mkdir -p ~/packer-builds/qemu && cd ~/packer-builds/qemu
cat > qemu-rhel8.pkr.hcl <= 1.0.0"
}
}
}
source "qemu" "rhel8" {
iso_url = "https://example.com/rhel-8.x-x86_64-boot.iso"
iso_checksum = "sha256:CHECKSUM_HERE"
output_directory = "output-rhel8"
disk_size = "20G"
format = "qcow2"
accelerator = "kvm"
headless = true
http_directory = "http"
boot_command = [
" inst.ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks.cfg"
]
ssh_username = "root"
ssh_password = "packer"
ssh_timeout = "30m"
shutdown_command = "shutdown -P now"
}
build {
sources = ["source.qemu.rhel8"]
provisioner "shell" {
inline = [
"dnf update -y",
"dnf install -y python3 git"
]
}
}
EOF
Step 4 — Add an Ansible Provisioner Playbook
Create the Ansible playbook referenced by the AWS template. Packer runs Ansible directly against the temporary instance over SSH.
cd ~/packer-builds/aws
cat > playbook.yml << 'EOF'
---
- hosts: all
become: true
tasks:
- name: Install common packages
dnf:
name:
- vim
- htop
- curl
- unzip
state: present
- name: Set timezone
timezone:
name: UTC
- name: Disable root SSH login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
notify: Restart sshd
handlers:
- name: Restart sshd
service:
name: sshd
state: restarted
EOF
Step 5 — Initialize Plugins and Validate Templates
packer init downloads required plugins declared in the required_plugins block. packer validate checks HCL2 syntax and variable types without running a build.
# Initialize and validate the AWS template
cd ~/packer-builds/aws
packer init .
packer validate .
# Initialize and validate the QEMU template
cd ~/packer-builds/qemu
packer init .
packer validate .
Step 6 — Run the Builds
Set your AWS credentials as environment variables, then launch both builds. The -var flag overrides template variables at runtime without modifying the HCL2 file.
# Build the AWS AMI
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
cd ~/packer-builds/aws
packer build -var "region=us-east-1" .
# Build the local QEMU image
cd ~/packer-builds/qemu
packer build .
# List the resulting QEMU image
ls -lh output-rhel8/
On success, Packer prints the AMI ID for the AWS build and the path to the .qcow2 file for the QEMU build.
Conclusion
You have installed Packer on RHEL 8 via the official HashiCorp RPM repository, written HCL2 templates for both an AWS AMI and a local QEMU disk image, and automated provisioning with shell commands and Ansible. Packer brings infrastructure-as-code discipline to image creation, ensuring that every environment — development, staging, and production — starts from an identical, auditable baseline. Storing your .pkr.hcl files in version control and triggering builds from a CI pipeline gives you fully automated, reproducible golden images on every commit.
Next steps: Automating AMI Promotion Across AWS Regions with Packer Post-Processors, Integrating Packer Builds into a Jenkins Pipeline on RHEL 8, and Signing and Verifying Machine Images with Cosign on RHEL 8.