Do I understand btrfs compression correctly?

I missed the part about compression LEVEL indeed :+1:
Ahhh, but the OP is using the default level anyhow :wink:

BTRFS is still a WIP (Like most bleeding-edge technologies), this could be improved to allow per subvolume settings ofcourse to choose compression levels independently
Maybe someone already proposed it to the devs of BTRFS who knows…
Anyhow it is getting better and better over time…

Thanks for updating my personal info about this aspect :+1:

The higher compression level would be nice for the archive subvolume but it’s not a deal breaker.

How can I verify that the subvolume is actually used and persistet over reboots? The show command doesn’t list a parent, but it should, right?

And it’s a subvolume inside a subvolume. How does it know it should be mounted?

It is always being used, because to userspace, there is no difference between the subvolume and its directory name. As soon as you put a file in the directory, you are writing to the subvolume.

Also, if it’s listed in /etc/fstab, then an explicit mount of a subvolume will survive a reboot. Do however note that subvolumes are not block devices, and that therefore you have to use the UUID of the main btrfs filesystem. The explicit mount of a subvolume is done via the subvol and/or subvolid mount options — see… :arrow_down:

man mount

… for details.

I’m not currently using any nested subvolumes — I have distinct partitions that each have their own dedicated and independent btrfs filesystem on them — but the UNIX-standard mount command should list the explicit subvolume mounts. :arrow_down:

mount | grep btrfs

A nested subvolume and its contents are always visible and accessible when the parent is mounted, but in order to mount the subvolume with differing mount options — e.g. read-only — it must be explicitly mounted via /etc/fstab, or if you will, manually from the command line.

Thanks, I’ll experiment with this. The subvolume didn’t show up in the mount command.
Probably, I’ll have to revert switching to systemd-homed and put it manually into fstab.

Then it’s not explicitly mounted, because a nested subvolume, when explicitly mounted, is actually a kind of bind-mount.

When systemd-homed was released as a systemd add-on, I looked up at the sky and whispered “Lord, please let this chalice pass me by”, and I’m not even religious. :laughing:

fstab has some limit.

You can use systemd-mount. There are mount-points in systemd list: systemd-mount --list

You can edit the systemd-mount of systemd-homed if it exists.

systemctl edit --full [systemd-homed-mount]

[Unit]
After=[systemd-homed-login.mount?]

[Mount]
What=/dev/mapper/...
Where=/home/[user]
Type=btrfs
#here, you can config these mount options
Options=subvol=/[@user],noatime,compress=zstd:9 

Maybe it would help.

See:

1 Like

An important point to keep in mind is that compression managed via mount applies to the whole filesystem, not the individual subvolume. Essentially, this means that which subvolume is mounted first will be the one that controls the compression applied. This will usually be the subvolume mounted at /. You can easily check this with the mount or findmnt commands after booting. You should see they all have the same compression type set.

It makes no difference if you use fstab, a systemd mount unit or the mount command.

Another important thing to note is that compression is only applied to future changes so if you converted an ext4 fs to btrfs, none of that data will be compressed.

You can use the btrfs filesystem defrag command to compress existing files though.

3 Likes

I wouldn’t recommend that on an SSD, though. On the other hand, on a spinning HDD it’s all fine.

Since we are referring to a one-time event to compress the data, there is little reason not to apply to an SSD. The lifespan of most SSDs won’t be materially impacted by that. Especially since the compression won’t be applied to files where the compression wouldn’t yield results which will include most larger files that are likely to be found on a desktop.

1 Like

You could view the BTRFS filesystem as an hard disk with a partition table, where the root filesystem is the primary partition and sub-volumes are the extra partitions.

The BTRFS filesystem allows you to access all directory tables in all partitions without the need to mount the partitions separately, except for the primary partition which you mount at the time you mount the BTRFS filesystem.

Because of this you are able to access every directory and file in the whole BTRFS filesytem while the needed info of some directories and files are kept in a different table (partition) transparent to the user.

Thank you all for your valuable input.

The current status is, I still use systemd-homed and when it detects that the /home partition is btrfs, it will automatically create a subvolume (for each user, I only have one) and mount the directory /home/username.homedir into /home/username.
This is a directory mount, systemd-homed could also mount a luks or differently encrypted image file which makes more sense than a folder mount.

So, it create a ephemeral mount home-username.mount which does exactly this.
It also shows up in /proc/mounts:

grep home /proc/mounts

/dev/mapper/crypthome /home btrfs rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/ 0 0
/dev/mapper/crypthome /home/username btrfs rw,nosuid,nodev,noatime,idmapped,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/ 0 0

