---
layout: guides
page_title: Packer Build Pipelines
sidebar_title: Pipelineing Builds
description: |-
  Here we explore how to break your builds into discrete steps so that your
  builds can be shorter and more reliable.
---

# Why Create a Template Pipeline?

A common issue users face when beginning to create their Packer templates is
that while they may need several specialized images, the early provisioning
steps are all the same. It can feel tedious to copy all of those images' basic
configuraton into each build template. It can feel even more tedious to wait a
long time for similar builds to run duplicate steps.

This is one reason why Packer recommends breaking your builds into small,
discrete steps. Not only does it allow you to create "base" images that you can
build from to create further customizations, but it also allows you to save
time in your build process because the "base" images are likely to change less
than your customizations.

It also makes it so that a failing build takes less time to debug and re-run.

In this example, we will use the Virtualbox builders, but the concepts from
this example can be applied to other builders as well.

## Starting from an ISO

Here is an extremely basic virtualbox-iso template:

<Tabs>
<Tab heading="JSON">

```json
{
  "builders": [
    {
      "type": "virtualbox-iso",
      "vm_name": "vbox-example",
      "boot_command": [
        "<esc><wait>",
        "<esc><wait>",
        "<enter><wait>",
        "/install/vmlinuz<wait>",
        " initrd=/install/initrd.gz",
        " auto-install/enable=true",
        " debconf/priority=critical",
        " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ubuntu_preseed.cfg<wait>",
        " -- <wait>",
        "<enter><wait>"
      ],
      "http_directory": "./http",

      "disk_size": "40960",
      "guest_os_type": "Ubuntu_64",
      "iso_checksum": "sha256:946a6077af6f5f95a51f82fdc44051c7aa19f9cfc5f737954845a6050543d7c2",
      "iso_url": "http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04-server-amd64.iso",

      "shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now",

      "ssh_port": 22,
      "ssh_username": "vagrant",
      "ssh_password": "vagrant"
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": ["echo initial provisioning"]
    }
  ],
  "post-processors": [
    {
      "type": "manifest",
      "output": "stage-1-manifest.json"
    }
  ]
}
```

</Tab>
<Tab heading="HCL2">

```hcl
source "virtualbox-iso" "step_1" {
  boot_command     = ["<esc><wait>", "<esc><wait>", "<enter><wait>",
                      "/install/vmlinuz<wait>", " initrd=/install/initrd.gz",
                      " auto-install/enable=true", " debconf/priority=critical",
                      " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ubuntu_preseed.cfg<wait>",
                      " -- <wait>", "<enter><wait>"]
  disk_size        = "40960"
  guest_os_type    = "Ubuntu_64"
  http_directory   = "./http"
  iso_checksum     = "sha256:946a6077af6f5f95a51f82fdc44051c7aa19f9cfc5f737954845a6050543d7c2"
  iso_url          = "http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04-server-amd64.iso"
  shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now"
  ssh_password     = "vagrant"
  ssh_port         = 22
  ssh_username     = "vagrant"
  vm_name          = "vbox-example"
}
build {
  sources = ["source.virtualbox-iso.step_1"]


  provisioner "shell" {
    inline = ["echo initial provisioning"]
  }
  post-processor "manifest" {
    output = "stage-1-manifest.json"
  }
}
```

</Tab>
</Tabs>

In order to build using this template, create a directory named "http" in your
current working directory. Copy the minimal example from our
[preseed guide](https://www.packer.io/guides/automatic-operating-system-installs/preseed_ubuntu#examples)
into a file in your http directory and name it "ubuntu_preseed.cfg". Copy the
above json template into your current working directory and save it as
"example_virtualbox_iso.json"

To run the build, call `packer build example_virtualbox_iso.json`.

This example does not set the output_directory or output_filename, so the file
will be placed in a default name of "output-virtualbox-iso/vbox-example.ovf" --
the builder will print this file name to the UI output, but in this example the
[manifest](https://www.packer.io/docs/post-processors/manifest) post-processor
to will store build information, including the names of the output files, in a
json file named "stage-1-manifest.json". From there, you can programmatically
look up the output file information.

## Customizing the iso using the virtualbox-ovf builder

That output filename generated in the first stage can be used as the
[source_path](https://www.packer.io/docs/builders/virtualbox/ovf#source_path)
for the virtualbox-ovf builder.

<Tabs>
<Tab heading="JSON">

```json
{
  "builders": [
    {
      "type": "virtualbox-ovf",
      "vm_name": "virtualbox-example-ovf",

      "shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now",
      "source_path": "output-virtualbox-iso/vbox-example.ovf",

      "ssh_password": "vagrant",
      "ssh_port": 22,
      "ssh_username": "vagrant"
    }
  ],
  "provisioners": [
    {
      "inline": ["echo secondary provisioning"],
      "type": "shell"
    }
  ]
}
```

</Tab>
<Tab heading="HCL2">

```hcl
source "virtualbox-ovf" "step_2" {
  shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now"
  source_path      = "output-virtualbox-iso/vbox-example.ovf"
  ssh_password     = "vagrant"
  ssh_port         = 22
  ssh_username     = "vagrant"
  vm_name          = "virtualbox-example-ovf"
}

build {
  sources = ["source.virtualbox-ovf.step_2"]

  provisioner "shell" {
    inline = ["echo secondary provisioning"]
  }
}

```

</Tab>
</Tabs>

## More efficiencies

You may find that you want to run time-consuming import post-processors like
the "amazon-import" post-processor independently of the build that produces
the artifacts you want to process.

In this case, you can use a null builder
and manually modify the input to the post-processing chain so that you can get
the behavior you want. The below example shows a "vagrant" post-processor
being used with a null builder, and manually sets the artifact from our
stage-2 ovf build:

<Tabs>
<Tab heading="JSON">

```json
{
  "builders": [
    {
      "type": "null",
      "communicator": "none"
    }
  ],
  "post-processors": [
    [
      {
        "type": "artifice",
        "files": [
          "output-virtualbox-ovf/virtualbox-example-ovf.ovf",
          "output-virtualbox-ovf/virtualbox-example-ovf-disk001.vmdk"
        ]
      },
      {
        "type": "vagrant",
        "provider_override": "virtualbox"
      }
    ]
  ]
}
```

</Tab>
<Tab heading="HCL2">

```hcl
source "null" "step_3" {
  communicator = "none"
}

build {
  sources = ["source.null.step_3"]

  post-processors {
    post-processor "artifice" {
      files = ["output-virtualbox-ovf/virtualbox-example-ovf.ovf", "output-virtualbox-ovf/virtualbox-example-ovf-disk001.vmdk"]
    }
    post-processor "vagrant" {
      provider_override = "virtualbox"
    }
  }
}
```

</Tab>
</Tabs>

By using the null builder instead of just running an ovf builder, we can spare ourselves all of the time Packer would normally spend launching and destroying VMs.

## Putting it all together

Packer templates don't come with a custom "glue" to bind them together. We recommend using your CI system or wrapping scripts to connect the templates into a chain.
