Self-hosted service
hashicorp/packer avatar
hashicorp/packer

HashiCorp Packer: image builds as parallel, declarative pipelines

Packer is a tool for creating identical machine images for multiple platforms from a single source configuration.

15,796 stars3,335 forksGoNOASSERTION

At a glance

What is it?
Packer takes one source configuration and produces identical machine images across platforms in parallel, through plugins. The interesting engineering is in the Makefile and Dockerfile that build the tool itself, and in how much of the plugin ecosystem is now community-run.
Who is it for?
Packer is the right tool when one machine image has to come out identical on several clouds, because the single source configuration and the parallel build are the reason the tool exists rather than something bolted on. It is actively developed, the last push was on 2026-09-21, and the plugin boundary means platform support is now largely someone else's repository.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One configuration, many platforms

The README states the purpose in two sentences, and they are the whole pitch. Packer is a tool for building identical machine images for multiple platforms from a single source configuration. It is lightweight, runs on every major operating system, and creates machine images for multiple platforms in parallel.

Three claims hide in there. Identical means the same provisioning steps run regardless of target, so a configuration does not fork into per-cloud scripts. Multiple platforms is delivered through external plugin integrations rather than through code in the core, with the full list hosted on the developer site. Parallel is the one that changes how you actually use it: instead of running one builder at a time and waiting, you write a template that names several builders and they run at once, which is the difference between a ten minute image pipeline and a one minute one.

The legacy payoff is worth mentioning because it explains a lot of the design. The images Packer creates can easily be turned into Vagrant boxes, which is why a tool whose images are now aimed at cloud providers still carries a `Vagrantfile` at the repository root and a `builder/` directory in its tree. Packer started as a way to make development environments reproducible and grew outward from there.

The tree confirms the split. `builder/`, `provisioner/`, `post-processor/` and `datasource/` are the four extension categories, `hcl2template/` holds the HCL template engine, `command/` holds the CLI surface, and `examples/` is split into `hcl/`, `ci/`, `preseeds/` and `_common/`.

The plugin boundary is the architecture

Everything about a specific platform lives in a plugin, and the dependency list shows how thin the core becomes because of that. The `go.mod` requires `github.com/hashicorp/packer-plugin-sdk v0.6.11` as the interface between core and plugins, alongside `github.com/hashicorp/go-getter/v2` for fetching inputs, `github.com/hashicorp/go-multierror` for aggregating build failures, and `github.com/hashicorp/go-version` and `github.com/hashicorp/go-uuid` for version comparison and identifier generation.

The HCL story is in there too, with `github.com/hashicorp/hcl/v2 v2.24.0` and `github.com/hashicorp/go-cty-funcs`, which is the standard mechanism for adding functions to HCL templates. Templates being HCL is a deliberate choice over the older JSON format, and the presence of `hcl2template/` as a top-level directory and a `fix/` directory for migrating older configurations says the transition is complete but remembered.

What is absent from the core is the point. There is no AWS builder, no Azure builder, no Docker driver in `go.mod` because none of them are in the core any more. A platform integration is a separate repository with its own release cycle.

That boundary has a consequence the README addresses directly in its unmaintained plugins section. When a community-maintained plugin slows down, HashiCorp may archive the plugin repository to signal its status, and the project spells out what unmaintained means: the code and full commit history remain available, documentation stays on the Packer website, issues and pull requests are monitored on a best effort basis, and no active development is performed by HashiCorp. The address for taking one over is `[email protected]`. Read that list as a risk register for any platform you depend on.

HCP Packer turns images into tracked artifacts

The README splits the quick start into two halves, and the second half is where the interesting shift is. The first is the ordinary case: there is an introduction and getting started guide for building a Docker image locally without paid cloud resources, and separately a getting started guide for AWS if you want a machine image for an external cloud.

The second is HCP Packer, which is a registry that stores Packer image metadata so you can track the image lifecycle. The stated flow is to build an AWS machine image into the registry and then reference it from Terraform.

That combination changes what Packer is. A build tool produces a file; adding a registry means the build now produces something with identity, version and lineage, which is what you need when a cloud image is consumed by infrastructure as code. If your images are built by hand and referenced by hardcoded identifiers in Terraform, you already have the problem HCP Packer is meant to solve, even if you solve it differently.

The point of order here is that Packer builds and Terraform provisions are separable. Nothing in the core requires HCP Packer, and the README presents it as an addition rather than a prerequisite.

Building Packer itself

The `Makefile` is where a project this size stops being a thin wrapper and becomes an ordinary Go program with real release engineering. The header shows the environment being captured up front, which is the part worth copying if you build anything similar:

makefile
GIT_COMMIT=$(shell git rev-parse --short HEAD)
GIT_IMPORT=github.com/hashicorp/packer/version
UNAME_S := $(shell uname -s)
LDFLAGS=-s -w
GOLDFLAGS=-X $(GIT_IMPORT).GitCommit=$(GIT_COMMIT)$(GIT_DIRTY) $(LDFLAGS)

A short commit hash is stamped into the binary through a linker flag at `github.com/hashicorp/packer/version`, and `-s -w` strips the symbol table and DWARF data. Above that sits a dirty-tree check that appends a marker when `git status --porcelain` reports changes, so a locally built binary cannot be mistaken for a tagged one. `version/` at the repository root is where that gets consumed, and `scripts/build.sh` is what the make targets invoke.

