Buildkite Agent hooks
An agent goes through different phases in its lifecycle, including starting up, shutting down, and checking out code. Hooks let you extend or override the behavior of agents at different stages of its lifecycle. You "hook into" the agent at a particular stage.
A hook is a script executed or sourced by the Buildkite agent at a specific point in the job lifecycle. You can use hooks to extend or override the built-in behavior of an agent. Hooks are generally shell scripts, which the agent then executes or sources.
polyglot-hooks experiment, agents running v3.47.0 or later can run hooks written in any programming language. See the polyglot hooks section for more information.
You can define hooks in the following locations:
- In the filesystem of the agent machine (called agent hooks, or more rarely global hooks).
- In your pipeline's repository (called repository hooks, or more rarely local hooks).
- In plugins applied to steps.
For example, you could define an agent-wide
checkout hook that spins up a fresh
git clone on a new build machine, a repository
pre-command hook that sets up repository-specific environment variables, or a plugin
environment hook that fetches API keys from a secrets storage service.
There are two categories of hooks:
- Agent lifecycle
- Job lifecycle
Agent lifecycle hooks are executed by the Buildkite agent as part of the agent's lifecycle. For example, the
pre-bootstrap hook is executed before starting a job's bootstrap process, and the
agent-shutdown hook is executed before the agent process terminates.
Job lifecycle hooks are sourced (see "A note on sourcing" for specifics) by the Buildkite bootstrap in the different job phases. They run in a per-job shell environment, and any exported environment variables are carried to the job's subsequent phases and hooks. For example, the
environment hook can modify or export new environment variables for the job's subsequent checkout and command phases. Shell options set by individual hooks, such as set
-e -o pipefail, are not carried over to other phases or hooks.
📝 A note on sourcing
We use the word "sourcing" on this page, but it's not strictly correct. Instead, the agent uses a process called "the scriptwrapper" to run hooks.
This process notes down the environment variables before a hook run, sources that hook, and compares the environment variables after the hook run to the environment variables before the hook run.
Any environment variables added, changed, or removed are then exported to the subsequent phases and hooks. Functionally, this is very similar to how
source would work, but it's not quite the same. If you're relying on some very specific pieces of shellscripting functionality, you might find that things don't work quite as you expect.
We do this because there's no shared bash environment between two different hooks on the same job. Functionally, each hook runs in its own shell, orchestrated through the agent's Go code. This means that if you set an environment variable in one hook, it wouldn't be available in the next hook without this scriptwrapper process.
You can define hooks in the following locations:
Agent hooks: These exist on the agent file system in a directory created by your agent installer and configured by the
hooks-pathsetting. You can define both agent lifecycle and job lifecycle hooks in the agent hooks location. Job lifecycle hooks defined here will run for every job the agent receives from any pipeline.
Repository hooks: These exist in your pipeline repository's
.buildkite/hooksdirectory and can define job lifecycle hooks. Job lifecycle hooks defined here will run for every pipeline that uses the repository.
- Plugin hooks: These are provided by plugins you've included in your pipeline steps and can define job lifecycle hooks. Job lifecycle hooks defined by a plugin will only run for the step that includes them. Plugins can be vendored (if they are already present in the repository and included using a relative path) or non-vendored (when they are included from elsewhere), which affects the order they are run in.
Every agent installer creates a hooks directory containing a set of sample hooks. You can find the location of your agent hooks directory in your platform's installation documentation.
To get started with agent hooks, copy the relevant example script and remove the
.sample file extension.
Repository hooks allow you to execute repository-specific scripts. Repository hooks live alongside your repository's source code under the
To get started, create a shell script in
post-checkout. It will be sourced and run after your repository has been checked out as part of every job for any pipeline that uses this repository.
You can define any of the job lifecycle hooks whose
Order includes Repository.
Plugin hooks allow plugins you've defined in your Pipeline Steps to override the default behavior.
To use Polyglot hooks, set
experiment="polyglot-hooks" in your agent configuration.
By default, hooks must be shell scripts. However, with the
polyglot-hooks experiment on agents running version v3.47.0, hooks are significantly more flexible and can be written in the programming language of your choice.
In addition to the regular shell script hooks, polyglot hooks enable you to run two more types of hooks:
Interpreted hooks: Hooks that are run by an interpreter, such as Python, Ruby, or Node.js. These hooks are run in the same way as shell script hooks, but are executed by the appropriate interpreter instead of by the shell. These hooks must have a valid shebang as the first line of the hook. For example,
- Binary hooks: Binary executables produced by compiled languages such as Go, Rust, or C++. These hooks are run in the same way as shell script hooks, but are executed directly by the operating system. These hooks must be compiled for the correct operating system and architecture, and be executable by the agent user.
Interpreted hooks are not supported on Windows agents.
Polyglot hooks are run transparently by the agent, and are not distinguished from shell script hooks in the logs or the Buildkite dashboard. The agent will automatically detect the type of hook–whether it's a shell script, an interpreted hook, or a binary–and run it appropriately. All you need to do is place your hook in the correct location and ensure it's executable.
When polyglot hooks are called, the following extra environment variables are set:
BUILDKITE_HOOK_PHASE- The lifecycle phase of the hook being run. For example,
post-checkout. See job lifecycle hooks for the full list of phases. This enables the hook to determine the phase it's running in, allowing you to use the same hook for multiple phases.
BUILDKITE_HOOK_PATH- The path to the hook being run. For example,
BUILDKITE_HOOK_SCOPE- The scope of the hook being run. For example,
Polyglot hook usage comes with the following caveats:
- Interpreted hooks are not supported on Windows.
- Hooks must not have a file extension–except on Windows, where binary hooks must have the
- For interpreted hooks, the specified interpreter must already be installed on the agent machine. The agent won't install the interpreter or any package dependencies for you.
- Unlike shell hooks, environment variable changes are not automatically captured from polyglot hooks. If you want to modify the job's environment, you'll have to use the Job API.
|Executed at agent startup, immediately prior to the agent being registered with Buildkite. Useful for initialising resources that will be used by all jobs that an agent runs, outside of the job lifecycle.
Supported from agent version 3.42.0 and above.
|Executed when the agent shuts down. Useful for performing cleanup tasks for the entire agent, outside of the job lifecycle.
The Buildkite agent executes agent lifecycle hooks.
These hooks can only be defined in the agent
Agent lifecycle hooks can be executables written in any programming language.
On Unix-like systems (such as Linux and macOS), hooks must be files that are executable by the user the agent is running as.
Use agent lifecycle hooks to prepare for or clean up after all jobs that may run.
For example, use
pre-bootstrap to block unwanted jobs from running or use
agent-shutdown to tear down a service after all jobs are finished.
If your hook uses details about any individual job to run, prefer job lifecycle hooks for those tasks instead.
The agent exports few environment variables to agent lifecycle hooks. Read the agent lifecycle hooks table for details on the interface between the agent and each hook type.
The following is a complete list of available job hooks, and the order in which they are run as part of each job:
|Executed before any job is started. Useful for adding strict checks before jobs are permitted to run.
The proposed job command and environment is written to a file and the path to this file provided in the
BUILDKITE_ENV_FILE environment variable. Use the contents of this file to determine whether to permit the job to run on this agent.
pre-bootstrap hook terminates with an exit code of
0, the job is permitted to run. Any other exit code results in the job being rejected, and job failure being reported to the Buildkite API.
|Runs before all other hooks. Useful for exporting secret keys.
|Runs before checkout.
|Overrides the default git checkout behavior. (See Hook exceptions.)
|Runs after checkout.
|Unlike other plugins, environment hooks for vendored plugins run after checkout.
|Runs before the build command
|Overrides the default command running behavior. (See Hook exceptions.)
|Runs after the command.
|Runs before artifacts are uploaded, if an artifact upload pattern was defined for the job.
|Runs after artifacts have been uploaded, if an artifact upload pattern was defined for the job.
|Runs before the job finishes. Useful for performing cleanup tasks.
Typically, if there are multiple hooks of the same type, all of them will be run (in the order shown in the table).
As of Agent v3.15.0, if multiple
command hooks are found, only the first (of each type) will be run. This does not apply to other hook types.
However, for legacy compatibility, there is an exception with plugins. All
command hooks provided by plugins will run in the order the plugins are specified, meaning multiple
command hooks can run. Note that
checkout hooks and
command hooks provided by plugins will prevent any repository or agent hooks of the same type from running.
Job lifecycle hooks are sourced for every job an agent accepts. Use job lifecycle hooks to prepare for jobs, override the default behavior, or clean up after jobs that have finished. For example, use the
environment hook to set a job's environment variables or the
pre-exit hook to delete temporary files and remove containers. If your hook is related to the startup or shutdown of the agent, consider agent lifecycle hooks for those tasks instead.
Job lifecycle hooks have access to all the standard Buildkite environment variables.
Job lifecycle hooks are copied to
$TMPDIR directory and sourced by the agent's default shell. This has a few implications:
$BASH_SOURCEcontains the location the hook is sourced from.
$0contains the location of the copy of the script that is running from
If your hooks don't execute, and throw a
Permission denied error, it might mean that they were copied to a temporary directory on the agent that isn't executable. Configure the directory that hooks are copied to before execution using the
$TMPDIR environment variable on the Buildkite agent, or make sure the existing directory is marked as executable.
To write job lifecycle hooks in another programming language, you need to execute them from within the shell script, and explicitly pass any Buildkite environment variables you need to the script when you call it.
The following is an example of an
environment hook which exports a GitHub API key for the pipeline's release build step:
echo '--- :house_with_garden: Setting up the environment'
Buildkite defaults to using the Batch shell on Windows. Buildkite agents running on Windows require that either:
- The hooks files have a
.batextension, and be written in Windows Batch, or
- The agent
shelloption points to the PowerShell or PowerShell Core executable, and the hooks files are written in PowerShell. PowerShell hooks are supported in Buildkite agent version 3.32.3 and above.
An example of a Windows
ECHO "--- :house_with_garden: Setting up the environment"