BTRFS-SUBVOLUME(8)
โจ btrfs-subvolume(8) โจ
okay bestie, pull up a chair because we're talking about btrfs-subvolume from the btrfs manual, section 8 ๐ this whole page is about managing btrfs subvolumes and honestly? it's giving main character energy ๐
๐ NAME ๐
btrfs-subvolume โ manage btrfs subvolumes. yes, that's the whole name. short, sweet, iconic.
๐ธ SYNOPSIS ๐ธ
btrfs subvolume <subcommand> [<args>]
that's it. you give it a subcommand, you hand it some args, and it does the thing ๐
๐ฆ DESCRIPTION ๐ฆ
so btrfs subvolume is the tool you use to create, delete, list and show btrfs subvolumes and snapshots. literally its whole job, no cap.
now, what even is a BTRFS subvolume? bestie it's a part of filesystem with its own independent file/directory hierarchy and inode number namespace. subvolumes can share file extents with each other (sharing is caring fr fr ๐ซถ). and a snapshot? a snapshot is also a subvolume, except it comes with a given initial content of the original subvolume. and every single subvolume has inode number 256, always โ that's just the vibe. (there's more on this in the inode numbers section further down, iykyk)
Note: a subvolume in BTRFS is not like an LVM logical volume. LVM does block-level snapshots, while BTRFS subvolumes are file extent-based. different species entirely, don't mix them up ๐
a subvolume looks like a normal directory, except it has some extra operations on top, described below. subvolumes can be renamed or moved, and nesting subvolumes is not restricted at all โ but it does have some implications regarding snapshotting (we'll get there, hold on). the numeric id, which people call the subvolid or rootid, of the subvolume is persistent and cannot be changed. periodt.
you can access a subvolume in BTRFS in two ways:
- like any other directory that is accessible to the user
- like a separately mounted filesystem (using the options
subvolorsubvolid)
in that second case, the parent directory is not visible and accessible. this is similar to a bind mount, and honestly in fact the subvolume mount does exactly that. no notes โจ
a freshly created filesystem is also a subvolume, called top-level, and internally it has an id 5. this subvolume cannot be removed or replaced by another subvolume โ it's permanent, it's the blueprint, it stays. this is also the subvolume that will be mounted by default, unless the default subvolume has been changed (see btrfs subvolume set-default).
a snapshot is a subvolume like any other, with given initial content. by default, snapshots are created read-write. and here's the important bit: file modifications in a snapshot do not affect the files in the original subvolume. totally separate, as they should be ๐
subvolumes can be given capacity limits through the qgroups/quota facility, but otherwise they share the single storage pool of the whole btrfs filesystem. they may even share data between themselves, through deduplication or snapshotting. generous queens ๐
Note: a snapshot is not a backup. like, at all. snapshots work by use of BTRFS' copy-on-write behaviour. a snapshot and the original it was taken from initially share all of the same data blocks. so if that data gets damaged in some way โ cosmic rays โ๏ธ, a bad disk sector, an accident with dd to the disk ๐ โ then the snapshot and the original will both be damaged. snapshots are useful for having local online "copies" of the filesystem you can refer back to, or for implementing a form of deduplication, or for fixing the state of a filesystem so you can make a full backup without anything changing underneath it. they do not in themselves make your data any safer. sorry bestie, that's just the truth ๐ญ
๐ SUBVOLUME FLAGS ๐
the subvolume flag currently implemented is the ro property (read-only status). read-write subvolumes have that set to false, and snapshots have it set to true. and in addition to that, a plain snapshot will also have last change generation and creation generation equal.
read-only snapshots are the building blocks of incremental send (see btrfs-send(8)), and the whole use case relies on unmodified snapshots where the relative changes are generated from. so if you go and change the subvolume flags from read-only to read-write, that breaks the assumptions and may lead to unexpected changes in the resulting incremental stream. so like... don't ๐ซฃ
a snapshot that was created by send/receive will be read-only, with different last change generation, and with set received_uuid โ which identifies the subvolume on the filesystem that produced the stream. the use case relies on matching data on both sides. changing the subvolume to read-write after it has been received requires you to reset the received_uuid. and because that's a notable change that could potentially break the incremental send use case, doing it with btrfs property set requires force if that is really what you want. it's the "are you sure, bestie" of flags.
Note: the safety checks have been implemented in5.14.2. any subvolumes previously received (with a validreceived_uuid) and read-write status may exist and could still lead to problems with send/receive. you can usebtrfs subvolume showto identify them. flipping the flags to read-only and back to read-write will reset thereceived_uuidmanually. there may exist a convenience tool in the future. ๐ฎ
๐ง NESTED SUBVOLUMES ๐ง
there are no restrictions for subvolume creation, so it's entirely up to you how to organize them. flat layout where all subvolumes are direct descendants of the toplevel one? nested? your filesystem, your rules ๐
but what should be mentioned early is that snapshotting is not recursive. so a subvolume or a snapshot is effectively a barrier, and no files in the nested subvolumes appear in the snapshot. instead, there's a stub subvolume, also sometimes called an empty subvolume, with the same name as the original subvolume and with inode number 2. this can be used intentionally, but could also be confusing in case of nested layouts. it's giving "wait why is this empty" ๐
$ btrfs subvolume create subvol1
$ btrfs subvolume create subvol1/subvol2
$ btrfs subvolume snapshot subvol1 snap1
$ find -ls
121093 0 drwxr-xr-x 1 user users 24 Jul 30 12:34 .
256 0 drwxr-xr-x 1 user users 14 Jul 30 12:34 ./subvol1
256 0 drwxr-xr-x 1 user users 0 Jul 30 12:34 ./subvol1/subvol2
257 0 -rw-r--r-- 1 user users 0 Jul 30 12:34 ./subvol1/subvol2/file
256 0 drwxr-xr-x 1 user users 14 Jul 30 12:34 ./snap1
2 0 drwxr-xr-x 1 user users 0 Jul 30 12:34 ./snap1/subvol2
the numbers in the first columns are inode numbers. 256 is for a regular subvolume (or snapshot), 2 is the empty subvolume. and see how the snapshotted directory representing subvol2 does not contain the file? yeah. that's the barrier doing barrier things ๐
Note: the empty subvolume will not be sent (btrfs-send(8)) and thus will not be created on the receive side (btrfs-receive(8)).
๐ธ Case study: system root layouts ๐ธ
there are two ways how the system root directory and subvolume layout could be organized. the interesting use case for root is to allow rollbacks to a previous version, as one atomic step. so if the entire filesystem hierarchy starting in / is in one subvolume, taking a snapshot will encompass all files. super easy for the snapshotting part โ but it has undesirable consequences for rollback ๐ฌ for example, log files would get rolled back too, or any data that's stored on the root filesystem but is not meant to be rolled back either (database files, VM images, ...). not the vibe.
here we could utilize that snapshotting barrier mentioned above, making each directory that stores data to be preserved across rollbacks its own subvolume. this could be e.g. /var. further more fine-grained partitioning could be done too, e.g. adding separate subvolumes for /var/log, /var/cache etc. subvolumes everywhere, we love to see it ๐
the fact that there are separate subvolumes means there are separate actions required to take the snapshots (here, it gets disconnected from the system root snapshots). this needs to be taken care of by system tools, installers, together with the selection of which directories are highly recommended to be separate subvolumes. teamwork makes the dream work fr fr ๐
๐ MOUNT OPTIONS ๐
mount options are of two kinds: generic ones (that are handled by the VFS layer) and specific ones, handled by the filesystem. the following list shows which are applicable to individual subvolume mounts, while there are more options that always affect the whole filesystem:
- Generic:
noatime/relatime/...,nodev,nosuid,ro,rw,dirsync - Filesystem-specific:
compress,autodefrag,nodatacow,nodatasum
examples of whole filesystem options are e.g. space_cache, rescue, device, skip_balance, etc. the exceptional options are subvol and subvolid, which are actually used for mounting a given subvolume and can be specified only once for the mount. one shot only bestie โจ
subvolumes belong to a single filesystem and, as implemented now, all share the same specific mount options. also, changes done by remount have immediate effect. this may change in the future. (foreshadowing ๐ซฃ)
mounting a read-write snapshot as read-only is possible and will not change the ro property and flag of the subvolume. yes, they're independent, don't worry about it ๐ซถ
the name of the mounted subvolume is stored in the file /proc/self/mountinfo in the 4th column:
27 21 0:19 /subv1 /mnt rw,relatime - btrfs /dev/sda rw,space_cache
^^^^^^
โญ INODE NUMBERS โญ
a directory representing a subvolume has always inode number 256 (sometimes also called a root of the subvolume):
$ ls -lis
total 0
389111 0 drwxr-xr-x 1 user users 0 Jan 20 12:13 dir
389110 0 -rw-r--r-- 1 user users 0 Jan 20 12:13 file
256 0 drwxr-xr-x 1 user users 0 Jan 20 12:13 snap1
256 0 drwxr-xr-x 1 user users 0 Jan 20 12:13 subv1
and if a subvolume is nested and then a snapshot is taken, then the cloned directory entry representing the subvolume becomes empty and the inode has number 2. all other files and directories in the target snapshot preserve their original inode numbers. so the 2s are the impostors ๐
Note: inode number is not a filesystem-wide unique identifier, even though some applications assume that. please use thesubvolumeid:inodenumberpair for that purpose instead ๐ซถ the subvolume id can be read bybtrfs inspect-internal rootidor by the ioctlBTRFS_IOC_INO_LOOKUP.
๐ฅ PERFORMANCE ๐ฅ
subvolume creation needs to flush dirty data that belongs to the subvolume, and this step may take some time. otherwise, once there's nothing else to do, the snapshot is instantaneous and only creates a new tree root copy in the metadata. basically instant, chef's kiss ๐จ๐ณ๐
snapshot deletion has two phases: first its directory is deleted and the subvolume is added to a queuing list, then the list is processed one by one and the data related to the subvolume gets deleted. this is usually called cleaning and it can take some time, depending on the amount of shared blocks (can be a lot of metadata updates), and the number of currently queued deleted subvolumes. so be patient, bestie, it's working on it ๐ญ
๐ SUBVOLUME AND SNAPSHOT ๐
okay bestie, so a subvolume is literally just a chunk of the filesystem that has its own independent file/directory hierarchy all to itself. and the slay part? subvolumes can share file extents with each other. a snapshot is also a subvolume, just one that starts out with a given initial content copied from the original subvolume. โจ
Note: a subvolume in btrfs is not like an LVM logical volume, bestie. LVM is a block-level snapshot, while btrfs subvolumes are file extent-based. different species, different vibe. ๐
a subvolume looks like a totally normal directory, but with some extra operations, which we'll get into below. you can rename them, move them, and nesting subvolumes isn't restricted โ though it does have some implications when it comes to snapshotting. iykyk. ๐ธ
there are two ways to access a subvolume in btrfs:
- like any other directory that's accessible to the user
- like a separately mounted filesystem (that's the
subvolorsubvolidoptions)
in that second case, the parent directory is not visible and not accessible. it's giving bind mount โ and in fact, the subvolume mount does exactly that. ๐ช
a freshly created filesystem is also a subvolume, and it's called the top-level, which internally has an id 5. this subvolume cannot be removed or replaced by another subvolume. it's also the subvolume that gets mounted by default, unless the default subvolume has been changed (see the set-default subcommand). ๐
and a snapshot? it's a subvolume like any other, with a given initial content. by default, snapshots are created read-write. any file modifications you make in a snapshot do not affect the files in the original subvolume. no cap. ๐ฆ
โจ SUBCOMMAND โจ
๐ create ๐
create [options] [<dest>/]<name> [[<dest2>/]<name2> ...]
create subvolume(s) at the destination(s), bestie.
- if the dest part of the path isn't given, the subvolume
namegets created in the current directory. cozy. ๐ - if you give multiple destinations, then the options you passed get applied to all the subvolumes.
- and if a failure happens for any of the destinations, the command still retries the remaining destinations โ but it returns
1to let you know something went wrong (similar to whatmkdirwould do).
Options
-i <qgroupid>โ add the newly created subvolume to a qgroup. you can give this option multiple times. ๐ซถ-p,--parentsโ create any missing parent directories for each argument (likemkdir -p).
๐ delete ๐
delete [options] [<subvolume> [<subvolume>...]]
delete -i|--subvolid <subvolid> <path>
delete the subvolume(s) from the filesystem. bye bestie ๐
if the thing you point at is not a subvolume, the command returns an error โ but it keeps going if there are more arguments to process.
if --subvolid is used, then path must point to a btrfs filesystem. see btrfs subvolume list or btrfs inspect-internal rootid for how to get the subvolume id. ๐
the corresponding directory gets removed instantly, but the data blocks are removed later, in the background. so the command returns immediately. see btrfs subvolume sync for how to wait until the subvolume gets completely removed. โณ
the deletion does not involve a full transaction commit by default, for performance reasons. as a consequence, the subvolume may appear again after a crash. ๐ญ use one of the --commit options to wait until the operation is safely stored on the device before the command returns.
deleting a subvolume needs sufficient permissions, fr fr. by default the owner cannot delete it unless it's enabled by a mount option user_subvol_rm_allowed, or unless the deletion is run as root. and the default subvolume (see btrfs subvolume set-default) cannot be deleted at all โ it returns error (EPERM) and this gets logged to the system log. ๐ a subvolume that's currently involved in send (see btrfs-send(8)) also cannot be deleted until the send is finished. this is also logged in the system log.
Options
-c,--commit-afterโ wait for transaction commit at the end of the operation.-C,--commit-eachโ wait for transaction commit after deleting each subvolume.-i,--subvolid <subvolid>โ subvolume id to be removed instead of the<path>that should point to the filesystem with the subvolume.-R,--recursiveโ delete subvolumes beneath each subvolume recursively.
this requires either CAP_SYS_ADMIN or the filesystem must be mounted with the user_subvol_rm_allowed mount option. in the unprivileged case, subvolumes which cannot be accessed are skipped. and heads up: the deletion is not atomic. ๐
-v,--verboseโ (deprecated) alias for the global-voption.
๐ฆ find-new ๐ฆ
find-new <subvolume> <last_gen>
list the recently modified files in a subvolume, after last_gen generation. like a little what's-new feed for your files โจ
๐ง get-default ๐ง
get-default <path>
get the default subvolume of the filesystem path.
the output format is similar to the subvolume list command.
โญ list โญ
list [options] [-G [+|-]<value>] [-C [+|-]<value>] [--sort=rootid,gen,ogen,path] <path>
list the subvolumes present in the filesystem path.
for every subvolume, this info is shown by default:
ID ID gen generation top level parent_ID path path
where ID is the subvolume's (root)id, generation is an internal counter which is updated every transaction, parent_ID is the same as the parent subvolume's id, and path is the relative path of the subvolume to the top level subvolume. the subvolume's ID may be used by the subvolume set-default command, or at mount time via the subvolid= option. ๐ฅ
Options
Path filtering:
-oโ print only subvolumes below the specified<path>. note that this is not a recursive command, and won't show nested subvolumes under<path>.-aโ print all the subvolumes in the filesystem and distinguish between absolute and relative path with respect to the given path.
Field selection:
-pโ print the parent ID (parent here means the subvolume which contains this subvolume).-cโ print the ogeneration of the subvolume, aliases:ogenor origin generation.-gโ print the generation of the subvolume (default).-uโ print the UUID of the subvolume.-qโ print the parent UUID of the subvolume (parent here means subvolume of which this subvolume is a snapshot).-Rโ print the UUID of the sent subvolume, where the subvolume is the result of a receive operation.
Type filtering:
-sโ only snapshot subvolumes in the filesystem will be listed.-rโ only readonly subvolumes in the filesystem will be listed.-dโ list deleted subvolumes that are not yet cleaned.
Other:
-tโ print the result as a table.
Sorting:
by default, the subvolumes will be sorted by subvolume ID ascending. ๐
-G [+|-]<value>โ list subvolumes in the filesystem that its generation is>=,<=or=value.'+'means>= value,'-'means<= value. if there is neither'+'nor'-', it means= value.-C [+|-]<value>โ list subvolumes in the filesystem that its ogeneration is>=,<=or=value. the usage is the same as the-Goption.--sort=rootid,gen,ogen,pathโ list subvolumes in order by specified items. you can add+or-in front of each item,+means ascending,-means descending. the default is ascending.
for --sort you can combine some items together by ,, just like --sort=+ogen,-gen,path,rootid. ๐ซ
๐ set-default ๐
set-default [<subvolume>|<id> <path>]
set the default subvolume for the (mounted) filesystem.
this sets the default subvolume for the (mounted) filesystem at path. this will hide the top-level subvolume (i.e. the one mounted with subvol=/ or subvolid=5). takes action on next mount. โญ๏ธ
there are two ways to specify the subvolume: by id or by the subvolume path. the id can be obtained from btrfs subvolume list, btrfs subvolume show or btrfs inspect-internal rootid.
๐ show ๐
show [options] <path>
show more information about a subvolume โ UUIDs, generations, times, flags, related snapshots. it's giving receipts ๐
/mnt/btrfs/subvolume
Name: subvolume
UUID: 5e076a14-4e42-254d-ac8e-55bebea982d1
Parent UUID: -
Received UUID: -
Creation time: 2018-01-01 12:34:56 +0000
Subvolume ID: 79
Generation: 2844
Gen at creation: 2844
Parent ID: 5
Top level ID: 5
Flags: -
Snapshot(s):
Options
-r,--rootid <ID>โ show details about the subvolume with root ID, looked up in path.-u,--uuid UUIDโ show details about the subvolume with the given UUID, looked up in path.
๐ซถ snapshot ๐ซถ
snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>
create a snapshot of the subvolume source with the name name in the dest directory.
if only dest is given, the subvolume will be named the basename of source. and if source is not a subvolume, btrfs returns an error. ๐ซ
Options
-rโ make the new snapshot read only.-i <qgroupid>โ add the newly created subvolume to a qgroup. this option can be given multiple times.
๐ธ sync ๐ธ
sync <path> [subvolid...]
wait until the given subvolume(s) are completely removed from the filesystem after deletion. if no subvolume id is given, wait until all current deletion requests are completed โ but do not wait for subvolumes deleted in the meantime. โฐ
if the filesystem status changes to read-only, then the waiting is interrupted.
Options
-s <N>โ sleep N seconds between checks (default:1).
๐ EXAMPLES ๐
๐๏ธ Deleting a subvolume
if we want to delete a subvolume called foo from a btrfs volume mounted at /mnt/bar, we could run the following:
btrfs subvolume delete /mnt/bar/foo
๐ซ EXIT STATUS ๐ซ
btrfs subvolume returns a zero exit status if it succeeds. a non-zero value is returned in case of failure. that's it, that's the tea ๐ตโจ
โจ AVAILABILITY โจ
Okay so bestie, where does this whole thing even live? ๐ btrfs is part of btrfs-progs, that's the package, that's the whole squad. ๐ซถ
And if you want the full lore, the documentation is literally right here:
https://btrfs.readthedocs.io
Go read it, it's giving textbook but make it slay. ๐โจ
๐ SEE ALSO ๐
The friend group, the entourage, the ones who show up in every cross-reference โ go check them out:
btrfs-qgroup(8)btrfs-quota(8)btrfs-send(8)mkfs.btrfs(8)mount(8)โhttps://man7.org/linux/man-pages/man8/mount.8.html
๐ and that's a wrap on the whole page, no cap. every flag, every subcommand, every little detail โ all of it, periodt.
This was btrfs-progs version 7.3, dated Oct 24, 2026. ๐ Go make some subvolumes, bestie โ you're the main character of your own filesystem now. ๐๐ธ๐ฆ
๐ฆ where this page comes from
- arch rolling: btrfs-progs 7.3-1 ยท GPL-2.0-only
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms