---
description: >
  Post-processors compress files, upload files, and perform other tasks that transform artifacts. Learn how to create customm post-processors that extend Packer.
page_title: Create custom post-processors
---

# Create custom post-processors

Packer post-processors transform one artifact into another. For example, a post-processor might compress or upload files.

In the compression example, the transformation would be taking an artifact with
a set of files, compressing those files, and returning a new artifact with only
a single file (the compressed archive). For the upload example, the
transformation would be taking an artifact with some set of files, uploading
those files, and returning an artifact with a single ID: the URL of the upload.

Post-processor plugins implement the [`packer.PostProcessor`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#PostProcessor) interface and are
served using the `plugin.ServePostProcessor` function.

This page explains how to implement and serve custom post-processors. If you want your post-processor to support HashiCorp Cloud Platform (HCP) Packer, you should also review the [HCP Packer Support](/packer/docs/plugins/creation/hcp-support) documentation.

~> **Warning:** This is an advanced topic that requires strong knowledge of Packer and Packer plugins.

## Before You Begin

We recommend reviewing the following resources before you begin development:
- [Developing Plugins - Overview](/packer/docs/plugins/creation)
- The [Go](https://go.dev/) language. You must write custom plugins in Go, so this guide assumes you are familiar with the language.

## The Interface

The interface that must be implemented for a post-processor is the
[`packer.PostProcessor`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#PostProcessor) interface. It is reproduced below for reference. The
actual interface in the source code contains some basic documentation as well
explaining what each method should do.

```go
type PostProcessor interface {
  ConfigSpec() hcldec.ObjectSpec
  Configure(interface{}) error
  PostProcess(context.Context, Ui, Artifact) (a Artifact, keep, mustKeep bool, err error)
}
```

### The "ConfigSpec" Method

This method returns a hcldec.ObjectSpec, which is a spec necessary for using
HCL2 templates with Packer. For information on how to use and implement this
function, check our
[object spec docs](/packer/guides/hcl/component-object-spec)

### The "Configure" Method

The `Configure` method for each post-processor is called early in the build
process to configure the post-processor. The configuration is passed in as a
raw `interface{}`. The configure method is responsible for translating this
configuration into an internal structure, validating it, and returning any
errors.

For decoding the `interface{}` into a meaningful structure, the
[mapstructure](https://github.com/mitchellh/mapstructure) library is
recommended. Mapstructure will take an `interface{}` and decode it into an
arbitrarily complex struct. If there are any errors, it generates very
human-friendly errors that can be returned directly from the configure method.

While it is not actively enforced, **no side effects** should occur from
running the `Configure` method. Specifically, don't create files, don't create
network connections, etc. Configure's purpose is solely to setup internal state
and validate the configuration as much as possible.

`Configure` being run is not an indication that `PostProcess` will ever run.
For example, `packer validate` will run `Configure` to verify the configuration
validates, but will never actually run the build.

### The "PostProcess" Method

The `PostProcess` method is where the real work goes. PostProcess is
responsible for taking one `packer.Artifact` implementation, and transforming
it into another.
A `PostProcess` call can be cancelled at any moment. Cancellation is triggered
when the done chan of the context struct (`<-ctx.Done()`) unblocks .

When we say "transform," we don't mean actually modifying the existing
`packer.Artifact` value itself. We mean taking the contents of the artifact and
creating a new artifact from that. For example, if we were creating a
"compress" post-processor that is responsible for compressing files, the
transformation would be taking the `Files()` from the original artifact,
compressing them, and creating a new artifact with a single file: the
compressed archive.

The result signature of this method is `(Artifact, bool, bool, error)`. Each
return value is explained below:

- `Artifact` - The newly created artifact if no errors occurred.
- `bool` - If keep true, the input artifact will forcefully be kept. By default,
  Packer typically deletes all input artifacts, since the user doesn't
  generally want intermediary artifacts. However, some post-processors depend
  on the previous artifact existing. If this is `true`, it forces packer to
  keep the artifact around.
- `bool` - If forceOverride is true, then any user input for
  keep_input_artifact is ignored and the artifact is either kept or discarded
  according to the value set in `keep`.
- `error` - Non-nil if there was an error in any way. If this is the case,
  the other two return values are ignored.
