HashiCorp Packer can publish images to the HashiCorp Cloud Platform (HCP) Registry, but it’s not a direct push like docker push. Instead, Packer builds an image and then uses a separate HCP Packer builder to register that already built image with the HCP Registry.

Let’s see it in action. Imagine you’ve built an AMI for AWS using Packer. You want to make this AMI available through HCP Packer for your team to consume.

Here’s a snippet of a Packer template that builds an AWS AMI and then registers it with HCP:

packer {
  required_plugins {
    amazon = {
      version = ">= 1.0.0"
      source  = "github.com/hashicorp/amazon"
    }
    hcp = {
      version = ">= 0.10.0"
      source  = "github.com/hashicorp/hcp"
    }
  }
}

source "amazon-ebs" "my-ami" {

  ami_name      = "my-custom-ami-{{timestamp}}"

  instance_type = "t3.micro"
  region        = "us-east-1"
  source_ami_filter {
    filters = {
      Name = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
      Root-Device-Type = "ebs"
    }
    most_recent = true
    owners      = ["099720109477"] # Canonical's owner ID
  }
  ssh_username = "ubuntu"
}

build {
  sources = ["source.amazon-ebs.my-ami"]

  provisioner "shell" {
    inline = [
      "sudo apt-get update",
      "sudo apt-get install -y nginx",
      "sudo systemctl enable nginx"
    ]
  }

  post-processor "hcp-packer-publish" {
    # This is the crucial part for HCP integration
    bucket_name = "my-hcp-packer-bucket" # Your HCP Packer bucket name
    product_name = "my-product"       # Your HCP product name
    release_name = "my-release"       # A name for this specific image release
    # HCP Packer requires a cloud provider configuration.
    # For AWS, this is usually your AWS credentials (e.g., via environment variables).
    # If you need to explicitly configure, you'd add something like:
    # cloud_provider_config {
    #   aws {
    #     access_key = env("AWS_ACCESS_KEY_ID")
    #     secret_key = env("AWS_SECRET_ACCESS_KEY")
    #     region     = "us-east-1"
    #   }
    # }
  }
}

When you run packer build your-template.pkr.hcl, Packer first executes the amazon-ebs source. It launches an EC2 instance, runs the shell provisioner to install Nginx, and then creates an AMI from that instance. Once the AMI is successfully built, the hcp-packer-publish post-processor kicks in. It takes the identifier of the newly created AMI (e.g., ami-0abcdef1234567890) and registers it with your specified HCP Packer bucket, product, and release.

The HCP Registry then acts as a catalog for your machine images. You can define "products" and "releases" within your HCP account. A product is a logical grouping of images (e.g., "Web Server Images," "Database Images"). A release is a specific version of that product, and each release can contain multiple image configurations for different cloud providers (e.g., an AWS AMI ID, an Azure Image ID, a GCP Image ID).

This allows you to manage your machine images centrally. Instead of teams needing to know how to find or build specific AMIs, they can query the HCP Packer Registry using tools like Terraform or the HCP CLI. Terraform, for instance, can use the hashicorp/hcp provider to reference an image from the registry.

The primary problem this solves is discoverability and versioning of built artifacts. Before HCP Packer, you might have AMIs scattered across regions, with naming conventions that are hard to track. HCP Packer centralizes this, providing a single source of truth. You can also define dependencies: a new release might depend on a previous release, ensuring that updates are managed systematically.

The HCP Packer builder doesn’t build the image; it registers an already built image. This is a key distinction. Packer’s role is to construct the machine image itself (e.g., an AMI, a Docker image, a VMDK). The hcp-packer-publish post-processor’s role is to announce that constructed artifact to the HCP Registry. You need to have the necessary credentials configured for Packer to access the cloud provider and for the hcp-packer-publish post-processor to authenticate with HCP. This is typically done via environment variables or by configuring the cloud_provider_config block within the post-processor.

When you use Terraform to consume an image from HCP Packer, you’re not directly referencing an AMI ID. Instead, you’re referencing the HCP Packer product and release. The hcp provider in Terraform then queries the HCP Registry to get the appropriate image ID for the cloud provider and region you’ve specified in your Terraform configuration. This decouples your Terraform code from specific image IDs, making it easier to update images without changing your IaC.

The one thing that trips people up is the relationship between the bucket_name, product_name, and release_name. The bucket_name is the top-level container within your HCP account where your Packer images reside. Within that bucket, you define product_names. Think of products as categories of images. For instance, you might have a "WebServers" product. Then, for each version of your web server image, you create a release_name. So, you could have release_name = "v1.0.0", release_name = "v1.1.0", etc., all under the "WebServers" product. The hcp-packer-publish post-processor associates the built image artifact with a specific combination of bucket, product, and release.

The next concept you’ll encounter is managing image versions and dependencies across different cloud providers and regions within a single HCP Packer release.

Want structured learning?

Take the full Packer course →