This specification defines the interface between a buildpack and the environment that runs it. This API will be used by buildpack authors.
A buildpack must contain three files:
buildpack.toml
bin/detect
bin/build
The two files in bin/
must be executable. They can be shell scripts written in a language like Bash or they
can be executables compiled from a language like Go.
bin/detect
bin/detect
This entrypoint is used to determine if a buildpack should run against a given codebase. It will often check for the existence of a particular file or some configuration indicating what kind of application has been provided. Two environment variables identify important file system paths:
CNB_PLATFORM_DIR
- a directory containing platform provided configuration, such as environment variables.CNB_BUILD_PLAN_PATH
- a path to a file containing the Build Plan.In addition, the working directory is defined as the location of the codebase the buildpack will execute against.
The executable must return an exit code of 0
if the codebase can be serviced by this buildpack, and 100
if it cannot.
Other exit codes indicate an error during detection.
This is a simple example of a buildpack that detects a Python application by
checking for the presence of a requirements.txt
file:
#!/bin/sh
if [ -f requirements.txt ]; then
echo "Python Buildpack"
exit 0
else
exit 100
fi
bin/build
bin/build
This entrypoint transforms a codebase. It will often resolve dependencies, install binary packages, and compile code. Three environment variables identify important file system paths:
CNB_LAYERS_DIR
- a directory that may contain subdirectories representing each layer created by the buildpack in the final image or build cache.CNB_PLATFORM_DIR
- a directory containing platform provided configuration, such as environment variables.CNB_BP_PLAN_PATH
- a path to a file containing the Build Plan.In addition, the working directory is defined as the location of the codebase this buildpack will execute against.
All changes to the codebase in the working directory will be included in the final
image, along with any launch layers created in the CNB_LAYERS_DIR
.
Each directory created by the buildpack under the CNB_LAYERS_DIR
can be used for any
of the following purposes:
A buildpack defines how a layer will by used by creating a <layer>.toml
with
a name matching the directory it describes in the CNB_LAYERS_DIR
. For example, a
buildpack might create a $CNB_LAYERS_DIR/python
directory and a $CNB_LAYERS_DIR/python.toml
with the following contents:
launch = true
cache = true
build = true
In this example, the python
directory will be included in the run image,
cached for future builds, and will be accessible to subsequent buildpacks.
This is a simple example of a buildpack that runs Python’s pip
package manager
to resolve dependencies:
#!/bin/sh
PIP_LAYER="$CNB_LAYERS_DIR/pip"
mkdir -p "$PIP_LAYER/modules" "$PIP_LAYER/env"
pip install -r requirements.txt -t "$PIP_LAYER/modules" \
--install-option="--install-scripts=$PIP_LAYER/bin" \
--exists-action=w --disable-pip-version-check --no-cache-dir
echo "launch = true" > "$PIP_LAYER.toml"
buildpack.toml
A buildpack must contain a buildpack.toml
file in its root directory.
api = "0.8"
[buildpack]
id = "example.com/python"
version = "1.0"
[[stacks]]
id = "io.buildpacks.stacks.bionic"
The schema is as follows:
api
(string, required, current: 0.8
)
The Buildpack API version the buildpack adheres to. Used to ensure compatibility against
the lifecycle.
Not to be confused with Cloud Foundry or Heroku buildpack versions. This version pertains to the interface between the buildpack and the lifecycle of Cloud Native Buildpacks.
buildpack
(required)
Information about the buildpack.
id
(string, required)
A globally unique identifier.
version
(string, required)
The version of the buildpack.
name
(string, required)
Human readable name.
clear-env
(boolean, optional, default: false
)
Clears user-defined environment variables when true
on executions of bin/detect
and bin/build
.
homepage
(string, optional)
Buildpack homepage.
description
(string, optional)
A short description of the buildpack.
keywords
(string(s), optional)
Keywords to help locate the buildpack. These can be useful if publishing to the Buildpack Registry.
sbom-formats
(string(s), optional)
SBOM formats output by the buildpack. Supported values are the following media types: application/vnd.cyclonedx+json
, application/spdx+json
, and application/vnd.syft+json
.
licenses
(list, optional)
A list of licenses pertaining to the buildpack.
type
(string, optional)
The type of the license. This may use the SPDX 2.1 license expression, but it is not limited to identifiers in the SPDX Licenses List. If the buildpack is using a nonstandard license, then the uri
key may be specified in lieu of or in addition to type
to point to the license.
uri
(string, optional)
A URL or path to the license.
stacks
(list, optional)
A list of stacks supported by the buildpack.
If omitted, order
list must be present. Cannot be used in conjunction with order
list.
id
(string, required)
The id of the supported stack.
mixins
(string list, required)
A list of mixins required on the stack images.
order
(list, optional)
A list of buildpack groups for the purpose of creating a meta-buildpack. This list determines the
order in which groups of buildpacks will be tested during detection. If omitted, stacks
list must be present.
Cannot be used in conjunction with stacks
list.
group
(list, required)
A list of buildpack references.
id
(string, required)
The identifier of a buildpack being referred to.
Buildpacks with the same ID may appear in multiple groups at once but never in the same group.
version
(string, required)
The version of the buildpack being referred to.
optional
(boolean, optional, default: false
)
Whether or not this buildpack is optional during detection.
metadata
(any, optional)
Arbitrary data for buildpack.
The Build Plan is a document the buildpacks can use to pass information between the detect and build phases. The build plan is passed (by the lifecycle) as a parameter to the detect
and build
binaries of the buildpack.
detect
phase, the buildpack(s) may write something it requires
or provides
(or both) into the Build Plan.build
phase, the buildpack(s) may read the Buildpack Plan (a condensed version of the Build Plan, composed by the lifecycle) to determine what it should do, and refine the Buildpack Plan with more exact metadata (eg: what version dependency it requires).A buildpack can require
or provide
multiple dependencies, and even multiple groupings of dependencies (using or
lists). Additionally, multiple buildpacks may require
or provide
the same dependency.
The lifecycle uses the Build Plan as one element in deciding whether or not a particular list of buildpacks is appropriate, by seeing whether all dependencies required can be provided by that list.
Let’s walk through some possible cases a node-engine
buildpack may consider:
We will also consider what a NPM
and a JVM
buildpack may do.
A node-engine
buildpack is always happy to provide
the node
dependency. The build plan it will write may look something like:
[[provides]]
name = "node"
NOTE: If this was the only buildpack running, this would fail the
detect
phase. In order to pass, everyprovides
must be matched up with arequires
, whether in the same buildpack or in another buildpack. See the spec for particulars on how ordering buildpacks can adjust detection results.
During the detect
phase, the node-engine
buildpack sees in one configuration file (e.g. a .nvmrc
file in the app directory) that node v10.x
is explicitly requested by the application. Seeing that, it may write the below text to the build plan:
[[provides]]
name = "node"
[[requires]]
name = "node"
version = "10.x"
[requires.metadata]
version-source = ".nvmrc"
As always, the buildpack provides
node
. In this particular case, a version of node
(10.x
) is being requested in a configuration file (.nvmrc
). The buildpack chooses to add an additional piece of metadata (version-source
), so that it can understand where that request came from.
NPM
is the default package manager for node
. A NPM Buildpack may ensure that all the packages for the application are present (by running npm install
), and perhaps cache those packages as well, to optimize future builds.
NPM is typically distributed together with node. As a result, a NPM buildpack may require node
, but not want to provide
it, trusting that the node-engine
buildpack will be in charge of providing
node
.
The NPM buildpack could write the following to the build plan, if the buildpack sees that npm
is necessary (e.g., it sees a package.json
file in the app directory):
[[requires]]
name = "node"
If, looking in the package.json
file, the NPM buildpack sees a specific version of node
requested in the engines field (e.g. 14.1
), it may write the following to the build plan:
[[requires]]
name = "node"
version = "14.1"
[requires.metadata]
version-source = "package.json"
NOTE: As above, if this was the only buildpack running, this would fail the
detect
phase. In order to pass, everyprovides
must be matched up with arequires
, whether in the same buildpack or in another buildpack. See the spec for particulars on how ordering buildpacks can adjust detection results.
However, if the NPM Buildpack was run together with the Node Engine buildpack (which provides
node
), the lifecycle will see that all requirements are fulfilled, and select that group as the correct set of buildpacks.
Java is distributed in two formats - the jdk
(Java Development Kit), which allows for compilation and running of Java programs, and the jre
(Java Runtime Environment, which allows for running compiled Java programs). A very naive implementation of the buildpack may have it write several provides
options to the build plan, detailing everything that it can provide, while later buildpacks would figure out based on the application which options it requires, and would require
those. In this particular case, we can use the or
operator to present different possible build plans the buildpack can follow:
# option 1 (`jre` and `jdk`)
[[provides]]
name = "jre"
[[provides]]
name = "jdk"
# option 2 (or just `jdk`)
[[or]]
[[or.provides]]
name = "jdk"
# option 3 (or just `jre`)
[[or]]
[[or.provides]]
name = "jre"
The buildpack gives three options to the lifecycle:
jre
jdk
jdk
and jre
As with the other buildpacks, this alone will not be sufficient for the lifecycle. However, other buildpacks that follow may require
certain things. For example, another buildpack may look into the application and, seeing that it is a Java executable, require
the jre
in order to run it. When the lifecycle analyzes the results of the detect phase, it will see that there is a buildpack which provides jre
, and a buildpack that requires jre
, and will therefore conclude that those options represent a valid set of buildpacks.
provides
(list, optional)
A list of dependencies which the buildpack provides.
name
(string, required)requires
(list, optional)
A list of dependencies which the buildpack requires.
name
(string, required)
The name of the required dependency.
version
(string, optional)
The version of the dependency required.
metadata
(object, optional)
Any additional key-value metadata you wish to store.
or
(array, optional)
A list of alternate requirements which the buildpack provides/requires. Each or
array must contain a valid Build Plan (with provides
and requires
)
For more information, see the Build Plan section of the spec.
Given the buildpack and lifecycle both declare a Buildpack API version in format:
<major>.<minor>
Then a buildpack and a lifecycle are considered compatible if all the following conditions are true:
<major>
is 0
, then <minor>
s must match.<major>
is greater than 0
, then <minor>
of the buildpack must be less than
or equal to that of the lifecycle.<major>
s must always match.Buildpack implements Buildpack API | Lifecycle implements Buildpack API | Compatible? |
---|---|---|
0.2 |
0.2 |
yes |
1.1 |
1.1 |
yes |
1.2 |
1.3 |
yes |
0.2 |
0.3 |
no |
0.3 |
0.2 |
no |
1.3 |
1.2 |
no |
1.3 |
2.3 |
no |
2.3 |
1.3 |
no |
You can read the complete Buildpack API specification on Github.