The build split is the part that catches people out. `bin` is explicitly labelled debug and test, and its recipe warns that it is for debug or test builds only and that `make release` is for release builds. `releasebin` adds a guard that greps `version/version.go` for a prerelease constant and fails the build if it is still set, which stops a snapshot from being published as a release. And `package` refuses to run without `VERSION` unless you explicitly pass `skip compilation`.

The default target is a chain, `install-build-deps install-gen-deps generate dev`, so a bare `make` in a fresh clone gets you to a working binary rather than a missing-dependency error.

The Dockerfile has the same two-audience split

The `Dockerfile` is the clearest illustration of how this project serves maintainers and consumers with one file. The header comment says it contains multiple targets, that you build one with a target flag, and that every non-dev target has a `PRODUCT_VERSION` argument that must be supplied at build time.

The development target is three instructions:

dockerfile
FROM docker.mirror.hashicorp.services/alpine:latest as dev

RUN apk add --no-cache git bash openssl ca-certificates

COPY bin/packer /bin/packer

That target is not a packaged image at all. It copies a locally generated binary in from `./bin/`, which means you have to run `make dev` first, and the header says exactly that. The second target, `release-light`, is the one published to DockerHub under the `light`, `light-$VERSION` and `latest` tags, and it declares `PRODUCT_VERSION`, `BIN_NAME`, and the `TARGETOS` and `TARGETARCH` arguments that Docker sets automatically when you pass `--platform`.

The base image is Alpine pulled from `docker.mirror.hashicorp.services` rather than Docker Hub directly, and the installed packages are git, bash, openssl and ca-certificates. That last one is there so the container can verify TLS certificates when talking to cloud APIs, which is a small detail that saves an afternoon the first time it is missing.

Because the two targets need different inputs, a build of the packaged image without the version argument fails in a way that is easy to diagnose once you have read the header.

Testing and release cadence

The `Makefile` declares its own priorities through its phony target list, which includes `ci-lint`, `test`, `testacc`, `testrace`, `releasebin`, `generate` and `install-lint-deps`. The presence of a separate acceptance target set, controlled by `ACC_TEST_BUILDERS` and `ACC_TEST_PROVISIONERS` variables defaulting to `all`, says the plugin integration is tested rather than assumed.

Acceptance tests for a tool like this are inherently slow, because building a real machine image on a real cloud is exactly what you cannot do on every commit. Splitting them out is the right call, and the defaults make running them against a single builder possible when you are debugging a plugin.

On releases, the repository publishes two channels. Version 1.16.1 shipped on 2026-09-18 and its notes are organised the way you would want a build tool's notes to be: features, improvements and bug fixes, each entry carrying a pull request reference. That release added installing plugins from non-GitHub HTTPS sources including mirrors and artifact repositories that expose the HashiCorp release directory format, added configurable connection timeout and retry interval settings to the WinRM communicator, and passed user variable values to builders and provisioners as `packer_user_variables` to restore legacy interpolation in plugin-managed templates.

There is also a `nightly` tag, described in its own release body as a snapshot of development activity intended for testing existing build configurations against the latest code and for experimenting with features before release. It recommends running nightlies outside production and notes you may hit issues not present in the stable release. The project is not archived and the last push was on 2026-09-21.

Editorial conclusion

Packer is the right tool when one machine image has to come out identical on several clouds, because the single source configuration and the parallel build are the reason the tool exists rather than something bolted on. It is actively developed, the last push was on 2026-09-21, and the plugin boundary means platform support is now largely someone else's repository. Two things deserve attention before you adopt it. The licence moved to BUSL-1.1, which the README badge and the Dockerfile SPDX header both state, so read it before shipping it in a product. And the unmaintained plugin policy is explicit that no active development happens once a plugin is archived, with the repo and docs staying available and issue triage on a best-effort basis, so check the plugin you need before building a pipeline around it.

Frequently asked questions

What does HashiCorp Packer actually build?

Machine images. It takes a single source configuration and produces identical images for multiple platforms through external plugin integrations, running the builders in parallel. Those images can also be turned into Vagrant boxes, which is where the tool originally started.

How does Packer support so many cloud platforms?

Through plugins. The core depends on `github.com/hashicorp/packer-plugin-sdk` and contains no platform builders itself, so each integration lives in its own repository with its own release cycle and can be community-maintained.

Can I still build Packer images from my own machine?

Yes. The README points to a getting started guide for building a Docker image locally without using any paid cloud resources, and a separate one for AWS if you want a machine image for an external provider.

What happens when a Packer plugin stops being maintained?

HashiCorp may archive the plugin repository to signal its status. The README is explicit that the code and commit history stay available, documentation remains on the Packer website, issues and pull requests are monitored as a best effort, and no active development is performed by HashiCorp.

Why does `make bin` warn against being used for releases?

Because it produces a debug or test build only. Release builds go through `make release`, and `make releasebin` refuses to run while the prerelease constant in `version/version.go` is still set to `dev`.

Official sources

  1. hashicorp/packer on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hashicorp-packer.svg)](https://hysenlabs.com/projects/hashicorp-packer)