<?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=Krum</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=Krum"/>
	<link rel="alternate" type="text/html" href="https://wiki.alpinelinux.org/wiki/Special:Contributions/Krum"/>
	<updated>2026-04-30T13:52:32Z</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=25839</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=25839"/>
		<updated>2023-11-30T08:48:24Z</updated>

		<summary type="html">&lt;p&gt;Krum: Correct some spelling and grammar mistakes, minor changes to wording in some areas&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;
		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 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>Krum</name></author>
	</entry>
</feed>