Storage

A UraniumOS system is kept on one btrfs filesystem that has the label uranium. The root and /home are the subvolumes @ and @home, and the "frames" of scram are read-only snapshots that are stored beside them. A second partition, a FAT32 EFI System Partition with the label URANIUMESP, holds the boot loader, the kernel, the initramfs and the boot menu.

The installer and the manual install produce the same layout. Only UEFI machines are supported.

Partitions

The installer erases the disk that you choose. It unmounts everything that is mounted from the disk, wipes the old signatures with wipefs -af and writes a GPT with sfdisk:

label: gpt
,512M,U
,,L
1URANIUMESP512 MiB, type EFI System, formatted with mkfs.vfat -F32 -n URANIUMESP. Mounted on /boot.
2uraniumThe rest of the disk, type Linux filesystem, formatted with mkfs.btrfs -L uranium. It holds @, @home and .scram.

The installer then creates the two subvolumes on the top level, mounts them on the target and copies the live system into @ with rsync, leaving out /proc, /sys, /dev, /run, /tmp, /mnt, /boot, /home and /rod/containers. It writes /etc/fstab, runs setup, then scram apply, scram frame -m "fresh install" install and scram boot. It stops with "this installer needs UEFI" when /sys/firmware/efi is missing.

Mounts

LABEL=uranium      /      btrfs  noatime,subvol=@
LABEL=uranium      /home  btrfs  noatime,nodev,nosuid,subvol=@home
LABEL=URANIUMESP   /boot  vfat   umask=0077,nodev,nosuid,noexec

The initramfs mounts the root itself with subvol=@,noatime, and /etc/rc remounts it read-write and runs mount -a, which mounts the other two lines. /tmp is a tmpfs with nosuid,nodev, /dev/shm a tmpfs with nosuid,nodev,noexec, and /run a tmpfs. The tmpfs for /tmp has no size option and takes the kernel default of half the memory.

The top level of the btrfs filesystem (subvolume 5) is mounted by the initramfs, moved into the new root as /run/scram/top, and carried over the tmpfs on /run by /etc/rc. The boot partition is mounted read-write and FAT keeps no snapshots.

/run/scram/top
    @                          mounted on /
    @home                      mounted on /home
    .scram/
        20261102-165551-install/
            @  @home           read-only snapshots
            boot/              vmlinuz  initrd  id
            note
        keep.list
        boot-action            present while a change waits

What a frame holds

A frame consists of a snapshot of @ and a snapshot of @home. It contains everything in them that is not a nested subvolume: /etc with the accounts and password hashes, /var/log, the Nix store in /nix/store, the package profiles and the list of installed packages in /rod, the containers of silo in /rod/containers, and all of /home. It does not contain the left-out directories, the boot partition, other disks or anything in a tmpfs. A frame costs only the blocks that change after it is taken.

A program that writes while a snapshot is taken leaves the snapshot in the state that a power cut would leave. The pre-frame hooks may be used to stop such a program first.

What is left out of frames

A nested subvolume is not part of a snapshot. A left-out directory is therefore made a subvolume of it's own, so that frames do not contain it and a flash does not change it. The system leaves out three directories, which are listed in /etc/scram.d/00-uranium.conf:

/rod/nixpkgsThe package index, downloaded by rod +s. A flash does not change it.
/var/cacheCaches.
/var/tmpTemporary files that survive a reboot.

To leave out further directories, you may put lines in /etc/scram.d/ (for the whole system) or in ~/.config/scram (for the user who runs scram apply), and then you must run scram apply as root. A line that begins with - leaves a path out. The sign ~ stands for the home directory of the user who runs the command. The format is described in scram.

# echo '-/srv/www' > /etc/scram.d/10-www.conf
# scram apply
excluded /srv/www
# scram status | grep -A4 ^excluded
excluded:
  @ rod/nixpkgs
  @ var/cache
  @ var/tmp
  @ srv/www

apply renames an existing directory, creates the subvolume, copies the contents back with reflinks and removes the old directory. The subvolume stays a subvolume. Files that sat in the directory when an earlier frame was taken are also in that frame, so the frame holds an old copy of what is now left out.

Every nested subvolume in @ must be in the list. You must not create a subvolume under the root with btrfs subvolume create without adding it to the configuration.

scram doctor reports a listed directory that is not a subvolume. In a frame taken after apply, a left-out directory is an empty directory. A frame booted from the menu therefore has an empty /rod/nixpkgs, and rod +s has to fetch the index again there.

Space

Deleting or changing a file does not free its blocks while a frame still refers to them, so a system on which many packages are installed and removed grows with its frames. Space may be freed in the following ways:

geiger warns when the root is 85% full and fails at 95%, and warns for the boot partition at 80% and fails at 92%. The boot partition holds the current kernel and initramfs, up to three more of each for older frames and the loader. scram boot prints an error when a copy does not fit. When scram frame or rod runs it, a failure to write the menu is not reported, so watch the boot partition with geiger.

Maintenance

The file /etc/crontabs/root contains two jobs for the cron service:

0 4 * * 0 fstrim -a
0 5 1 * * btrfs scrub start -B /

The first trims free space on Sundays at 04:00 and the second scrubs the filesystem on the first of the month at 05:00. A scrub reads every block and checks its checksum. Data has one copy on this layout, so a scrub reports damaged data and cannot repair it.

Swap is a compressed zram device. /etc/rc asks for the zstd algorithm, which the kernel may refuse, and sets its size to half of the memory, runs mkswap and enables it with priority 100. Nothing is written to disk for swap, so hibernation does not work.

An encrypted root

The installer cannot make an encrypted root. The manual install can: make the btrfs filesystem on a LUKS device that is opened as uranium-root, and add uranium.luks=DEVICE to /etc/cmdline. DEVICE is a path under /dev, or anything that findfs resolves, such as UUID=... or LABEL=.... See manual install.

At boot the initramfs waits up to 20 seconds for the device. Without uranium.luks.key it runs cryptsetup open DEVICE uranium-root, which asks for the passphrase on the console, and gives up after three failed tries with initramfs: cannot unlock DEVICE and a shell. A device that does not appear gives an error. The root is then found by its label, root=LABEL=uranium, on /dev/mapper/uranium-root. Frames, the Nix store, the home directories and everything else in the btrfs filesystem are inside the encrypted device. The boot partition is not encrypted: limine, the kernel, the initramfs and limine.conf are readable, and /etc/cmdline content appears there.

With uranium.luks.key=FILE the initramfs passes FILE to cryptsetup --key-file. The initramfs has only its own files and /dev, so the file has to be a device node or inside the initramfs.

A damaged LUKS header makes the disk, and all frames on it, unreadable. You should save a copy of the header on another medium with cryptsetup luksHeaderBackup.