Wrapper around pkg(8) to automatically mount pkg cache in tmpfs and create a ZFS boot environment

I'm experimenting with this wrapper around pkg(8) to automatically mount the pkg cache in tmpfs and create a ZFS boot environment. The reason for using tmpfs is to prevent a eMMC/SSD from wearing. Any suggestions for improvements are welcome.

Code:
#!/bin/sh
#
# Wrapper around FreeBSD pkg(8) to create a ZFS Boot Environment
# and mount /var/cache/pkg in tmpfs before pkg install|upgrade

cmd=
opts=

while [ $# -gt 0 ] ; do
    case "$1" in
    -*)
        opts="$opts $1"
        shift ;;
    *)
        cmd="$1"
        break ;;
    esac
done

case "$cmd" in
    install|upgrade)
        shift ;;
    *)
        exec pkg $opts "$cmd" "$@" ;;
esac

size=$(pkg $opts "$cmd" -Fn "$@" | awk '/to be downloaded/ { printf "%s%c", $1 + 10, substr($2, 0, 1) }')
[ -z "$size" ] && exit 0

mount -v -t tmpfs -o size="$size" tmpfs /var/cache/pkg || exit 1

cleanup () {
    umount -v /var/cache/pkg
    exit "${1:-1}"
}

trap cleanup HUP QUIT INT

rootfs=$(mount -p | awk '$2 == "/" { print $3 }')

if [ "$rootfs" = "zfs" ] ; then
    bectl create -r default@"$(date +'%Y-%m-%d_%H%M%S')"
fi

pkg $opts "$cmd" "$@"
cleanup $?
 
I recently compiled 2500 packages from source. SSD is still alive :)
I know modern SSD's resist a bit more but I dunno about eMMC's. It's also faster to download to RAM. I'm even tempted to symlink /var/cache/pkg to /tmp which is already tmpfs in my case but want to do it in a secure way.
 
My opinions:
Your intent is to create a BE that represents the system BEFORE applying the pkg install/upgrade command?
/var/cache/pkg holds the downloaded pkg so having it as tmpfs is equivalent to running pkg clean -ay after every command, so that's a nice trick.
Just keep in mind BE propagation; you run this script a bunch of times and do bectl list and go "where did these 50 BEs come from".
New BE on install of one or two packages you wind up with lots of BEs with small deltas, so maybe only a new BE on doing pkg upgrade? That kind of mirrors your thoughts about /var/cache/pkg on eMMC (lots of new BEs on eMMC?)

Think about following quarterly, do you want to create a new BE everytime to pick up a new version of FireFox or create a new BE every quarter so you can roll back to working system?
Thinking on what you want, you could tweak to only create new BE on upgrade, that way install would still have the tmpfs advantage and you only create a new BE on significant events.

But other than that, neat idea.
 
I think a new boot environment is only usefull when there is a major upgrade of the version of the O.S.
I think it depends, which is why I pointed it out.
If you follow quarterly? Make a new one before doing upgrade so you can rollback.
If you are upgrade across releases like 14.x to 15.x? Make a new BE and do the OS and pkg upgrade into it.

But only rbranco can answer "what makes sense to him".
 
My opinions:
Your intent is to create a BE that represents the system BEFORE applying the pkg install/upgrade command?
/var/cache/pkg holds the downloaded pkg so having it as tmpfs is equivalent to running pkg clean -ay after every command, so that's a nice trick.
Just keep in mind BE propagation; you run this script a bunch of times and do bectl list and go "where did these 50 BEs come from".
New BE on install of one or two packages you wind up with lots of BEs with small deltas, so maybe only a new BE on doing pkg upgrade? That kind of mirrors your thoughts about /var/cache/pkg on eMMC (lots of new BEs on eMMC?)

Think about following quarterly, do you want to create a new BE everytime to pick up a new version of FireFox or create a new BE every quarter so you can roll back to working system?
Thinking on what you want, you could tweak to only create new BE on upgrade, that way install would still have the tmpfs advantage and you only create a new BE on significant events.

To avoid reinventing the wheel, there are the 3 workflows I know of:

1. Illumos pkg creates a new BE by default except when installing new packages.
2. openSUSE's zypper creates a Btrfs snapshot everytime, even when removing packages, keeping a configurable maximum of 10 snapshots.
3. GhostBSD which is what I'm trying to mimic here.

As ZFS snapshots are "cheap", the user must do the housekeeping, which is what you must do on Illumos anyway removing older snapshots when the disk is full.
 
I follow quarterly , am on zfs , have one boot environment. OK .
Never had any problem upgrading packages.
Well , upgrading major O.S. versions had me for a problem.
Used usb image to boot and fixed it in 5 minutes.
Don't understand me wrong boot environments are a nice feature. But don't overuse them.
 
To avoid reinventing the wheel, there are the 3 workflows I know of:

1. Illumos pkg creates a new BE by default except when installing new packages.
2. openSUSE's zypper creates a Btrfs snapshot everytime, even when removing packages, keeping a configurable maximum of 10 snapshots.
3. GhostBSD which is what I'm trying to mimic here.

As ZFS snapshots are "cheap", the user must do the housekeeping, which is what you must do on Illumos anyway removing older snapshots when the disk is full.
Yep, sounds like you're aware of potential pitfalls. Snapshots/BEs are cheap until they're not :) Lots of threads here about running out of space on a device and the poster shows bectl list with BEs from 12.x to 14.x (obviously from running freebsd-update)
 
I wrote a script that allows me to upgrade packages only in BE but not for the same reason than rbranco , for me it's mainly to avoid bad things to happen while upgrading, that way on my server any "pkg upgrade" goes through boot environment.
Code:
me@myserver > bectl list -C creation
BE                                Active Mountpoint Space Created
pkg_upgrade_28-12-2025_030147     NR     /          6.39G 2025-12-28 03:01
pkg_upgrade_27-12-2025_015547     -      -          45.9M 2025-12-27 01:55
14.3-RELEASE-p6_2025-12-18_021200 -      -          47.3M 2025-12-18 02:12
...

I like the idea of reducing IO though, in case one day I need that too I'll follow the thread, thanks.
 
Back
Top