BundleWrap's decentralized nature makes it necessary to coordinate actions between users of a shared repository. Locking is an important part of collaborating using BundleWrap.

Hard locks

Since very early in the history of BundleWrap, what we call "hard locks" were used to prevent multiple users from using bw apply on the same node at the same time. When BundleWrap finds a hard lock on a node in interactive mode, it will display information about who acquired the lock (and when) and will ask whether to ignore the lock or abort the process. In non-interactive mode, the operation is always cancelled for the node in question unless --force is used.

Soft locks

Many teams these days are using a workflow based on pull requests. A common problem here is that changes from a feature branch might already have been applied to a set of nodes, while the master branch is still lacking these changes. While the pull request is open and waiting for review, other users might rightly use the master branch to apply to all nodes, reverting changes made by the feature branch. This can be a major nuisance.

As of version 2.6.0, BundleWrap provides "soft locks" to prevent this. The author of a feature branch can now lock the node so only they can use bw apply on it:

$ bw lock add node1
✓ node1  locked with ID B9JS (expires in 8h)

This will prevent all other users from changing any items on the node for the next 8 hours. BundleWrap will tell users apart by their BW_IDENTITY. Now say someone else is reviewing the pull request and wants to use bw apply, while still keeping others out and the original author in. This can be done by simply locking the node again as the reviewer. Nodes can have many soft locks. Soft locks act as an exemption from a general ban on changing items that goes into effect as soon as one or more soft locks are present on the node. Of course, if no soft locks are present, anyone can change any item.

You can list all soft locks on a node with:

$ bw lock show node1
i node1  ID    Created              Expires              User   Items  Comment
› node1  Y1KD  2016-05-25 21:30:25  2016-05-26 05:30:25  alice  *      locks are awesome
› node1  B9JS  2016-05-24 13:10:11  2016-05-27 08:10:11  bob    *      me too

Note that each lock is identified by a case-insensitive 4-character ID that can be used to remove the lock:

$ bw lock remove node1 y1kd
✓ node1  lock Y1KD removed

Expired locks are automatically and silently purged whenever BundleWrap has the opportunity. Be sure to check out bw lock add --help for how to customize expiration time, add a short comment explaining the reason for the lock, or lock only certain items. Using bw apply on a soft locked node is not an error and affected items will simply be skipped.

Locking non-UNIX nodes

Most of the time, BundleWrap assumes that your target system is a UNIX-like operating system. It then stores locks as files in the node's local file system (/var/lib/bundlewrap by default).

BundleWrap supports managing non-UNIX nodes, too, such as Kubernetes. You can also write your own custom item types to manage hardware. In those situations, BundleWrap has no place to store lock files.

You can solve this by designating another regular UNIX node as a "locking node":

nodes['my.k8s.cluster'] = {
    'locking_node': 'my.openbsd.box',
    'os': 'kubernetes',
    'metadata': {

my.openbsd.box is the name of another regular node, which must be managed by BundleWrap. You can now use all the usual locking mechanisms when working with my.k8s.cluster and its locks will be stored on my.openbsd.box. (They will, of course, not conflict with regular locks for my.openbsd.box.)

A locking node can host locks for as many other nodes as you wish.