The options in the first row are set by me, the second ones by systemd. There is no home-username.mount file anywhere. I can only see the specifics with systemctl show home-username.mount.

Then, the mystery begins.

We have output:

sudo btrfs subvolume show /home
/
	Name: 			<FS_TREE>
	UUID: 			00487190-dd99-4034-b8c2-01170924c98e
	Parent UUID: 		-
	Received UUID: 		-
	Creation time: 		2023-03-03 13:23:50 +0100
	Subvolume ID: 		5
	Generation: 		935
	Gen at creation: 	0
	Parent ID: 		0
	Top level ID: 		0
	Flags: 			-
	Send transid: 		0
	Send time: 		2023-03-03 13:23:50 +0100
	Receive transid: 	0
	Receive time: 		-
	Snapshot(s):

But for the user’s supposed subvolume:

$ sudo btrfs subvolume show /home/username
ERROR: Not a Btrfs subvolume: Invalid argument

However, the subvolume for the archived projects from a few posts before is there:

$ sudo btrfs subvolume show /home/username/archives
username.homedir/archives
	Name:            archives
	UUID:            444be83c-5670-0e41-84f1-c91f2d25e4f8
	Parent UUID:     -
	Received UUID:   -
	Creation time: 	2023-03-03 21:01:41 +0100
	Subvolume ID: 	256
	Generation: 		901
	Gen at creation: 	877
	Parent ID: 		5
	Top level ID: 	5
	Flags: 			-
	Send transid: 	0
	Send time: 		2023-03-03 21:01:41 +0100
	Receive transid: 	0
	Receive time:     -
	Snapshot(s):

But I can’t find any information about the mount info, it’s not a systemd-unit or in /proc/mounts.

As opposed to the discussion above, I think I was able to set the compression level of this subvolume with
$ sudo btrfs property set /home/username/archives compression zstd:9
Which I can verify with:

$ sudo btrfs property get /home/username/archives compression
compression=zstd:9

However, I don’t know if this is overridden by the parent mount or an invalid option.

I did some tests and a 100MB file from here and compressed it with the levels 1, 3, 8, 12, and 19 on a tmpfs:

100.000.000 100M
 40.678.709 100M-L1.zst
 37.372.605 100M-L2.zst
 35.487.160 100M-L3.zst
 31.607.256 100M-L8.zst
 30.413.324 100M-L12.zst
 26.954.633 100M-L19.zst

And to check on btrfs, I copied the file to the /home/username/archives subvolume and run compsize:

Processed 1 file, 763 regular extents (763 refs), 0 inline.
Type       Perc     Disk Usage   Uncompressed Referenced  
TOTAL       39%     39960576     100003840    100003840   
zstd        39%     39960576     100003840    100003840   

From what I see, it’s closest to compression level 1 when looking closely on the bytes.

So, it is smaller than level 1 but larger than level 2. Shouldn’t it be closer to level 3 if we can cut some slack on overhead? But the property that I’ve set above with using level 9 is surely not applied.

Try this instead:

systemctl status /home/username

It should show you how systemd mounted it, to see the actual config you could use cat instead of status.

$ sudo systemctl status /home/username                                              
● home-username.mount - /home/username
     Loaded: loaded (/proc/self/mountinfo)
     Active: active (mounted) since Mon 2023-03-06 09:17:41 CET; 3h 45min ago
      Where: /home/username
       What: /dev/mapper/crypthome

For cat: No files found for home-username.mount.

The info is there with

