Use a locked RUN cache between builds in BuildKit

TL;DR

This post provides hands-on tutorial on how to use a locked RUN cache that is defined by RUN –mount=type=cache, sharing=locked in Dockerfile. Here is a snippet of Dockerfile.

RUN --mount=type=cache,id=locked-cache,target=/root/.cache,sharing=locked \
  echo $(date):" locked1 Locked" && \ 
  echo $(date)": Hello from locked1" >> /root/.cache/locked.log && \
  sleep 10 && \
  echo "--- locked.log ---------" && \
  cat /root/.cache/locked.log && \
  echo "------------------------" && \
  echo $(date):" locked1 Unlocked" 
A locked cache

RUN cache has three sharing types (shared, private and locked) . In the previous post, I shared how to use a RUN cache which type is shared. In this post, we will try to use a locked type.

  1. Clone the repository from GitHub
  2. Create a RUN cache
  3. About locking the cache
  4. Check the locking behavior
    1. Notes: about the command
  5. Wrap up

Clone the repository from GitHub

I already prepared contents for hands-on, so please clone it from my repository named buildkit101.

git clone https://github.com/yukinakanaka/buildkit-101.git

Then, move to the following directory.

cd buildkit-101/run-cache/locked 

Create a RUN cache

Create a RUN cache

Dockerfile.locked1 contains RUN –mount=type=cache,id=locked-cache,target=/root/.cache,sharing=locked. It will create a RUN cache during building a DockerImage.

$ cat Dockerfile.locked1
FROM debian:12.4-slim
RUN mkdir -p /root/.cache
COPY rebuild-trigger .

RUN date
RUN --mount=type=cache,id=locked-cache,target=/root/.cache,sharing=locked \
  echo $(date):" locked1 Locked" && \ 
  echo $(date)": Hello from locked1" >> /root/.cache/locked.log && \
  sleep 10 && \
  echo "--- locked.log ---------" && \
  cat /root/.cache/locked.log && \
  echo "------------------------" && \
  echo $(date):" locked1 Unlocked" 

RUN date

Let’s build a DockerImage from Dockerfile.locked1!

docker build --progress=plain -f Dockerfile.locked1 .

In the build log, you can see that the build process wrote a log in /root/.cache/locked.log. The file locked.log was stored in a cache named locked-cache!

#9 [stage-0 5/6] RUN --mount=type=cache,id=locked-cache,target=/root/.cache,sharing=locked   echo $(date):" locked1 Locked" &&   echo $(date)": Hello from locked1" >> /root/.cache/locked.log &&   sleep 10 &&   echo "--- locked.log ---------" &&   cat /root/.cache/locked.log &&   echo "------------------------" &&   echo $(date):" locked1 Unlocked"
#9 0.277 Fri Mar 8 02:43:42 UTC 2024: locked1 Locked
#9 10.28 --- locked.log ---------
#9 10.28 Fri Mar 8 02:43:42 UTC 2024: Hello from locked1
#9 10.28 ------------------------
#9 10.28 Fri Mar 8 02:43:52 UTC 2024: locked1 Unlocked
#9 DONE 10.3s

About locking the cache

During the execution of the RUN command, the build process locks the cache. This means that other build processes cannot access it and will wait until it’s unlocked. In this blog’s sample, the RUN command in the Dockerfile has sleep 10, so it locks the cache for about 10 seconds.

Lock the cache

Check the locking behavior

Let’s check locking behavior by running two build processes at the same time!

echo $RANDOM > rebuild-trigger
docker build --progress=plain -f Dockerfile.locked1 . > locked1.log 2>&1 &
sleep 1
docker build --progress=plain -f Dockerfile.locked2 .

Please check the logs in locked.log. You can see that the build process of Dockerfile.locked2 waits 10 seconds until the build process of Dockerfile.locked1 unlocked the cache!

#9 20.38 --- locked.log ---------
#9 20.38 Fri Mar 8 02:43:42 UTC 2024: Hello from locked1
#9 20.38 Fri Mar 8 02:45:17 UTC 2024: Hello from locked1 <<< locked1 locked the cache for 10 seconds
#9 20.38 Fri Mar 8 02:45:27 UTC 2024: Hello from locked2 < locked2 locked the cache after locked1 unlock the cache
#9 20.38 ------------------------

Notes: about the command

  • echo $RANDOM > rebuild-trigger

Changed the contents of the file named rebuild-trigger to prevent Docker from skipping builds due to layer caching.

  • docker build –progress=plain -f Dockerfile.locked1 . > locked1.log 2>&1 &

Build the image from Dockerfile.locked1 in the background.

  • sleep 1

Wait one second so that the build process of Dockerfile.locked1 locks the cache before the build process of Dockerfile.locked2 accesses the cache.

  • docker build –progress=plain -f Dockerfile.locked2 .

Build the image from Dockerfile.locked2 in the foreground.

Wrap up

In this post, I explained the behavior locked-type RUN cache between builds. 🐳

Please check bellow if you’re interested in other type of cache.