Buildpack API

Last Updated: 02 April 2014

Table of Contents

If you have questions about the build process on Heroku, consider discussing it in the Build forums.

Buildpack API

We encourage buildpack developers to use sh or bash to ensure compatibility with future Heroku stacks.

A buildpack consists of three scripts:

  • bin/detect: Determines whether to apply this buildpack to an app.
  • bin/compile: Used to perform the transformation steps on the app.
  • bin/release: Provides metadata back to the runtime.

bin/detect

Usage

bin/detect BUILD_DIR

The name sent to stdout will be displayed as the application type during push.

Summary

This script takes BUILD_DIR as a single argument and should return an exit code of 0 if the app present at BUILD_DIR can be serviced by this buildpack. If the exit code is 0, the script should print a human-readable framework name to stdout.

Example

#!/bin/sh

# this pack is valid for apps with a hello.txt in the root
if [ -f $1/hello.txt ]; then
  echo "HelloFramework"
  exit 0
else
  exit 1
fi

bin/compile

Usage

bin/compile BUILD_DIR CACHE_DIR ENV_DIR

Summary

The contents of CACHE_DIR will be persisted between builds. You can cache the results of long processes like dependency resolution here to speed up future builds.

This script performs the buildpack transformation. BUILD_DIR will be the location of the app and CACHE_DIR will be a location the buildpack can use to cache build artifacts between builds.

All output received on stdout from this script will be displayed to the user.

The application in BUILD_DIR along with all changes made by the compile script will be packaged into a slug.

ENV_DIR is a directory that contains a file for each of the application’s configuration variables. These are the same configuration variables that are injected into the apps runtime environment.

The application config vars are passed to the buildpack as an argument (versus set in the environment) so that the buildpack can optionally export none, all or parts of the app config vars available in ENV_DIR when the compile script is run. Exporting the config can be scoped to only certain steps in the compile script or to the entire compile phase. The reason application config vars are passed to the compile script in individual files is to make it easier for buildpacks to correctly handle config vars with multi-line values

The name of the file is the config key and the contents of the file is the config value. The equivalent of config var S3_KEY=8N029N81 is a file with the name S3_KEY and contents 8N029N81.

Below is a Bash function demonstrating how to export the content of the ENV_DIR into the environment.

export_env_dir() {
  env_dir=$1
  whitelist_regex=${2:-''}
  blacklist_regex=${3:-'^(PATH|GIT_DIR|CPATH|CPPATH|LD_PRELOAD|LIBRARY_PATH)$'}
  if [ -d "$env_dir" ]; then
    for e in $(ls $env_dir); do
      echo "$e" | grep -E "$whitelist_regex" | grep -qvE "$blacklist_regex" &&
      export "$e=$(cat $env_dir/$e)"
      :
    done
  fi
}

Use the regular expressions to blacklist or whitelist config keys for export. For example, runtime config vars like PATH can sometimes conflict with build tools so blacklisting that might be a good idea. Whitelisting is recommended if you know that builds will only require a specific subset of config vars (like DATABASE_URL) to run.

Style

Buildpack developers are encouraged to match the Heroku push style when displaying output.

-----> Main actions are prefixed with a 6-character arrow
       Additional information is indented to align

Whenever possible display the versions of the software being used.

-----> Installing dependencies with npm 1.0.27

Example

#!/bin/sh

indent() {
  sed -u 's/^/       /'
}

echo "-----> Found a hello.txt"

# if hello.txt has contents, display them (indented to align)
# otherwise error

if [ ! -s $1/hello.txt ]; then
  echo "hello.txt was empty"
  exit 1
else
  echo "hello.txt is not empty, here are the contents" | indent
  cat $1/hello.txt
fi | indent

bin/release

Usage

bin/release BUILD_DIR

Summary

addons will only be applied the first time an app is deployed.

BUILD_DIR will be the location of the app.

This script returns a YAML formatted hash with two keys:

  • addons is a list of default addons to install.
  • default_process_types is a hash of default Procfile entries.

This script will only be run if present.

Example

#!/bin/sh

cat << EOF
---
addons:
  - heroku-postgresql:dev
default_process_types:
  web: bin/node server.js
EOF

Of course, rather than using default_process_types, it’s simpler to just write a default Procfile if one isn’t provided.

Default config values

To add default config values, create a .profile.d script. In most cases it’s best to defer to an existing value if set.

Example .profile.d/nodejs.sh

# default NODE_ENV to production
export NODE_ENV=${NODE_ENV:production}

# add node binaries to the path
PATH=$PATH:$HOME/.node/bin

Caching

Use caution when storing large amounts of data in the CACHE_DIR as the full contents of this directory is stored with the git repo and must be network-transferred each time this app is deployed. A large CACHE_DIR can introduce significant delay to the build process.

The bin/compile script will be given a CACHE_DIR as its second argument which can be used to store artifacts between builds. Artifacts stored in this directory will be available in the CACHE_DIR during successive builds. CACHE_DIR is available only during slug compilation, and is specific to the app being built.

If the build pack does intend to use a cache, it should create the CACHE_DIR directory if it doesn’t exist.

Build packs often use this cache to store resolved dependencies to reduce build time on successive deploys.

Heroku users can use the heroku-repo plugin to clear the build cache created by the buildpack they use for their app

Binaries

A buildpack is responsible for building a complete working runtime environment around the app. This may necessitate including language VMs and other runtime dependencies that are needed by the app to run.

When building and packaging dependencies, make sure they are compatible with Heroku’s runtime environment. One way to do that is to build dependencies in Heroku dynos, for example by using heroku run.

Publishing

Buildpacks may be published to the buildpack catalog service using the heroku-buildpacks plugin.

Example Buildpacks

The easiest way to get started creating a buildpack is to examine or fork one of the many existing buildpacks: * Default Buildpacks * Third-party Buildpacks

Problems or questions

If you’re trying to create a buildpack and run into problems or have questions along the way, please submit them to Stack Overflow.