systemctl show /home/username
Where=/home/username
What=/dev/mapper/crypthome
Options=rw,nosuid,nodev,noatime,idmapped,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
Type=btrfs
TimeoutUSec=1min 30s
ControlPID=0
DirectoryMode=0755
SloppyOptions=no
LazyUnmount=no
ForceUnmount=no
ReadWriteOnly=no
Result=success
UID=[not set]
GID=[not set]
Slice=system.slice
[...]
Id=home-username.mount
Names=home-username.mount
Requires=dev-mapper-crypthome.device home.mount -.mount system.slice
Conflicts=umount.target
Before=local-fs.target session-2.scope umount.target
After=local-fs-pre.target systemd-journald.socket blockdev@dev-mapper-crypthome.target system.slice home.mount -.mount dev-mapper-crypthome.device
RequiresMountsFor=/home
Description=/home/username
LoadState=loaded
ActiveState=active
FreezerState=running
SubState=mounted
SourcePath=/proc/self/mountinfo
StateChangeTimestamp=Mon 2023-03-06 09:17:41 CET
StateChangeTimestampMonotonic=67906042
InactiveExitTimestamp=Mon 2023-03-06 09:17:41 CET
InactiveExitTimestampMonotonic=67906042
ActiveEnterTimestamp=Mon 2023-03-06 09:17:41 CET
ActiveEnterTimestampMonotonic=67906042
ActiveExitTimestampMonotonic=0
InactiveEnterTimestampMonotonic=0
CanStart=yes
CanStop=yes
CanReload=yes
CanIsolate=no
CanFreeze=no
StopWhenUnneeded=no
RefuseManualStart=no
RefuseManualStop=no
AllowIsolate=no
DefaultDependencies=yes
OnSuccessJobMode=fail
OnFailureJobMode=replace
IgnoreOnIsolate=yes
NeedDaemonReload=no
JobTimeoutUSec=infinity
JobRunningTimeoutUSec=infinity
JobTimeoutAction=none
ConditionResult=no
AssertResult=no
ConditionTimestampMonotonic=0
AssertTimestampMonotonic=0
Transient=no
Perpetual=no
StartLimitIntervalUSec=10s
StartLimitBurst=5
StartLimitAction=none
FailureAction=none
SuccessAction=none
InvocationID=d890471d826e44e9a6b04b394223e61f
CollectMode=inactive

But this doesn’t show any btrfs information. It could be anything (luks volume, ecryptfs or whatever else systemd supports.).

Please be aware, that btrfs will NOT compress a file as one block of data, but will break it up in several small chunks. This may affect compression ratio. This is visible if you look onto fragmentation of a compressed file.

Level 3 is very good. I use Level 9 overall and have better then 2:1 over all files.

Sure, thanks, I’ll plan on using something between 8 and 12, however, currently I don’t even know if it’s using compression and of so, which level and where it’s configured.
The test would indicate that it’s using the default level 3.

There is no way to set this or even check which one is used.

mount |grep -E btrfs
/dev/sda2 on / type btrfs (rw,noatime,compress=zstd:9,ssd,space_cache,commit=300,subvolid=36033,subvol=/@

Read the docs of the developers It is what it says :wink:

1 Like

But the subvolume is not mounted by me or ay unit I can find. It seems it’s transient (?). It’s not listed in the mounts.

The sentence is confusing, the compression level can be set during mounting the subvolume but is shared. So which one is it?

Edit:
Okay, so the subvolume can’t be mounted by name, however, with subvolid it can be mounted:

sudo mount -o subvolid=256,compression=zstd:9,noatime /dev/mapper/crypthome archives

but the compression level is not applied:

$ grep archives /proc/self/mounts
/dev/mapper/crypthome /home/username.homedir/archives btrfs rw,noatime,compress=zstd:3,ssd,discard=async,space_cache=v2,subvolid=256,subvol=/username.homedir/archives 0 0

:disappointed_relieved:

So, I can understand the docs: the compression level can be set during mount (can as it can be set without failing), however, it is then overwritten by the parent volume (what they weirdly call shared.)

I only found a few posts from 10-12 years ago complaining about this, but they all end in a “they are working on it” and “it works now” (the latter is probably falling for the same logic that it doesn’t error out when setting the level) and the first one is from their wiki

Can I set compression per-subvolume?
Currently no, this is planned. You can simulate this by enabling compression on the subvolume directory and the files/directories will inherit the compression flag.

So, @andreas85’s idea of differently compressed subvolumes doesn’t work.

1 Like

So bear with me here, I have an idea.

I have mounted /home in fstab and a mysterious subvolume to /home/username and then my archived subvolume. So far so good.

From a few experiments, I would very much have the archive subvolume at zstd:12. I almost never write to it (once every few months, I can wait for the copy, no problem) and reading doesn’t need to be realtime.

As the root volume ( here /home) is mounted with zstd:3, couldn’t I “just” for copying files to the archive remount the full volume? In TTY, this should be relatively easy, right?

# On TTY1
sudo umount /home
sudo mount -o defaults,noatime,compress=zstd:12 /dev/mapper/crypthome /home
# On TTY2
rsync -av --info=progress2 /home/username/project/a /home/username/archive/
logout
# On TTY1
sudo umount /home
sudo mount -a

Or am I totall out of place?

I’m not sure if you are able to unmount /home while loged into a normal user (non-root) in the GUI, because of some files keeping that mount bussy.

You could try it on the console yea maybe…

did i say something funny? :joy: :woman_shrugging:
ahhh TTY = console right? :woman_facepalming: