Malte Krupa


Filesystem Diffs Via ZFS Snapshots - 2022-09-11

The issue

Today I needed a to find a way to run a freebsd-update command exactly once on a certain jail.

Background

Let’s take a step back shortly to understand why I need this.

Previously the process to build jails looked as follows:

Do once:

Do for every new jail:

This felt tedious and seemed like something that can be improved. Since ZFS supports very fast and supposedly lightweight snapshots I figured they could be the right tool. After a quick search I found a blog post called “FreeBSD jails the hard way” which describes exactly this scenario. Funny thing: This post is from 2015 and still works like a charm. Do you remember the last time a seven year old blog post helped you solve an issue with kubernetes? It was probably seven years ago.

After a short refactoring I was able to change the process to this:

Do once:

Do for every new jail:

Check the commit if you’re interested in the details..

The solution

And since I do not want to run the slow freebsd-update command on every invocation of ansible, I needed to find a way to only run it once.

This command obviously changes files on the file system. To find these files, I made use of ZFS snapshots and the diff functionality of ZFS.

It’s as easy as it sounds.

  1. Create snapshot of dataset
  2. Run command that alters files
  3. Create another snapshot of the same dataset
  4. Run zfs diff snapshot1 snapshot2 to get a diff

And of course, after searching through the list of files that were changed, I found this thing:

root@w0:~ # zfs diff zroot/jails/test2/root@pre-install zroot/jails/test2/root@post-install
[..]
+       /usr/local/jails/test2/root/boot/kernel.old/.freebsd-update
[..]

The + at the beginning of the line suggests that this is a file that is present in the second but not the first snapshot. A perfect candidate for our task.

Finally, the ansible task to run the update looks as follows:

- name: Update
  ansible.builtin.command:
    cmd: "freebsd-update -b /usr/local/jails/{{ jails_version }}/root fetch install --not-running-from-cron"
    creates: "/usr/local/jails/{{ jails_version }}/root/boot/kernel.old/.freebsd-update"

Source on github

Someone on the FreeBSD Discord recently mentioned that they’re making use of ZFS deduplication for all of their full-jails. While I do not really want to go the route of using deduplication yet I liked the idea to save a lot of space when having multiple jails. Maybe on another day.


Privacy Policy | Imprint