buildkite-agent lock
The Buildkite Agent's lock
subcommands provide the ability to coordinate multiple concurrent builds on the same host that access shared resources.
The agent-api experiment must be enabled to use the lock
command. To enable the agent-api experiment, include the --experiment=agent-api
flag when starting the agent, or add experiment="agent-api"
to your agent configuration file.
With the lock
command, processes can acquire and release a lock using the acquire
and release
subcommands. For the special case of performing setup once for the life of the agent (and waiting until it is complete), there are the do
and done
subcommands. These provide an alternative to using flock
or OS-dependent locking mechanisms.
Each type of lock
subcommand makes use of a [key]
value, which is an arbitrary name (for example, my-key-value
) that you choose to identify your lock. A key does not reference any predefined value, and can be any name of your choosing, but it is recommended using a descriptive name that clearly indicates what resource or operation is being protected. All builds using the same lock key will coordinate with each other on the same host.
Inspecting the state of a lock
Usage
buildkite-agent lock get [key]
Description
Retrieves the value of a lock key. Any key not in use returns an empty string.
Note that this subcommand is only available when an agent has been started
with the agent-api
experiment enabled.
lock get
is generally only useful for inspecting lock state, as the value
can change concurrently. To acquire or release a lock, use lock acquire
and
lock release
.
Examples
$ buildkite-agent lock get llama
Kuzco
Options
--no-color #
|
Don't show colors in logging |
---|---|
--debug #
|
Enable debug mode. Synonym for `--log-level debug`. Takes precedence over `--log-level` |
--log-level value #
|
Set the log level for the agent, making logging more or less verbose. Defaults to notice. Allowed values are: debug, info, error, warn, fatal (default: "notice") |
--experiment value #
|
Enable experimental features within the buildkite-agent |
--profile value #
|
Enable a profiling mode, either cpu, memory, mutex or block |
--lock-scope value #
|
The scope for locks used in this operation. Currently only 'machine' scope is supported (default: "machine") |
--sockets-path value #
|
Directory where the agent will place sockets (default: "$HOME/.buildkite-agent/sockets") |
Acquiring a lock
Usage
buildkite-agent lock acquire [key]
Description
Acquires the lock for the given key. lock acquire
will wait (potentially
forever) until it can acquire the lock, if the lock is already held by
another process. If multiple processes are waiting for the same lock, there
is no ordering guarantee of which one will be given the lock next.
To prevent separate processes unlocking each other, the output from lock
acquire
should be stored, and passed to lock release
.
Note that this subcommand is only available when an agent has been started
with the agent-api
experiment enabled.
Examples
#!/usr/bin/env bash
token=$(buildkite-agent lock acquire llama)
# your critical section here...
buildkite-agent lock release llama "${token}"
Options
--no-color #
|
Don't show colors in logging |
---|---|
--debug #
|
Enable debug mode. Synonym for `--log-level debug`. Takes precedence over `--log-level` |
--log-level value #
|
Set the log level for the agent, making logging more or less verbose. Defaults to notice. Allowed values are: debug, info, error, warn, fatal (default: "notice") |
--experiment value #
|
Enable experimental features within the buildkite-agent |
--profile value #
|
Enable a profiling mode, either cpu, memory, mutex or block |
--lock-scope value #
|
The scope for locks used in this operation. Currently only 'machine' scope is supported (default: "machine") |
--sockets-path value #
|
Directory where the agent will place sockets (default: "$HOME/.buildkite-agent/sockets") |
--lock-wait-timeout value #
|
Sets a maximum duration to wait for a lock before giving up (default: 0s) |
Releasing a previously-acquired lock
Usage
buildkite-agent lock release [key] [token]
Description
Releases the lock for the given key. This should only be called by the
process that acquired the lock. To help prevent different processes unlocking
each other unintentionally, the output from lock acquire
is required as the
second argument, namely, the token
in the Usage section above.
Note that this subcommand is only available when an agent has been started
with the agent-api
experiment enabled.
Examples
#!/usr/bin/env bash
token=$(buildkite-agent lock acquire llama)
# your critical section here...
buildkite-agent lock release llama "${token}"
Options
--no-color #
|
Don't show colors in logging |
---|---|
--debug #
|
Enable debug mode. Synonym for `--log-level debug`. Takes precedence over `--log-level` |
--log-level value #
|
Set the log level for the agent, making logging more or less verbose. Defaults to notice. Allowed values are: debug, info, error, warn, fatal (default: "notice") |
--experiment value #
|
Enable experimental features within the buildkite-agent |
--profile value #
|
Enable a profiling mode, either cpu, memory, mutex or block |
--lock-scope value #
|
The scope for locks used in this operation. Currently only 'machine' scope is supported (default: "machine") |
--sockets-path value #
|
Directory where the agent will place sockets (default: "$HOME/.buildkite-agent/sockets") |
Starting a do-once section
Usage
buildkite-agent lock do [key]
Description
Begins a do-once lock. Do-once can be used by multiple processes to wait for completion of some shared work, where only one process should do the work.
Note that this subcommand is only available when an agent has been started
with the agent-api
experiment enabled.
lock do
will do one of two things:
- Print 'do'. The calling process should proceed to do the work and then
call
lock done
. - Wait until the work is marked as done (with
lock done
) and print 'done'.
If lock do
prints 'done' immediately, the work was already done.
Examples
#!/usr/bin/env bash
if [[ $(buildkite-agent lock do llama) == 'do' ]]; then
# your critical section here...
buildkite-agent lock done llama
fi
Options
--no-color #
|
Don't show colors in logging |
---|---|
--debug #
|
Enable debug mode. Synonym for `--log-level debug`. Takes precedence over `--log-level` |
--log-level value #
|
Set the log level for the agent, making logging more or less verbose. Defaults to notice. Allowed values are: debug, info, error, warn, fatal (default: "notice") |
--experiment value #
|
Enable experimental features within the buildkite-agent |
--profile value #
|
Enable a profiling mode, either cpu, memory, mutex or block |
--lock-scope value #
|
The scope for locks used in this operation. Currently only 'machine' scope is supported (default: "machine") |
--sockets-path value #
|
Directory where the agent will place sockets (default: "$HOME/.buildkite-agent/sockets") |
--lock-wait-timeout value #
|
Sets a maximum duration to wait for a lock before giving up (default: 0s) |
Completing a do-once section
Usage
buildkite-agent lock done [key]
Description
Completes a do-once lock. This should only be used by the process performing the work.
Note that this subcommand is only available when an agent has been started
with the agent-api
experiment enabled.
Examples
#!/usr/bin/env bash
if [[ $(buildkite-agent lock do llama) == 'do' ]]; then
# your critical section here...
buildkite-agent lock done llama
fi
Options
--no-color #
|
Don't show colors in logging |
---|---|
--debug #
|
Enable debug mode. Synonym for `--log-level debug`. Takes precedence over `--log-level` |
--log-level value #
|
Set the log level for the agent, making logging more or less verbose. Defaults to notice. Allowed values are: debug, info, error, warn, fatal (default: "notice") |
--experiment value #
|
Enable experimental features within the buildkite-agent |
--profile value #
|
Enable a profiling mode, either cpu, memory, mutex or block |
--lock-scope value #
|
The scope for locks used in this operation. Currently only 'machine' scope is supported (default: "machine") |
--sockets-path value #
|
Directory where the agent will place sockets (default: "$HOME/.buildkite-agent/sockets") |
Usage within a pipeline
Locks help coordinate access to shared resources when multiple agents run concurrently on the same host, such as when --spawn
is used to create multiple agents.
Coordinating sequential access
Use acquire
and release
when multiple builds need to run the same operation sequentially to prevent conflicts. Each build will execute the task, but only one at a time. This coordination works across multiple pipelines when they use the same lock key and the jobs run on the same host. Unlike do
and done
, each build still performs the workâlocks just ensure they don't interfere with each other.
Sequential locks example
In the following example, the key db-migration-lock
ensures that database migrations run sequentially across multiple builds on the same host.
steps:
- label: "Install Dependencies"
commands:
- "echo '+++ Installing dependencies'"
- "bundle install"
- "npm ci"
key: "install"
- label: "Migrate DB Schema"
commands:
- "echo '+++ Running DB migration with lock'"
- "token=$(buildkite-agent lock acquire db-migration-lock)"
- "bundle exec rake db:migrate"
- "buildkite-agent lock release db-migration-lock '$${token}'"
plugins:
- vault-secrets#v2.2.1:
server: "https://my-vault-server"
path: "data/buildkite/postgres"
auth:
method: "approle"
role-id: "my-role-id"
secret-env: "VAULT_SECRET_ID"
env:
RAILS_ENV: "development"
depends_on: "install"
key: "migrate-db"
This lock only controls access to the bundle exec rake db:migrate
process itself, and does not lock access to the vault server defined by the plugin, or any subsequent commands following the buildkite-agent lock release db-migration-lock '$${token}'
command. Only processes that occur between the lock acquire
and lock release
commands are the ones which are locked.
Multiple builds can still retrieve secrets from the vault concurrently, but only one can execute the actual database migration at a time, as long as all builds use the same lock key.
One-time locks
When running parallel jobs on the same host that need a shared setup, do
and done
ensure expensive operations happen only once. For instance, one agent performs the setup (for example, downloading datasets, generating certificates, starting services, etc.), while others wait and then proceed. This saves time and resources compared to each parallel job repeating the same work. Once marked as done
, the lock remains completed for all subsequent jobs on the host unless it is restarted.
One-time locks example
In the following example, they key test-env-setup
ensures that the test environment setup happens only once across multiple parallel jobs on the same host.
steps:
- label: "Install Dependencies"
commands:
- "echo '+++ Installing dependencies'"
- "bundle install"
- "npm ci"
key: "install"
- label: "Setup Test Environment"
command: "setup_test.sh"
depends_on: "install"
key: "prep"
parallelism: 5
- label: "Run Tests"
commands:
- "echo '+++ Running tests'"
- "bundle exec rspec"
depends_on: "prep"
parallelism: 10
#!/usr/bin/env bash
echo "+++ Setting up shared test environment"
if [[ $(buildkite-agent lock do test-env-setup) == 'do' ]]; then
echo "Downloading assets..."
curl -o /tmp/test-data.zip https://releases.example.com/data.zip
unzip /tmp/test-data.zip -d /tmp/shared-test-files/
buildkite-agent lock done test-env-setup
else
echo "Assets have already been pulled and unarchived"
fi
The first job to reach the buildkite-agent lock do test-env-setup
command receives a response of do
and executes the setup work (downloading and extracting test data). All other parallel jobs will wait and then receive a response of done
. These jobs will skip the if
statement in this example bash script and output Assets have already been pulled and unarchived
.
Unlike the acquire
/release
pattern, this lock is performed only once and subsequent jobs benefit from the completed work without repeating it.