Resizing a systemd-homed Btrfs Image & Fixing Slow LUKS Logins

PURPOSE

This started as a seemingly straightforward task: resizing my systemd-homed LUKS-encrypted Btrfs home directory. However, my laptop drive didn't have enough free space to handle the resize operation locally. I couldn't even change simple parameters like --luks-discard since there wasn't enough free space (more on why I was trying that below). I had to migrate the image to a home server, resize it there, and bring it back.

What followed was a cascade of complex filesystem quirks. First, transferring the image back resulted in heavy fragmentation unless strict NoCOW (+C) attributes and rsync --inplace were used. Next, systemd-homed's ID-mapping security feature collided with my nested Btrfs subvolumes (like ~/.cache), resulting in files mysteriously owned by nobody (UID 65534) and breaking terminal emulators like Alacritty.

Finally, after successfully fixing the permissions, I was still experiencing a heavily delayed login into Hyprland after entering my password. The true culprit? Online LUKS Discard (TRIM) thrashing the disk queue through multiple abstraction layers. This post documents the exact procedure to safely manage this migration, fix the ID-mapping fallout, and restore instant login speeds.

PREREQUISITES

For the procedure below to apply, your setup should match the following conditions:

  • You are using systemd-homed to manage a .home image.
  • The image uses LUKS encryption and a Btrfs filesystem.
  • The image is too large to be resized in-place on the local machine, requiring a transfer to another machine.
  • You utilize nested Btrfs subvolumes within your home directory (e.g., ~/.cache).
  • Crucially: You are experiencing massive I/O stalls and slow login times caused by online LUKS Discard being enabled on the systemd-homed container.

PROCEDURE

Diagnostic Checks

Before proceeding, verify if you are suffering from the UID mismatch or the LUKS Discard issue.

  • The UID Bug: Run find ~/.cache -uid 65534 2>/dev/null. If this returns a list of files, the kernel's Virtual File System has rejected the UID of those files and assigned them to nobody.
  • The Discard Bug: If your display manager hangs for several seconds after successfully accepting your password before your compositor (e.g., Hyprland) starts, you likely have online discard enabled.

Step 1: Prepare the Server and Transfer Out

If the server is also running Btrfs, you must ensure the destination directory has Copy-On-Write disabled before the image lands, or the resize operation will crawl.

On the Server, create the holding directory and disable CoW:

mkdir -p /data/backup/hosts/laptop
chattr +C /data/backup/hosts/laptop

On the Laptop, ensure your user is logged out (deactivated) and transfer the image:

homectl deactivate username
rsync -av --progress /home/username.home root@server:/data/backup/hosts/laptop/

Step 2: Transfer the Local Keys

systemd-homed cannot resize an image if it only possesses the foreign key on the server. You must temporarily move your laptop's local keys to the server to authorize the operation.

On the Server, back up any existing local keys and stop the service:

systemctl stop systemd-homed
mv /var/lib/systemd/home/local.private /var/lib/systemd/home/local.private.bak$(date +%F)
mv /var/lib/systemd/home/local.public /var/lib/systemd/home/local.public.bak$(date +%F)

On the Laptop, push your keys to the server:

rsync -av /var/lib/systemd/home/local.* root@server:/var/lib/systemd/home/

On the Server, restart the service to load the laptop's keys:

systemctl start systemd-homed

Step 3: The Resize Operation

Once the image and keys are safely on the server, you can leverage the server's free space to expand the LUKS container and internal filesystem.

On the Server, execute the resize:

# Execute the resize (e.g., to 500G)
homectl resize username 500G

(Note: You will be prompted for the user's encryption password).


Step 4: Prepare the Laptop and Transfer Back

Now that the server has successfully resized the image, it is safe to clear the local drive and bring the expanded image home. This requires strict NoCOW preparation to prevent massive fragmentation.

On the Laptop, delete the original image and create an empty target:

rm -f /home/username.home
touch /home/username.home

Disable Copy-On-Write on the empty file before transferring data:

chattr +C /home/username.home

Transfer the image back using rsync with the --inplace flag:

rsync -av --inplace --progress root@server:/data/backup/hosts/laptop/username.home /home/username.home
WARNING: If you skip the --inplace flag, rsync will create a hidden temporary file that does not inherit the +C NoCOW attribute, resulting in thousands of fragmented extents and terrible performance.

Step 5: Fixing the Subvolume nobody Mismatch

Because systemd-homed uses ID-mapped mounts, files transferred back from the server may fall outside the expected UID map. To fix this, we must bypass systemd-homed and mount the raw filesystem.

Open the LUKS container manually:

losetup -fP --show /home/username.home  # Note the output, e.g., /dev/loop0
cryptsetup open /dev/loop0p1 rescue_home

Mount the root filesystem and all nested subvolumes:

mount /dev/mapper/rescue_home /mnt
mount -o subvol=.cache /dev/mapper/rescue_home /mnt/.cache
# Repeat for any other subvolumes like Downloads, etc.
CAUTION: If you forget to explicitly mount your nested subvolumes (like .cache), the chown command will only apply to the empty mount point directory, and the files inside the subvolume will remain owned by nobody.

Execute a targeted, recursive chown to restore permissions based on the root mount, carefully ignoring read-only snapshots:

find /mnt -path '*/\.snapshots*' -prune -o -uid 65534 -exec chown --reference=/mnt {} +

Cleanly unmount and close the container:

umount -R /mnt
cryptsetup close rescue_home
losetup -d /dev/loop0
homectl activate username

Step 6: Flush Corrupted Caches

If terminal emulators like Alacritty are rendering invisible text or dynamic shell prompts are halting, the caches generated while permissions were broken are likely corrupted.

Clear and rebuild the caches as your standard user:

rm -rf ~/.cache/fontconfig ~/.cache/alacritty
fc-cache -r

Step 7: Disable Online LUKS Discard for Instant Logins

Passing synchronous TRIM commands through a LUKS container, down into a loopback device, and into a host Btrfs filesystem causes severe I/O stalls during decryption.

Update your systemd-homed record to disable online discard:

homectl update username --enforce-password-policy=no --luks-discard=false

Reboot your system.

Upon your next login, the decryption overhead drops dramatically, returning your session to an instant, zero-delay launch.

systemd-homed resize (out of space)