Today we're announcing the release of Packer v1.16.0, which introduces native support for generating, signing, and verifying SLSA provenance attestations for every image Packer builds. Machine images are the foundation every workload runs on, and a compromised or tampered image can silently propagate to every instance launched from it. Until now, tracking down the origin of a problem meant digging through build logs that may no longer exist. With this release, Packer provides a cryptographic, tamper-evident record of every build without requiring any separate provenance tooling.
This release also ships a handful of HCL2 improvements that make day-to-day template authoring easier.
In this post, we cover the new provenance post-processor, the packer verify-attestation command, SLSA build levels, and the smaller quality-of-life improvements also included in this release.
»Why provenance matters for machine images
The SLSA framework (Supply-chain Levels for Software Artifacts) defines progressively stronger guarantees about how software artifacts are produced. Until now, SLSA tooling has largely focused on application packages and containers. Machine images have received less attention than many supply-chain security workflows.
A provenance attestation is a signed, machine-readable statement that records the origin of an artifact. It captures the Git commit the build ran from, the repository and ref it came from, the CI pipeline that triggered the build, and the timestamps for when it ran. For local artifacts, attestation is bound to the SHA-256 digest of the artifact file. For cloud artifacts without local files, Packer derives the attestation subject from a canonical identity record. This record always contains the builder ID and artifact ID, and optionally includes the artifact's registry state (such as an HCP Packer registry URI) when the builder provides it.
Packer generates attestations as in-toto statements carrying an SLSA Provenance v1 predicate. This is the vendor-neutral format that supply-chain security tooling already knows how to consume and verify.
»The provenance post-processor
Adding provenance to an existing build requires a few lines of HCL:
build {
source "amazon-ebs" "my-image" { ... }
post-processor "provenance" {
signing_mode = "keyless"
upload_tlog = true
keyless_identity = var.keyless_identity
keyless_oidc_issuer = var.keyless_oidc_issuer
output_dir = "attestations/"
}
}
After the build completes, Packer writes a signed attestation envelope to the attestations/ directory as a plain JSON file. Store it in S3, a container registry, an artifact store, or alongside the artifact itself.
For local artifacts, every attestation captures the artifact name and its SHA-256 digest. For cloud artifacts without local files, it captures a digest of the canonical artifact identity. It also records available Git and CI metadata, the builder identity, and build timestamps.
»Signing modes
We built four options so teams can adopt provenance without overhauling their key management setup first:
Mode | How signing works | Best for |
none | Unsigned JSON statement | Getting started; storing in a trusted internal system |
key | Local PEM private key | Air-gapped environments; teams with an existing PKI |
kms | Cloud KMS or HashiCorp Vault | Production workloads with centralized key management |
keyless | Sigstore Fulcio with optional Rekor | GitHub Actions and CI pipelines with no long-lived key |
For kms mode, the provider is selected automatically from the URI:
awskms://1234abcd-12ab-34cd-56ef-1234567890ab # AWS KMS
gcpkms://projects/my-proj/locations/global/... # GCP Cloud KMS
azurekms://my-vault.vault.azure.net/keys/mykey # Azure Key Vault
hashivault://my-signing-key # HashiCorp Vault
Credentials are read from the environment in the standard way for each provider.
»SLSA build levels
SLSA defines a ladder of trust rather than a single pass/fail threshold. Here is how each level maps to what Packer provides.
Build L1: Signed provenance exists
Add the provenance post-processor to generate a record that identifies the artifact by digest and describes how it was produced. Signing is optional at L1: signing_mode = “none” writes an unsigned JSON statement. The provenance must be distributed with the artifact to satisfy L1.
Build L2: A hosted service generates and signs the provenance
Run Packer on GitHub Actions or another hosted CI platform and sign using keyless mode. The CI job's OIDC identity becomes the signer, so there are no static credentials to manage or rotate. Upload the attestation to the Rekor public transparency log to add auditable transparency log evidence. A ready-to-use reference workflow is included at examples/ci/github-actions-l2-keyless.yml. This pattern supports L2 only when the hosted build platform and provenance-generation controls meet the SLSA L2 requirements.
Build L3-compatible: Provenance generation is isolated from the build
The build job publishes only the artifact digest. A separate, isolated signing job handles the attestation and never shares a process space with the build steps. The reference workflow at examples/ci/github-actions-l3-delegated.yml uses slsa-framework/slsa-github-generator to demonstrate an L3-compatible delegated signing pattern. It does not establish L3 on its own; L3 also depends on hardened-platform and build isolation controls.
»Verifying attestations with packer verify-attestation
Provenance is only useful if it gets checked before an image is used. The new packer verify-attestation command is designed to sit inside a deployment pipeline or pre-flight script:
packer verify-attestation \
-signing-mode keyless \
-keyless-oidc-issuer https://token.actions.githubusercontent.comhttps://token.actions.githubusercontent.com/
\
-keyless-identity https://github.com/my-org/my-repo/.github/workflows/build.yml@refs/heads/mainhttps://github.com/my-org/my-repo/.github/workflows/build.yml@refs/heads/main
\
-builder-id https://github.com/my-org/my-repohttps://github.com/my-org/my-repo
\
-source-uri git+https://github.com/my-org/my-repo \
-require-rekor \
-require-timestamp \
-bundle attestations/my-image.qcow2.provenance.sigstore.json \
-artifact my-image.qcow2 \
attestations/my-image.qcow2.provenance.json
If any check fails, the command exits non-zero, and the deployment is blocked. You can enforce as much or as little as your environment warrants: At minimum, verify the signing identity and the artifact digest; for L2, add -require-rekor and -require-timestamp.
This directly enables several common use cases:
Deployment gates: Block any image that cannot produce a valid signed attestation from your trusted CI pipeline
Incident response: When a CVE drops, correlate the affected commit with images built from it and, when integrated with the inventory and deployment records, the running instances that may be affected
Compliance and audit: Provide signed provenance evidence that can support SOC 2, FedRAMP, or internal security-control reviews; the attestation does not establish compliance by itself
Shadow build detection: Verify that golden images came from approved pipelines rather than ad hoc builds
»Other improvements in v1.16.0
»continue_on_error for provisioners
The new continue_on_error meta-argument lets you mark a provisioner as non-fatal. When it fails, Packer logs a warning and continues to the next step rather than halting the build. This is useful for optional diagnostics, telemetry, or cleanup scripts that should not block image delivery. The option applies to HCL2 templates.
provisioner "shell" {
inline = ["run-diagnostics.sh"]
continue_on_error = true
}
Provisioners without this flag behave exactly as before.
»optional() in object-type variables
Object variables now support the optional() modifier for per-attribute defaults. Callers only need to provide required fields; optional fields fall back to their declared defaults. This makes it easier to evolve shared variable schemas without updating every caller at once.
variable "image_config" {
type = object({
name = string
region = optional(string, "us-east-1")
encrypt = optional(bool, true)
})
}
»New timestamp template functions
rfc3339_parse() parses an RFC 3339 timestamp into a structured object, including a unix field. unix_timestamp_parse() parses a Unix epoch integer into a structured object, including an rfc3339 field. Both are useful for stamping image names and tags with build-time date components without reaching for a shell script.
»Get started
Think: Packer now gives you a signed, verifiable record for builds configured with the provenance post-processor.
Feel: Confident that your image supply chain is auditable and defensible.
Do: Upgrade to v1.16.0 and add the provenance post-processor to your next build.
The new features described here are opt-in, and existing templates require no changes to continue building with Packer v1.16.0.
Here are some helpful resources:








