<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.alpinelinux.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mvsn</id>
	<title>Alpine Linux - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.alpinelinux.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Mvsn"/>
	<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/wiki/Special:Contributions/Mvsn"/>
	<updated>2026-04-30T13:57:25Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.40.0</generator>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25840</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25840"/>
		<updated>2023-11-30T09:32:59Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: Improved sysmut script&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using a modern bootloader and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with the ability to easily rollback or boot previous configurations is a concept that has been gaining popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has its killer features, it lacks the ones mentioned above on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it is described on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide, it&#039;s assumed that you have a fresh UEFI system without an OS and have just booted into a live Alpine system using a USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create the file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var btrfs subvol=/commons/@var,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory structure prepared, we can begin installing a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you&#039;re in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
This example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for the firmware, kernel, and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for your system, for example, &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for a typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It&#039;s also important to add the &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare the kernel and generate the &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt;, which will be used later to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that, you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install the &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; package to support changing the network connection. In this case, &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt;, and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured, we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing the snapshot by setting the &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting the root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Bootloader configuration ==&lt;br /&gt;
There are 2 options as examples of the bootloader installation: &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;GRUB&amp;lt;/code&amp;gt;.&lt;br /&gt;
Sometimes one of them will refuse to work on a system for no particular reason, in this case try the other one.&lt;br /&gt;
=== rEFInd ===&lt;br /&gt;
Check the latest version number of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -X https://dl-cdn.alpinelinux.org/alpine/edge/testing -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack the prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition). Keep in mind that if you named the btrfs volume other than &amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; during &amp;quot;Partitioning disks&amp;quot; stage, you have to change the &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; field below accordingly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
=== GRUB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add grub-efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
GRUB requires two configuration files this time as we will use &amp;lt;code&amp;gt;grub-mkstandalone&amp;lt;/code&amp;gt;.&lt;br /&gt;
The first configuration file is internal and should only point to the second file, where we store the menu:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cd /tmp&lt;br /&gt;
# vi grub_internal.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
Set the contents to the following, but make sure to replace &amp;lt;code&amp;gt;2FE6-837A&amp;lt;/code&amp;gt; with your own EFI partition UUID:&lt;br /&gt;
&amp;lt;pre&amp;gt;insmod part_gpt&lt;br /&gt;
insmod fat&lt;br /&gt;
search --set efi --fs-uuid 2FE6-837A&lt;br /&gt;
configfile (${efi})/EFI/grub/grub.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
The second config file is the main config where we describe the entire boot menu.&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi grub.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
Set to contain, but replace UUIDs:&lt;br /&gt;
&amp;lt;pre&amp;gt;set timeout=3&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Current&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/0/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/0/@/boot/initramfs-edge&lt;br /&gt;
}&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Snapshot 1&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/1/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/1/@/boot/initramfs-edge&lt;br /&gt;
}&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Snapshot 2&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/2/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/2/@/boot/initramfs-edge&lt;br /&gt;
}&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Snapshot 3&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/3/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/3/@/boot/initramfs-edge&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
Generate the &amp;lt;code&amp;gt;grubx64.efi&amp;lt;/code&amp;gt; binary:&lt;br /&gt;
&amp;lt;pre&amp;gt;# grub-mkstandalone -O x86_64-efi -o grubx64.efi &amp;quot;boot/grub/grub.cfg=/tmp/grub_internal.cfg&amp;quot;&lt;br /&gt;
# mkdir /mnt/EFI/grub&lt;br /&gt;
# mv grubx64.efi /mnt/EFI/grub/&lt;br /&gt;
# mv grub.cfg /mnt/EFI/grub/&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding EFI boot entry ==&lt;br /&gt;
To add the chosen bootloader to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool. The following example is for rEFInd, but could be easily adjusted for GRUB:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of the FAT32 partition containing the bootloader data.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Warning|Without the following step or an alternative you will have no easy way to mutate the installed system.}}&lt;br /&gt;
{{Warning|These examples are implemented using &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; and require the &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system.}}&lt;br /&gt;
{{Note|These could surely be implemented in POSIX shell, however, &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; provides a number of runtime advantages and the resulting script is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /usr/sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		foreground {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		importas apply ?&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		ifelse { exit ${apply} } {&lt;br /&gt;
			if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
			define newlink ${dt}${rnd}&lt;br /&gt;
			if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
			if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
			if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
			if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
			if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
			if { mkdir -p ${mnt}/next }&lt;br /&gt;
			if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
			if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
			echo &amp;quot;Changes applied&amp;quot;&lt;br /&gt;
		}&lt;br /&gt;
		echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like. The origin of the new snapshot is defined by the first and only argument, in form of number. If no argument provided the &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (current latest) is taken as origin.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/syscln&lt;br /&gt;
# chmod +x /usr/sbin/syscln&lt;br /&gt;
# vi /usr/sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use of this, add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Installation]][[Category:File systems]]&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25816</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25816"/>
		<updated>2023-11-21T17:13:54Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: Add GRUB as an alternative bootloader&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using a modern bootloader and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var btrfs subvol=/commons/@var,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Bootloader configuration ==&lt;br /&gt;
There are 2 options as examples of the bootloader installation: &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;GRUB&amp;lt;/code&amp;gt;.&lt;br /&gt;
Sometimes one of them will refuse to work on a system for no paticular reason, in this case try the other one.&lt;br /&gt;
=== rEFInd ===&lt;br /&gt;
Check the latest version number of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -X https://dl-cdn.alpinelinux.org/alpine/edge/testing -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition). Keep in mind that if you named the btrfs volume other than &amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; during &amp;quot;Partitioning disks&amp;quot; stage, you have to change the &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; field below accordingly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
=== GRUB ===&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add grub-efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
GRUB requires two configuration files this time as we will use &amp;lt;code&amp;gt;grub-mkstandalone&amp;lt;/code&amp;gt;.&lt;br /&gt;
The first configuration file is internal and should only point to the second file, where we store the menu:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cd /tmp&lt;br /&gt;
# vi grub_internal.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
Set the contents to the following, but make sure to replace &amp;lt;code&amp;gt;2FE6-837A&amp;lt;/code&amp;gt; with your own EFI partition UUID:&lt;br /&gt;
&amp;lt;pre&amp;gt;insmod part_gpt&lt;br /&gt;
insmod fat&lt;br /&gt;
search --set efi --fs-uuid 2FE6-837A&lt;br /&gt;
configfile (${efi})/EFI/grub/grub.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
Second config file is the main config where we decribe the whole boot menu.&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi grub.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
Set to contain, but replace UUIDs:&lt;br /&gt;
&amp;lt;pre&amp;gt;set timeout=3&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Current&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/0/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/0/@/boot/initramfs-edge&lt;br /&gt;
}&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Snapshot 1&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/1/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/1/@/boot/initramfs-edge&lt;br /&gt;
}&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Snapshot 2&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/2/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/2/@/boot/initramfs-edge&lt;br /&gt;
}&lt;br /&gt;
menuentry &amp;quot;Alpine Linux Snapshot 3&amp;quot; {&lt;br /&gt;
	search --set root --fs-uuid b9ff5e7b-e128-4e64-861a-2fdd794a9828&lt;br /&gt;
	linux /current/3/@/boot/vmlinuz-edge root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&lt;br /&gt;
	initrd /current/3/@/boot/initramfs-edge&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
Generate the &amp;lt;code&amp;gt;grubx64.efi&amp;lt;/code&amp;gt; binary:&lt;br /&gt;
&amp;lt;pre&amp;gt;# grub-mkstandalone -O x86_64-efi -o grubx64.efi &amp;quot;boot/grub/grub.cfg=/tmp/grub_internal.cfg&amp;quot;&lt;br /&gt;
# mkdir /mnt/EFI/grub&lt;br /&gt;
# mv grubx64.efi /mnt/EFI/grub/&lt;br /&gt;
# mv grub.cfg /mnt/EFI/grub/&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding EFI boot entry ==&lt;br /&gt;
To add the chosen bootloader to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool. The following example is for rEFInd, but could be easily adjusted for GRUB:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of the FAT32 partition containing the bootloader data.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Warning|Without the following step or an alternative you will have no easy way to mutate the installed system.}}&lt;br /&gt;
{{Warning|These examples are implemented using &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; and require the &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system.}}&lt;br /&gt;
{{Note|These could surely be implemented in POSIX shell, however &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; provides number of runtime advantages and the script in it is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /usr/sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/syscln&lt;br /&gt;
# chmod +x /usr/sbin/syscln&lt;br /&gt;
# vi /usr/sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Installation]][[Category:File systems]]&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Talk:Immutable_root_with_atomic_upgrades&amp;diff=25436</id>
		<title>Talk:Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Talk:Immutable_root_with_atomic_upgrades&amp;diff=25436"/>
		<updated>2023-11-04T18:46:28Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[User:Sb1]], regarding diskless mode note: thanks for reminding about it, but it&#039;s a different setup with its own features, so I reduced the note, but left the links. Similar guide to diskless chroot setup would be really appreciated btw. [[User:Mvsn|Mvsn]] ([[User talk:Mvsn|talk]]) 23:04, 31 May 2021 (UTC)&lt;br /&gt;
&lt;br /&gt;
== rEFInd ==&lt;br /&gt;
&lt;br /&gt;
The rEFInd configuration provided in this does not appear to work universally, as rEFInd assumes that the &amp;lt;code&amp;gt;loader&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;initrd&amp;lt;/code&amp;gt; parameters in the MenuEntry are located within the ESP partition. To produce a bootable version of this setup, I had to move my kernel and my initramfs to the ESP partition.&amp;lt;!-- Template:Unsigned --&amp;gt;&amp;lt;span class=&amp;quot;autosigned&amp;quot; style=&amp;quot;font-size:85%;&amp;quot;&amp;gt;—&amp;amp;nbsp;Preceding unsigned comments added by [[User:Machinestops|Machinestops ]] ([[User talk:Machinestops #top|talk]] • [[Special:Contributions/Machinestops |contribs]]) 12:01, 31 May 2023‎&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[User:Machinestops|Machinestops]], not sure I follow. The config field &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; defines which partition contains the path defined in the fields &amp;lt;code&amp;gt;loader&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;initrd&amp;lt;/code&amp;gt;. If you have btrfs volume with the label other than &amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt;, you have to also change &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; field in the config. Kernel and initramfs should reside in the btrfs volume, they are the part of the snapshot. [[User:Mvsn|Mvsn]] ([[User talk:Mvsn|talk]])&lt;br /&gt;
&lt;br /&gt;
== System mutation scripts ==&lt;br /&gt;
&lt;br /&gt;
It might be helpful to note that &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; should be included in the list of packages installed while setting up the initial root snapshot, and also that the scripts provided should be downloaded (perhaps provide them as raw files somehow?). Without them, there is no way to modify the system other than manually mounting, creating an RW snapshot, and performing the link migration manually, which might prove bothersome for some (or unclear).&lt;br /&gt;
&lt;br /&gt;
Documenting the functionality of the scripts with comments might also be useful. The level of piping and nesting in these scripts makes it difficult to follow along for anyone unfamiliar with &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt;.&amp;lt;!-- Template:Unsigned --&amp;gt;&amp;lt;span class=&amp;quot;autosigned&amp;quot; style=&amp;quot;font-size:85%;&amp;quot;&amp;gt;—&amp;amp;nbsp;Preceding unsigned comments added by [[User:Machinestops|Machinestops ]] ([[User talk:Machinestops #top|talk]] • [[Special:Contributions/Machinestops |contribs]]) 12:10, 31 May 2023‎&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25435</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25435"/>
		<updated>2023-11-04T18:41:59Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: simplify setup by using common non-snapshotted /var&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var btrfs subvol=/commons/@var,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Check the latest version number of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -X https://dl-cdn.alpinelinux.org/alpine/edge/testing -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition). Keep in mind that if you named the btrfs volume other than &amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; during &amp;quot;Partitioning disks&amp;quot; stage, you have to change the &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; field below accordingly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Warning|Without the following step or an alternative you will have no easy way to mutate the installed system.}}&lt;br /&gt;
{{Warning|These examples are implemented using &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; and require the &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system.}}&lt;br /&gt;
{{Note|These could surely be implemented in POSIX shell, however &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; provides number of runtime advantages and the script in it is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /usr/sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/syscln&lt;br /&gt;
# chmod +x /usr/sbin/syscln&lt;br /&gt;
# vi /usr/sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Installation]][[Category:File systems]]&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25434</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=25434"/>
		<updated>2023-11-04T18:22:54Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: add notes and warnings for some steps&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example iwd and bluetooth:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@lib@bluetooth&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/bluetooth btrfs subvol=/commons/@var@lib@bluetooth,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Check the latest version number of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -X https://dl-cdn.alpinelinux.org/alpine/edge/testing -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition). Keep in mind that if you named the btrfs volume other than &amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; during &amp;quot;Partitioning disks&amp;quot; stage, you have to change the &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; field below accordingly.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Warning|Without the following step or an alternative you will have no easy way to mutate the installed system.}}&lt;br /&gt;
{{Warning|These examples are implemented using &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; and require the &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system.}}&lt;br /&gt;
{{Note|These could surely be implemented in POSIX shell, however &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; provides number of runtime advantages and the script in it is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /usr/sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/syscln&lt;br /&gt;
# chmod +x /usr/sbin/syscln&lt;br /&gt;
# vi /usr/sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Installation]][[Category:File systems]]&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Talk:Immutable_root_with_atomic_upgrades&amp;diff=25433</id>
		<title>Talk:Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Talk:Immutable_root_with_atomic_upgrades&amp;diff=25433"/>
		<updated>2023-11-04T18:09:49Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[User:Sb1]], regarding diskless mode note: thanks for reminding about it, but it&#039;s a different setup with its own features, so I reduced the note, but left the links. Similar guide to diskless chroot setup would be really appreciated btw. [[User:Mvsn|Mvsn]] ([[User talk:Mvsn|talk]]) 23:04, 31 May 2021 (UTC)&lt;br /&gt;
&lt;br /&gt;
== rEFInd ==&lt;br /&gt;
&lt;br /&gt;
The rEFInd configuration provided in this does not appear to work universally, as rEFInd assumes that the &amp;lt;code&amp;gt;loader&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;initrd&amp;lt;/code&amp;gt; parameters in the MenuEntry are located within the ESP partition. To produce a bootable version of this setup, I had to move my kernel and my initramfs to the ESP partition.&amp;lt;!-- Template:Unsigned --&amp;gt;&amp;lt;span class=&amp;quot;autosigned&amp;quot; style=&amp;quot;font-size:85%;&amp;quot;&amp;gt;—&amp;amp;nbsp;Preceding unsigned comments added by [[User:Machinestops|Machinestops ]] ([[User talk:Machinestops #top|talk]] • [[Special:Contributions/Machinestops |contribs]]) 12:01, 31 May 2023‎&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[User:Machinestops|Machinestops]], not sure I follow. The config field &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; defines which partition contains the path defined in the fields &amp;lt;code&amp;gt;loader&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;initrd&amp;lt;/code&amp;gt;. If you have btrfs volume with the label other than &amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt;, you have to also change &amp;lt;code&amp;gt;volume&amp;lt;/code&amp;gt; field in the config. Kernel and initramfs should reside in the btrfs volume, they are the part of the snapshot. [[User:Mvsn|Mvsn]] ([[User talk:Mvsn|talk]]) 23:04, 31 May 2021 (UTC)&lt;br /&gt;
&lt;br /&gt;
== System mutation scripts ==&lt;br /&gt;
&lt;br /&gt;
It might be helpful to note that &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; should be included in the list of packages installed while setting up the initial root snapshot, and also that the scripts provided should be downloaded (perhaps provide them as raw files somehow?). Without them, there is no way to modify the system other than manually mounting, creating an RW snapshot, and performing the link migration manually, which might prove bothersome for some (or unclear).&lt;br /&gt;
&lt;br /&gt;
Documenting the functionality of the scripts with comments might also be useful. The level of piping and nesting in these scripts makes it difficult to follow along for anyone unfamiliar with &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt;.&amp;lt;!-- Template:Unsigned --&amp;gt;&amp;lt;span class=&amp;quot;autosigned&amp;quot; style=&amp;quot;font-size:85%;&amp;quot;&amp;gt;—&amp;amp;nbsp;Preceding unsigned comments added by [[User:Machinestops|Machinestops ]] ([[User talk:Machinestops #top|talk]] • [[Special:Contributions/Machinestops |contribs]]) 12:10, 31 May 2023‎&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21558</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21558"/>
		<updated>2022-02-06T08:39:01Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Check the latest version number of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -X https://dl-cdn.alpinelinux.org/alpine/edge/testing -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Note|These examples are implemented using execline and require &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system. These could surely be implemented in POSIX shell, however execline provides number of runtime advantages and is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /usr/sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/syscln&lt;br /&gt;
# chmod +x /usr/sbin/syscln&lt;br /&gt;
# vi /usr/sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21557</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21557"/>
		<updated>2022-02-05T22:10:16Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Check the latest version number of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -X https://dl-cdn.alpinelinux.org/alpine/edge/testing -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Note|These examples are implemented using execline and require &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system. These could surely be implemented in POSIX shell, however execline provides number of runtime advantages and is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21556</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21556"/>
		<updated>2022-02-05T22:07:41Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Check the latest version number of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Note|These examples are implemented using execline and require &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system. These could surely be implemented in POSIX shell, however execline provides number of runtime advantages and is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21555</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21555"/>
		<updated>2022-02-05T22:04:07Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: use refind from repository&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Checkout the latest version of the refind:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk info -U refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download the latest version (replace 0.13.2-r3 in the example below) of the &amp;lt;code&amp;gt;refind&amp;lt;/code&amp;gt; package:&lt;br /&gt;
&amp;lt;pre&amp;gt;wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r3.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r3.apk&lt;br /&gt;
# cp -r usr/share/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Note|These examples are implemented using execline and require &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system. These could surely be implemented in POSIX shell, however execline provides number of runtime advantages and is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21550</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21550"/>
		<updated>2022-01-31T20:09:37Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: add service reminder&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks [[BTRFS|btrfs]] driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add essential services to their respective runlevels:&lt;br /&gt;
&amp;lt;pre&amp;gt;rc-update add devfs sysinit&lt;br /&gt;
rc-update add dmesg sysinit&lt;br /&gt;
rc-update add mdev sysinit&lt;br /&gt;
rc-update add hwdrivers sysinit&lt;br /&gt;
&lt;br /&gt;
rc-update add hwclock boot&lt;br /&gt;
rc-update add modules boot&lt;br /&gt;
rc-update add sysctl boot&lt;br /&gt;
rc-update add hostname boot&lt;br /&gt;
rc-update add bootmisc boot&lt;br /&gt;
rc-update add syslog boot&lt;br /&gt;
&lt;br /&gt;
rc-update add mount-ro shutdown&lt;br /&gt;
rc-update add killprocs shutdown&lt;br /&gt;
rc-update add savecache shutdown&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Note|These examples are implemented using execline and require &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system. These could surely be implemented in POSIX shell, however execline provides number of runtime advantages and is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21549</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21549"/>
		<updated>2022-01-31T20:02:40Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: more robust syscln script&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks [[BTRFS|btrfs]] driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Note|These examples are implemented using execline and require &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system. These could surely be implemented in POSIX shell, however execline provides number of runtime advantages and is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime,compress=zstd:3 UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
				find -H ${mnt}/current/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
			}&lt;br /&gt;
			xargs -0 -r realpath&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find -H ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				pipeline {&lt;br /&gt;
					find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -print0&lt;br /&gt;
				}&lt;br /&gt;
				xargs -0 -r realpath&lt;br /&gt;
			}&lt;br /&gt;
			realpath ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { tr \\n \\0 }&lt;br /&gt;
		pipeline { sort -z }&lt;br /&gt;
		pipeline { uniq -u -z }&lt;br /&gt;
		pipeline { xargs -0 -r -n 1 -I [] find -H [] -maxdepth 1 -mindepth 1 -print0 }&lt;br /&gt;
		xargs -0 -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find -H ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21515</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=21515"/>
		<updated>2022-01-28T22:06:34Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: execline upgrade scripts instead of shell&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and [[BTRFS|btrfs]].&lt;br /&gt;
&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks [[BTRFS|btrfs]] driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the [[BTRFS|btrfs]] root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root [[BTRFS|btrfs]] volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports [[BTRFS|btrfs]] and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; ([[BTRFS|btrfs]] partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the [[BTRFS|btrfs]] partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Note|These examples are implemented using execline and require &amp;lt;code&amp;gt;execline&amp;lt;/code&amp;gt; package in the system. These could surely be implemented in POSIX shell, however execline provides number of runtime advantages and is much more readable.}}&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /usr/sbin/sysmut&lt;br /&gt;
# chmod +x /usr/sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
importas -D 0 source 1&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	backtick -E dt {&lt;br /&gt;
		date -u +%Y%m%d%H%M%S&lt;br /&gt;
	}&lt;br /&gt;
	backtick -E rnd {&lt;br /&gt;
		pipeline { cat /dev/urandom }&lt;br /&gt;
		pipeline { tr -dc a-zA-Z }&lt;br /&gt;
		pipeline { fold -w 8 }&lt;br /&gt;
		head -n 1&lt;br /&gt;
	}&lt;br /&gt;
	define newsnap ${dt}${rnd}&lt;br /&gt;
	if { mkdir -p ${mnt}/snapshots/${newsnap} }&lt;br /&gt;
	if { btrfs subvolume snapshot ${mnt}/current/${source}/@ ${mnt}/snapshots/${newsnap}/@ }&lt;br /&gt;
	if {&lt;br /&gt;
		redirfd -w 1 ${mnt}/snapshots/${newsnap}/@/etc/fstab&lt;br /&gt;
			sed s#CURRENT_SNAPSHOTS_PATH#/snapshots/${newsnap}#g ${mnt}/fstab&lt;br /&gt;
	}&lt;br /&gt;
	if { mount -t proc none ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	if { mount -t sysfs sys ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	if { mount -o bind,ro /dev ${mnt}/snapshots/${newsnap}/@/dev }&lt;br /&gt;
	foreground {&lt;br /&gt;
		foreground { mount -o bind,ro /etc/resolv.conf ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if {&lt;br /&gt;
			chroot ${mnt}/snapshots/${newsnap}/@&lt;br /&gt;
			foreground { mount -a }&lt;br /&gt;
			foreground { sh }&lt;br /&gt;
			importas apply ?&lt;br /&gt;
			foreground { umount -a }&lt;br /&gt;
			exit ${apply}&lt;br /&gt;
		}&lt;br /&gt;
		foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/etc/resolv.conf }&lt;br /&gt;
		if { btrfs property set -ts ${mnt}/snapshots/${newsnap}/@ ro true }&lt;br /&gt;
		define newlink ${dt}${rnd}&lt;br /&gt;
		if { mkdir -p ${mnt}/links/${newlink} }&lt;br /&gt;
		if { ln -s ../../snapshots/${newsnap} ${mnt}/links/${newlink}/0 }&lt;br /&gt;
		if { cp -P ${mnt}/current/0 ${mnt}/links/${newlink}/1 }&lt;br /&gt;
		if { cp -P ${mnt}/current/1 ${mnt}/links/${newlink}/2 }&lt;br /&gt;
		if { cp -P ${mnt}/current/2 ${mnt}/links/${newlink}/3 }&lt;br /&gt;
		if { mkdir -p ${mnt}/next }&lt;br /&gt;
		if { ln -sfn ./links/${newlink} ${mnt}/next/current }&lt;br /&gt;
		if { mv ${mnt}/next/current ${mnt}/ }&lt;br /&gt;
		echo &amp;quot;Switched to the new snapshot&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/proc }&lt;br /&gt;
	foreground { redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/sys }&lt;br /&gt;
	redirfd -w 2 /dev/null umount ${mnt}/snapshots/${newsnap}/@/dev&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/execlineb -W&lt;br /&gt;
unshare --mount&lt;br /&gt;
define mnt /media/root&lt;br /&gt;
if { mkdir -p ${mnt} }&lt;br /&gt;
if { mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 ${mnt} }&lt;br /&gt;
foreground {&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
                		find ${mnt}/snapshots/ -maxdepth 1 -mindepth 1&lt;br /&gt;
			}&lt;br /&gt;
			pipeline {&lt;br /&gt;
                		find ${mnt}/current/ -maxdepth 1 -mindepth 1&lt;br /&gt;
			}&lt;br /&gt;
			xargs -r -n 1 readlink -f&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { sort }&lt;br /&gt;
		pipeline { uniq -u }&lt;br /&gt;
		pipeline { xargs -r -n 1 -I [] find [] -maxdepth 1 -mindepth 1 }&lt;br /&gt;
		xargs -r btrfs subvolume delete&lt;br /&gt;
	}&lt;br /&gt;
	foreground { find ${mnt}/snapshots/ -maxdepth 1 -mindepth 1 -empty -type d -delete }&lt;br /&gt;
	foreground {&lt;br /&gt;
		pipeline {&lt;br /&gt;
			foreground {&lt;br /&gt;
				find ${mnt}/links/ -maxdepth 1 -mindepth 1&lt;br /&gt;
			}&lt;br /&gt;
			readlink -f ${mnt}/current&lt;br /&gt;
		}&lt;br /&gt;
		pipeline { sort }&lt;br /&gt;
		pipeline { uniq -u }&lt;br /&gt;
		pipeline { xargs -r -n 1 -I [] find [] -maxdepth 1 -mindepth 1 }&lt;br /&gt;
		xargs -r -n 1 unlink&lt;br /&gt;
	}&lt;br /&gt;
	find ${mnt}/links/ -maxdepth 1 -mindepth 1 -empty -type d -delete&lt;br /&gt;
}&lt;br /&gt;
umount ${mnt}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20865</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20865"/>
		<updated>2021-12-19T21:57:40Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporary runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20864</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20864"/>
		<updated>2021-12-19T21:56:15Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: Add overlaytmpfs info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Allowing temporal runtime alterations =&lt;br /&gt;
You can use &amp;lt;code&amp;gt;overlayfs&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;tmpfs&amp;lt;/code&amp;gt; built into Alpine&#039;s init script to allow changes in the rootfs which will be automatically reverted upon reboot.&amp;lt;br&amp;gt;&lt;br /&gt;
To make use this just add &amp;lt;code&amp;gt;overlaytmpfs&amp;lt;/code&amp;gt; to the kernel boot options in &amp;lt;code&amp;gt;refind.conf&amp;lt;/code&amp;gt;, e.g.:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@,ro,noatime resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 overlaytmpfs quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20863</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20863"/>
		<updated>2021-12-19T21:44:57Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;$SNP/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf &amp;quot;$SNP/etc/&amp;quot;&lt;br /&gt;
# chroot &amp;quot;$SNP&amp;quot; /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20129</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20129"/>
		<updated>2021-09-03T07:47:37Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    printf %s\\n &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20128</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20128"/>
		<updated>2021-09-02T07:02:52Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20127</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=20127"/>
		<updated>2021-09-02T07:00:51Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/snapshots/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/snapshots/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19836</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19836"/>
		<updated>2021-07-10T15:46:24Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOT_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19835</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19835"/>
		<updated>2021-07-10T15:44:55Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -t sysfs sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOT_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19834</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19834"/>
		<updated>2021-07-10T15:42:29Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOT_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({ \&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19833</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19833"/>
		<updated>2021-07-10T15:40:37Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example script to mutate the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOT_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshots&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will get you into the root shell chrooted into the new snapshot, where you can apply any change you like.&amp;lt;br&amp;gt;&lt;br /&gt;
If chroot shell exits with an error, there will be no switch to the new snapshots. This means you can manually discard changes while in the chroot by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit 1&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Deleting unused snapshots =&lt;br /&gt;
Unused snapshots can be garbage-collected by:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/syscln&lt;br /&gt;
# chmod +x /sbin/syscln&lt;br /&gt;
# vi /sbin/syscln&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/bin/sh&lt;br /&gt;
ROOTVOLMNT=&amp;quot;/vol/root&amp;quot;&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 &amp;quot;$ROOTVOLMNT&amp;quot;&lt;br /&gt;
&lt;br /&gt;
SNAPSHOTS=$({&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/snapshots/&amp;quot; -maxdepth 1 -mindepth 1&lt;br /&gt;
    find &amp;quot;$ROOTVOLMNT/current/&amp;quot; -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | xargs -n 1 readlink -f&lt;br /&gt;
} | sort | uniq -u)&lt;br /&gt;
&lt;br /&gt;
if [ -z &amp;quot;$SNAPSHOTS&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Nothing to remove&amp;quot;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 -n 1 -I {} find {} -maxdepth 1 -mindepth 1 \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 btrfs subvolume delete&lt;br /&gt;
    echo &amp;quot;$SNAPSHOTS&amp;quot; \&lt;br /&gt;
        | tr \\n \\0 \&lt;br /&gt;
        | xargs -r -0 rm -r&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19832</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19832"/>
		<updated>2021-07-10T13:27:30Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
Example script to change the the system:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;# touch /sbin/sysmut&lt;br /&gt;
# chmod +x /sbin/sysmut&lt;br /&gt;
# vi /sbin/sysmut&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;ROOTVOLMNT=&amp;quot;/vol/root&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t btrfs -o rw,noatime UUID=b9fa2e7b-e128-4e64-861a-3ee1d33d0676 &amp;quot;$ROOTVOLMNT&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs subvolume snapshot &amp;quot;$ROOTVOLMNT/current/0/@&amp;quot; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
sed &amp;quot;s#CURRENT_SNAPSHOT_PATH#/snapshots/$NEWSNAPSHOTS#g&amp;quot; &amp;quot;$ROOTVOLMNT/fstab&amp;quot; &amp;gt; &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/etc/fstab&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t proc none &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/proc&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -t sysfs sys &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/sys&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mount -o bind /dev &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@/dev&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
chroot &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; /bin/sh -c &#039;mount -a; sh; APPLY=$?; umount -a; exit $APPLY&#039; &amp;amp;&amp;amp; \&lt;br /&gt;
btrfs property set -ts &amp;quot;$ROOTVOLMNT/snapshots/$NEWSNAPSHOTS/@&amp;quot; ro true &amp;amp;&amp;amp; \&lt;br /&gt;
NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/links/$NEWLINKS&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/0&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/0&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/1&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/1&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/2&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
cp -P &amp;quot;$ROOTVOLMNT/current/2&amp;quot; &amp;quot;$ROOTVOLMNT/links/$NEWLINKS/3&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mkdir -p &amp;quot;$ROOTVOLMNT/next&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
ln -sfn &amp;quot;./links/$NEWLINKS&amp;quot; &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
mv &amp;quot;$ROOTVOLMNT/next/current&amp;quot; &amp;quot;$ROOTVOLMNT/&amp;quot; \&lt;br /&gt;
&amp;amp;&amp;amp; echo &amp;quot;Switched to the new snapshot&amp;quot; \&lt;br /&gt;
|| echo &amp;quot;Changes discarded&amp;quot;&lt;br /&gt;
umount &amp;quot;$ROOTVOLMNT&amp;quot;&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19831</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19831"/>
		<updated>2021-07-10T12:15:06Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&lt;br /&gt;
&lt;br /&gt;
# mount -a&lt;br /&gt;
&lt;br /&gt;
# mv /etc/resolv.conf /tmp/&lt;br /&gt;
# ln -s /tmp/resolv.conf /etc/resolv.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# umount -a&lt;br /&gt;
# exit&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Todo|}}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19612</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19612"/>
		<updated>2021-06-19T21:45:47Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/iwd&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@iwd $SNP/var/lib/iwd&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Now, configure the system, start with setting a password for the root:&lt;br /&gt;
&amp;lt;pre&amp;gt;# passwd root&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared and configured we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/iwd&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Todo|}}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19611</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19611"/>
		<updated>2021-06-19T21:24:33Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Include anything else in &amp;lt;code&amp;gt;/var&amp;lt;/code&amp;gt; that should be mutable, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@iwd&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/iwd btrfs subvol=/commons/@var@lib@iwd,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@var@lib@iwd&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/iwd&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@iwd $SNP/var/lib/iwd&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt; in this example, so you will not end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/iwd&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Todo|}}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19610</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19610"/>
		<updated>2021-06-19T21:05:57Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file, remember to replace example UUIDs with your own for &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; (btrfs partition) and &amp;lt;code&amp;gt;resume&amp;lt;/code&amp;gt; (swap partition):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;root=UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|&amp;lt;code&amp;gt;&amp;quot;ROOT&amp;quot;&amp;lt;/code&amp;gt; is the &amp;lt;code&amp;gt;PARTLABEL&amp;lt;/code&amp;gt; of the btrfs partition. You may also use &amp;lt;code&amp;gt;PARTUUID&amp;lt;/code&amp;gt; instead. To get both &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; package can be used. &amp;lt;code&amp;gt;blkid&amp;lt;/code&amp;gt; included in busybox does not provide this information. }}&lt;br /&gt;
To add rEFInd to UEFI, &amp;lt;code&amp;gt;efibootmgr&amp;lt;/code&amp;gt; is a suitable tool:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add efibootmgr&lt;br /&gt;
# efibootmgr --create --disk /dev/sda --part 1 --loader /EFI/refind/refind_x64.efi --label &amp;quot;rEFInd&amp;quot; --verbose&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; is our disk device and &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; is the number of partition containing &amp;lt;code&amp;gt;rEFInd&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= Updating or altering the system =&lt;br /&gt;
{{Todo|}}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19581</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19581"/>
		<updated>2021-06-05T13:24:11Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Preparation =&lt;br /&gt;
You should have bootable Alpine media. The process to obtain it decribed on the [[Installation|installation page.]]&lt;br /&gt;
{{Warning|Since version available in Alpine repositories lacks btrfs driver you should download rEFInd on [https://www.rodsbooks.com/refind/getting.html the official website] and copy it to installation media}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Unpack prepared rEFInd archive and copy relevant files to &amp;lt;code&amp;gt;/mnt/EFI/&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# unzip refind-bin-version.zip&lt;br /&gt;
# cp -r refind-bin-version/refind /mnt/EFI/&lt;br /&gt;
# cd /mnt/EFI/refind&amp;lt;/pre&amp;gt;&lt;br /&gt;
Delete all unnecessary files - everything that is not for your CPU architecture:&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -r drivers_aa64 drivers_ia32 tools_aa64 tools_ia32 refind_aa64.efi refind_ia32.efi&amp;lt;/pre&amp;gt;&lt;br /&gt;
Rename config file and edit it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mv refind.conf-sample refind.conf&lt;br /&gt;
# vi refind.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
And append following to the end of the file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
menuentry &amp;quot;Alpine Linux&amp;quot; {&lt;br /&gt;
    icon /EFI/refind/icons/os_linux.png&lt;br /&gt;
    volume &amp;quot;ROOT&amp;quot;&lt;br /&gt;
    loader /current/0/@/boot/vmlinuz-lts&lt;br /&gt;
    initrd /current/0/@/boot/initramfs-lts&lt;br /&gt;
    options &amp;quot;root=/dev/sda2 rootfstype=btrfs rootflags=subvol=/current/0/@ resume=/dev/sda3 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 1&amp;quot; {&lt;br /&gt;
        loader /current/1/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/1/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;/dev/sda2 rootfstype=btrfs rootflags=subvol=/current/1/@ resume=/dev/sda3 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 2&amp;quot; {&lt;br /&gt;
        loader /current/2/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/2/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;/dev/sda2 rootfstype=btrfs rootflags=subvol=/current/2/@ resume=/dev/sda3 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    submenuentry &amp;quot;Boot fallback 3&amp;quot; {&lt;br /&gt;
        loader /current/3/@/boot/vmlinuz-lts&lt;br /&gt;
        initrd /current/3/@/boot/initramfs-lts&lt;br /&gt;
        options &amp;quot;/dev/sda2 rootfstype=btrfs rootflags=subvol=/current/3/@ resume=/dev/sda3 ro,noatime quiet splash&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;/dev/sda2&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;/dev/sda3&amp;lt;/code&amp;gt; are the nodes of &amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SWAP&amp;lt;/code&amp;gt; devices.&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19580</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19580"/>
		<updated>2021-06-05T12:31:02Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&lt;br /&gt;
# mkdir /mnt/EFI&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download rEFInd binaries. You can do it on [https://www.rodsbooks.com/refind/getting.html the official website], but since it is available in Alpine repositories, there is a much simpler way to obtain it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r1.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; or replace &amp;lt;code&amp;gt;0.13.2-r1&amp;lt;/code&amp;gt; with [https://pkgs.alpinelinux.org/package/edge/testing/x86_64/refind latest version number].&amp;lt;br&amp;gt;&lt;br /&gt;
Unpack it and copy relevant content to &amp;lt;code&amp;gt;/mnt/EFI&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# tar -xzf refind-0.13.2-r1.apk&lt;br /&gt;
# cp -r /usr/share/refind /mnt/EFI/&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19579</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19579"/>
		<updated>2021-06-05T11:31:43Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag and unmounting root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&lt;br /&gt;
# umount /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Note|The example demonstrated here only make use of rEFInd bootloader as it is relatively easy to install manually and generally easy to use. You may however setup any prefered bootloader given it supports btrfs and can be configured to boot specific snapshot.}}&lt;br /&gt;
Mount the EFI partition:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t vfat /dev/sda1 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
Download rEFInd binaries. You can do it on [https://www.rodsbooks.com/refind/getting.html the official website], but since it is available in Alpine repositories, there is a much simpler way to obtain it:&lt;br /&gt;
&amp;lt;pre&amp;gt;# wget https://dl-cdn.alpinelinux.org/alpine/edge/testing/x86_64/refind-0.13.2-r1.apk&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; or replace &amp;lt;code&amp;gt;0.13.2-r1&amp;lt;/code&amp;gt; with [https://pkgs.alpinelinux.org/package/edge/testing/x86_64/refind latest version number].&amp;lt;br&amp;gt;&lt;br /&gt;
Unpack it:&lt;br /&gt;
&amp;lt;pre&amp;gt;tar -xzf refind-0.13.2-r1.apk&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Talk:Immutable_root_with_atomic_upgrades&amp;diff=19573</id>
		<title>Talk:Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Talk:Immutable_root_with_atomic_upgrades&amp;diff=19573"/>
		<updated>2021-05-31T23:04:42Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: Created page with &amp;quot;User:Sb1, regarding diskless mode note: thanks for reminding about it, but it&amp;#039;s a different setup with its own features, so I reduced the note, but left the links. Similar...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[User:Sb1]], regarding diskless mode note: thanks for reminding about it, but it&#039;s a different setup with its own features, so I reduced the note, but left the links. Similar guide to diskless chroot setup would be really appreciated btw. [[User:Mvsn|Mvsn]] ([[User talk:Mvsn|talk]]) 23:04, 31 May 2021 (UTC)&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19572</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19572"/>
		<updated>2021-05-31T22:52:24Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
{{Note|Alpine Linux can also boot from RAM in &#039;&#039;&#039;diskless mode&#039;&#039;&#039; (see [[Installation]]) which supports preserving changes between reboots using [[lbu]].}}&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Due to root being immutable during operation, it&#039;s recommended to install package &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt; to support changing network connection. In this case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and a link to the new resolv.conf location should be created: &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt;.&lt;br /&gt;
You may also use static DNS, but this would make your network activity directly identifiable to the DNS server provider, therefore it&#039;s not recommended.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Todo| }}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19533</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19533"/>
		<updated>2021-05-22T14:02:31Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
{{Note|Due to root being immutable, is recommended to use static DNS server, like &amp;lt;code&amp;gt;1.1.1.1&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt;. In latter case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and link &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt; should be created.}}&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finish editing snapshot by setting &amp;lt;code&amp;gt;ro&amp;lt;/code&amp;gt; flag:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs property set -ts &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot; ro true&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Todo| }}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19532</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19532"/>
		<updated>2021-05-22T12:30:19Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&amp;lt;br&amp;gt;&lt;br /&gt;
After that you should install any package you may need on first boot.&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
{{Note|Due to root being immutable, is recommended to use static DNS server, like &amp;lt;code&amp;gt;1.1.1.1&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt;. In latter case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and link &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt; should be created.}}&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Todo| }}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19531</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19531"/>
		<updated>2021-05-22T12:27:17Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware, kernel and btrfs packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts btrfs-progs&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
These steps prepare kernel and generate &amp;lt;code&amp;gt;initramfs&amp;lt;/code&amp;gt; which later will be used to boot from our first snapshot.&lt;br /&gt;
{{Warning|In case your PC only has wireless connection you should also install and configure any suitable networking software, like &amp;lt;code&amp;gt;iwd&amp;lt;/code&amp;gt;, so you won&#039;t end up severed from network on your first boot.}}&lt;br /&gt;
{{Note|Due to root being immutable, is recommended to use static DNS server, like &amp;lt;code&amp;gt;1.1.1.1&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;openresolv&amp;lt;/code&amp;gt;. In latter case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and link &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt; should be created.}}&lt;br /&gt;
With the snapshot prepared we can chroot out of it and unmount everything:&lt;br /&gt;
&amp;lt;pre&amp;gt;# exit&lt;br /&gt;
# umount $SNP/dev&lt;br /&gt;
# umount $SNP/proc&lt;br /&gt;
# umount $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# umount $SNP/home&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...and inject fstab:&lt;br /&gt;
&amp;lt;pre&amp;gt;# sed &amp;quot;s#CURRENT_SNAPSHOTS_PATH#/snapshots/20210411212549sdBXyLxg#g&amp;quot; /mnt/fstab &amp;gt; &amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@/etc/fstab&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Bootloader installation =&lt;br /&gt;
{{Todo| }}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19530</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19530"/>
		<updated>2021-05-22T11:49:11Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware and kernel packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Note|Due to root being immutable, is recommended to use static DNS server, like &amp;lt;code&amp;gt;1.1.1.1&amp;lt;/code&amp;gt; or use &amp;lt;code&amp;gt;resolvconf&amp;lt;/code&amp;gt;. In latter case &amp;lt;code&amp;gt;/etc/resolvconf.conf&amp;lt;/code&amp;gt; should have &amp;lt;code&amp;gt;resolv_conf{{=}}/tmp/resolv.conf&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/etc/resolv.conf&amp;lt;/code&amp;gt; should be moved to &amp;lt;code&amp;gt;/tmp/resolv.conf&amp;lt;/code&amp;gt; and link &amp;lt;code&amp;gt;ln -sfn /tmp/resolv.conf /etc/resolv.conf&amp;lt;/code&amp;gt; should be created.}}&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19526</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19526"/>
		<updated>2021-05-22T11:06:11Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware and kernel packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts&amp;lt;/pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19525</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19525"/>
		<updated>2021-05-22T11:05:44Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As soon as you in chroot, define repositories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir -p /etc/apk&lt;br /&gt;
# echo &amp;quot;https://dl-cdn.alpinelinux.org/alpine/latest-stable/main&amp;quot; &amp;gt; /etc/apk/repositories&amp;lt;pre&amp;gt;&lt;br /&gt;
Example shows only &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;, but you should also add &amp;lt;code&amp;gt;testing&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;community&amp;lt;/code&amp;gt; if you need any packages in those.&amp;lt;br&amp;gt;&lt;br /&gt;
Now it&#039;s time for firmware and kernel packages:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add -U linux-firmware linux-lts&amp;lt;pre&amp;gt;&lt;br /&gt;
You may want to change &amp;lt;code&amp;gt;linux-firmware&amp;lt;/code&amp;gt; to a custom set of firmware packages suitable for you system, for example &amp;lt;code&amp;gt;linux-firmware-amd linux-firmware-amd-ucode linux-firmware-amdgpu linux-firmware-ath10k linux-firmware-qca&amp;lt;/code&amp;gt; for typical AMD laptop.&amp;lt;br&amp;gt;&lt;br /&gt;
It is also important to add &amp;lt;code&amp;gt;btrfs&amp;lt;/code&amp;gt; feature to &amp;lt;code&amp;gt;mkinitfs.conf&amp;lt;/code&amp;gt; and run &amp;lt;code&amp;gt;mkinitfs&amp;lt;/code&amp;gt; manually:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /etc/mkinitfs/mkinitfs.conf&lt;br /&gt;
# mkinitfs&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19524</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19524"/>
		<updated>2021-05-22T09:26:44Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount -o bind /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount -o bind /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount -o bind /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount -o bind /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount -o bind /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19523</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=19523"/>
		<updated>2021-05-22T08:25:15Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# export SNP=&amp;quot;/mnt/snapshots/20210411212549sdBXyLxg/@&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# mount -o bind /dev $SNP/dev&lt;br /&gt;
# mount -t proc none $SNP/proc&lt;br /&gt;
# mount -o bind /sys $SNP/sys&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/tmp&lt;br /&gt;
# mount /mnt/commons/@var@tmp $SNP/var/tmp&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/cache&lt;br /&gt;
# mount /mnt/commons/@var@cache $SNP/var/cache&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/log&lt;br /&gt;
# mount /mnt/commons/@var@log $SNP/var/log&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/var/lib/flatpak&lt;br /&gt;
# mount /mnt/commons/@var@lib@flatpak $SNP/var/lib/flatpak&lt;br /&gt;
&lt;br /&gt;
# mkdir -p $SNP/home&lt;br /&gt;
# mount /mnt/commons/@home $SNP/home&lt;br /&gt;
&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18954</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18954"/>
		<updated>2021-05-03T14:06:00Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
In this guide it is assumed that you have a fresh UEFI system without OS and just managed to boot into live Alpine using USB flash drive or CD.&lt;br /&gt;
The first step is creating partition table on your HDD/SSD target device (&amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt; here):&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -o bind /dev /mnt/dev&lt;br /&gt;
# mount -t proc none /mnt/proc&lt;br /&gt;
# mount -o bind /sys /mnt/sys&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18950</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18950"/>
		<updated>2021-05-02T21:14:00Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
The first step is creating partition table on target device &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&amp;lt;br&amp;gt;&lt;br /&gt;
Considering that installation is done from Alpine system, we only need following parts of [[Alpine_Linux_in_a_chroot|the process]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk -X https://dl-cdn.alpinelinux.org/alpine/latest-stable/main -U --allow-untrusted -p /mnt/snapshots/20210411212549sdBXyLxg/@ --initdb add alpine-base&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can setup basic chroot to complete the installation process:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -o bind /dev /mnt/dev&lt;br /&gt;
# mount -t proc none /mnt/proc&lt;br /&gt;
# mount -o bind /sys /mnt/sys&lt;br /&gt;
# cp -L /etc/resolv.conf /mnt/etc/&lt;br /&gt;
# chroot /mnt /bin/sh&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18949</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18949"/>
		<updated>2021-05-02T16:49:27Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
The first step is creating partition table on target device &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; &lt;br /&gt;
Stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to to &amp;lt;code&amp;gt;/current/0/@&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1/@&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/lib/flatpak btrfs subvol=/commons/@var@lib@flatpak,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts with, for example, &amp;lt;code&amp;gt;/snapshots/20210411212549sdBXyLxg&amp;lt;/code&amp;gt;, and the result will be piped into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&amp;lt;br&amp;gt;&lt;br /&gt;
Root btrfs volume structure mounted on &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| | |--@var@tmp&lt;br /&gt;
| | |--@var@cache&lt;br /&gt;
| | |--@var@log&lt;br /&gt;
| | |--@var@lib@flatpak&lt;br /&gt;
| | |--@home&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Base system install =&lt;br /&gt;
With the directory strtucture prepared we can start installation of a basic Alpine Linux system.&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18861</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18861"/>
		<updated>2021-04-24T14:46:29Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
The first step is creating partition table on target device &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/next&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/commons&amp;lt;/pre&amp;gt;&lt;br /&gt;
Stores common non-snapshotting subvolumes, is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
We may populate it right away:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@tmp&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@cache&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@var@log&lt;br /&gt;
# btrfs subvolume create /mnt/commons/@home&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you use flatpak, you may also want to keep it&#039;s directory separate:&lt;br /&gt;
&amp;lt;pre&amp;gt;# btrfs subvolume create /mnt/commons/@var@lib@flatpak&amp;lt;/pre&amp;gt;&lt;br /&gt;
Next, most important directories:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/snapshots&amp;lt;/pre&amp;gt; Stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /mnt/links&amp;lt;/pre&amp;gt; stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to &amp;lt;code&amp;gt;/current/0&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt; in the btrfs root.&amp;lt;br&amp;gt;&lt;br /&gt;
Get UUIDs of the partitions first:&lt;br /&gt;
&amp;lt;pre&amp;gt;# blkid &amp;gt; /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now edit fstab accordingly:&lt;br /&gt;
&amp;lt;pre&amp;gt;# vi /mnt/fstab&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 / btrfs subvol=CURRENT_SNAPSHOTS_PATH/@,ro,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/tmp btrfs subvol=/commons/@var@tmp,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/cache btrfs subvol=/commons/@var@cache,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /var/log btrfs subvol=/commons/@var@log,rw,noatime 0 0&lt;br /&gt;
UUID=b9ff5e7b-e128-4e64-861a-2fdd794a9828 /home btrfs subvol=/commons/@home,rw,noatime 0 0&lt;br /&gt;
# UUID=2FE6-837A /boot/efi vfat rw,noatime,discard 0 2&lt;br /&gt;
tmpfs /tmp tmpfs mode=1777,noatime,nosuid,nodev,size=2G 0 0&lt;br /&gt;
UUID=f0239163-9d46-47c1-67a4-3ee1d63d0676 swap swap rw,noatime,discard 0 0&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CURRENT_SNAPSHOTS_PATH&amp;lt;/code&amp;gt; will be replaced by scripts and the result put into &amp;lt;code&amp;gt;/etc/fstab&amp;lt;/code&amp;gt; of a created &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt; snapshot during new generation preparations.&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18857</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18857"/>
		<updated>2021-04-20T14:13:25Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
The first step is creating partition table on target device &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount -t btrfs /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
Now we should create file structure that would provide reliable atomic system upgrades.&amp;lt;br&amp;gt;&lt;br /&gt;
Start with following directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;# mkdir /mnt/commons&amp;lt;/code&amp;gt; stores common non-snapshotting subvolumes.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;# mkdir /mnt/next&amp;lt;/code&amp;gt; stores next &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; link.&amp;lt;br&amp;gt;&lt;br /&gt;
Latter is necessary due to how busybox &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; does atomic link replacement.&amp;lt;br&amp;gt;&lt;br /&gt;
Next, most important directories:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;# mkdir /mnt/snapshots&amp;lt;/code&amp;gt; stores directories containing snapshots belonging to one generation.&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;# mkdir /mnt/links&amp;lt;/code&amp;gt; stores generations of directories containing links to snapshot generations.&amp;lt;br&amp;gt;&lt;br /&gt;
Let&#039;s create first generation and populate it with one OS root snapshot &amp;lt;code&amp;gt;@&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWSNAPSHOTS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/$NEWSNAPSHOTS&amp;quot;&lt;br /&gt;
# btrfs subvolume create /mnt/$NEWSNAPSHOTS/@&amp;lt;/pre&amp;gt;&lt;br /&gt;
Populate &amp;lt;code&amp;gt;links&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# NEWLINKS=&amp;quot;$(date -u +&amp;quot;%Y%m%d%H%M%S&amp;quot;)$(cat /dev/urandom | tr -dc &#039;a-zA-Z&#039; | fold -w 8 | head -n 1)&amp;quot;&lt;br /&gt;
# mkdir &amp;quot;/mnt/links/$NEWLINKS&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/0&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/1&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/2&amp;quot;&lt;br /&gt;
# ln -s &amp;quot;../../snapshots/$NEWSNAPSHOTS&amp;quot; &amp;quot;/mnt/links/$NEWLINKS/3&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can have as many links as you like, just apply changes to rEFInd config and upgrade scripts described below accordingly.&amp;lt;br&amp;gt;&lt;br /&gt;
Link that will point to latest links generation:&lt;br /&gt;
&amp;lt;pre&amp;gt;# ln -s &amp;quot;./links/$NEWLINKS&amp;quot; /mnt/current&amp;lt;/pre&amp;gt;&lt;br /&gt;
This setup allows us to just have static rEFInd config that points to &amp;lt;code&amp;gt;/current/0&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/current/1&amp;lt;/code&amp;gt;, etc. while the actual underlying boot environment will change with each upgrade.&amp;lt;br&amp;gt;&lt;br /&gt;
But how will fs mounting services know which snapshot generation is currently loaded?&amp;lt;br&amp;gt;&lt;br /&gt;
The answer is common &amp;lt;code&amp;gt;fstab&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;WIP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--0&lt;br /&gt;
| | | |--1&lt;br /&gt;
| | | |--2&lt;br /&gt;
| | | |--3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18854</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18854"/>
		<updated>2021-04-19T09:27:27Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= Partitioning disks =&lt;br /&gt;
The first step is creating partition table on target device &amp;lt;code&amp;gt;/dev/sda&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apk add gptfdisk&lt;br /&gt;
# gdisk /dev/sda&lt;br /&gt;
&amp;gt; o ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;gt; w ↵&lt;br /&gt;
&amp;gt; y ↵&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can define the partitions:&lt;br /&gt;
&amp;lt;pre&amp;gt;# cgdisk /dev/sda&amp;lt;/pre&amp;gt;&lt;br /&gt;
Partition creation process consists of several steps:&lt;br /&gt;
# Start sector - you can safely use default value by pressing ↵&lt;br /&gt;
# Size&lt;br /&gt;
# Type (as hex code) - EFI is ef00, Linux filesystem is 8300, Swap is 8200.&lt;br /&gt;
Result table:&lt;br /&gt;
&amp;lt;pre&amp;gt;Part.     #     Size        Partition Type            Partition Name&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
1               200.0 MiB   EFI System                EFI&lt;br /&gt;
2               200.0 GiB   Linux filesystem          ROOT&lt;br /&gt;
3               32.0 GiB    Linux swap                SWAP&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;ROOT&amp;lt;/code&amp;gt; partition name will later be used in rEFInd configuration to identify boot volume.&lt;br /&gt;
Next step is creating filesystems:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkfs.vfat -F32 /dev/sda1&lt;br /&gt;
# mkfs.btrfs /dev/sda2&lt;br /&gt;
# mkswap /dev/sda3&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now we can mount our root volume:&lt;br /&gt;
&amp;lt;pre&amp;gt;# mount /dev/sda2 /mnt&amp;lt;/pre&amp;gt;&lt;br /&gt;
= File system structure =&lt;br /&gt;
&amp;lt;pre&amp;gt;|--mnt&lt;br /&gt;
| |--commons&lt;br /&gt;
| |--current&lt;br /&gt;
| |--fstab&lt;br /&gt;
| |--links&lt;br /&gt;
| | |--20210411213742qwrXAJBz&lt;br /&gt;
| | | |--gen_0&lt;br /&gt;
| | | |--gen_1&lt;br /&gt;
| | | |--gen_2&lt;br /&gt;
| | | |--gen_3&lt;br /&gt;
| |--next&lt;br /&gt;
| |--snapshots&lt;br /&gt;
| | |--20210411212549sdBXyLxg&lt;br /&gt;
| | | |--@&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Help_talk:Editing&amp;diff=18853</id>
		<title>Help talk:Editing</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Help_talk:Editing&amp;diff=18853"/>
		<updated>2021-04-18T22:28:58Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: Permission issue got resolved&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18852</id>
		<title>Immutable root with atomic upgrades</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Immutable_root_with_atomic_upgrades&amp;diff=18852"/>
		<updated>2021-04-18T22:27:40Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: Immutable root with atomic upgrades&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
{|style=&amp;quot;width: 33em; padding:2px; margin:0; margin-bottom:10px; background-color:#f6f6f6; border:1px solid #aaa; -moz-border-radius-bottomright: 0.5em; -moz-border-radius-bottomleft: 1em; border-radius-bottomright: 0.5em; border-radius-bottomleft: 1em; -webkit-border-bottom-right-radius: 0.5em; -webkit-border-bottom-left-radius: 1em;&amp;quot;&lt;br /&gt;
|&amp;lt;div style=&amp;quot;font-size: 1.5em; font-weight:bold;&amp;quot;&amp;gt; [[Image:Underconstruction_clock_icon_gray.svg‎|64px|left|link=]] This page is a work in progress ... &amp;lt;/div&amp;gt;&amp;lt;p style=&amp;quot;text-align: center; font-size: 87%;&amp;quot;&amp;gt;This page is still being developed.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== What? ===&lt;br /&gt;
This article provides a basic guide to setting up a read-only-root-based Alpine Linux system with several boot environments and atomic upgrades using rEFInd and btrfs.&lt;br /&gt;
=== Why? ===&lt;br /&gt;
Read-only root and atomic upgrades with ability to easily rollback or boot previous configurations is a concept that got some popularity recently. Distributions providing and promoting such features, for example, are [https://silverblue.fedoraproject.org/ Fedora Silverblue], [https://microos.opensuse.org/ Opensuse MicroOS], [https://nixos.org NixOS] and [https://guix.gnu.org GNU Guix].&lt;br /&gt;
While Alpine Linux has it&#039;s killer features it lacks mentioned above ones on default setup. This is a proof of concept that it&#039;s possible to implement them in a minimal way on a minimal system.&lt;br /&gt;
= File system structure =&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
	<entry>
		<id>https://wiki.alpinelinux.org/w/index.php?title=Help_talk:Editing&amp;diff=18851</id>
		<title>Help talk:Editing</title>
		<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/w/index.php?title=Help_talk:Editing&amp;diff=18851"/>
		<updated>2021-04-18T22:26:59Z</updated>

		<summary type="html">&lt;p&gt;Mvsn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Is it possible to contribute to wiki? I wanted to write an article but I can&#039;t create any page. Any attempt to post first WIP part gets marked as harmful.&lt;/div&gt;</summary>
		<author><name>Mvsn</name></author>
	</entry>
</feed>