Skip to content

Instantly share code, notes, and snippets.

@ehrenfeu
Last active May 15, 2026 12:03
Show Gist options
  • Select an option

  • Save ehrenfeu/28441927a3e02e0c54fbad08675e9f73 to your computer and use it in GitHub Desktop.

Select an option

Save ehrenfeu/28441927a3e02e0c54fbad08675e9f73 to your computer and use it in GitHub Desktop.
Convert a fresh Ubuntu 24.04 to encrypted btrfs and set up the Desktop environment

Ubuntu 24.04 using encrypted btrfs

Loosely based on the instructions from paadza and the (therein referenced) script from Tosten Bronger.

🚧 Note: 🚧 the script is mainly geared towards readability and doesn't contain any kind of error handling (except that set -e will terminate the script upon a non-zero exit from any of the commands).

Required partitioning scheme for the installer

In the installer's disk setup part, select Manual installation, remove the presets from the boot disk, then create three partitions as follows:

  1. partition /boot, type ext4, size 1024 MB
  2. partition EFI (/boot/efi), type vfat/fat32, size 1280 MB
  3. partition /, type btrfs, size 20 GB

🚨 IMPORTANT 🚨

  • The / partition will be expanded by the script to use all available space evenutally.
  • The script assumes installation happens on /dev/nvme0n1, if that's not correct adjust the DEV_* variables!

Subvolumes and swapfile

On top of the existing setup (a single plain filesystem for / using btrfs), the script will create the following subvolumes:

  • @ - the / subvolume, all data from the plain btrfs will be moved here.
  • @home - data from the /home will be moved here.
  • @cache@ - data from /var/cache will be moved here.
  • @tmp - will be mounted as /tmp.
  • @swap - will be mounted as /swap, a btrfs-aware swapfile will be created there and mounted as swap.

New Ubuntu 24.04 Desktop Setup

Account

Groups

sudo adduser ehrenfeu docker
sudo adduser ehrenfeu adm

Packages

APT

sudo apt update
sudo apt install -y \
    bat \
    btop \
    byobu \
    dhclient \
    fish \
    gnome-tweaks \
    gp-saml-gui \
    gparted \
    guake \
    htop \
    keepassxc \
    lm-sensors \
    multitail \
    net-tools \
    network-manager-openconnect \
    network-manager-openconnect-gnome \
    nextcloud-desktop \
    nextcloud-desktop-cmd \
    openconnect \
    powertop \
    python-is-python3 \
    python3.12-venv \
    qtqr \
    sublime-merge \  # repo seems to be preconfigured from ITS!
    tree \
    vim \
    wireguard-tools

Snap

snap install \
    chromium \
    chromium-ffmpeg \
    ghostty

Flatpak

flatpak install \
    net.nokyan.Resources \
    org.gnome.Loupe \
    re.sonny.Tangram

Ente Auth

Download .deb from ente-releases.

Starship, Pixi, uv, et al

mkdir -pv ~/.local/bin
for URI in \
    https://github.com/starship/starship/releases/download/v1.25.0/starship-x86_64-unknown-linux-gnu.tar.gz \
    https://github.com/prefix-dev/pixi/releases/download/v0.67.2/pixi-x86_64-unknown-linux-musl.tar.gz \
    https://releases.astral.sh/github/uv/releases/download/0.11.7/uv-x86_64-unknown-linux-gnu.tar.gz
do
    wget $URI
    PKG="$(basename --suffix=".tar.gz" $URI)"
    ZIP="${PKG}.tar.gz"
    tar xzf "$ZIP"
    rm "$ZIP"
    if [ -d "$PKG" ] ; then
        mv "$PKG"/* .
        rm -r "$PKG"
    fi
done
PIXI_PACK="0.7.6"
mkdir -pv "$HOME/.local/bin/pixi-pack-${PIXI_PACK}"
cd "$HOME/.local/bin/pixi-pack-${PIXI_PACK}"
wget "https://github.com/Quantco/pixi-pack/releases/download/v${PIXI_PACK}/pixi-pack-x86_64-unknown-linux-musl"
chmod +x pixi-pack-x86_64-unknown-linux-musl
cd "$HOME/.local/bin"
ln -fsv "pixi-pack-${PIXI_PACK}/pixi-pack-x86_64-unknown-linux-musl" "pixi-pack"

Fonts

mkdir -pv ~/.local/share/fonts/InconsolataGo
cd ~/.local/share/fonts/InconsolataGo
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.4.0/InconsolataGo.zip
unzip InconsolataGo.zip
rm InconsolataGo.zip
fc-cache -fv

Shell

echo 'set -g fish_greeting' > ~/.config/fish/conf.d/no-greeting.fish
echo 'starship init fish | source' > ~/.config/fish/conf.d/starship.fish
echo 'uv generate-shell-completion fish | source' > ~/.config/fish/conf.d/completion-uv.fish
echo 'pixi completion --shell fish | source' > ~/.config/fish/conf.d/completion-pixi.fish
starship preset gruvbox-rainbow -o ~/.config/starship.toml

Ansible

Keyring integration for the vault

ACS_PATH=~/.local/share/ansible-contrib-scripts
mkdir -pv $ACS_PATH
git clone https://github.com/ansible-community/contrib-scripts "$ACS_PATH"
cd ~/.local/bin
ln -s ../share/ansible-contrib-scripts/vault/vault-keyring-client.py

Settings

πŸ‘» Ghostty

πŸ“‘ NOTE: setting command explicitly is a feasible workaround as long as we can't use chsh to change the default shell (domain-accounts are not stored in /etc/passwd but fetch this type of information from LDAP/AD).

echo 'command = /usr/bin/fish
shell-integration = fish
shell-integration-features = ssh-env,ssh-terminfo
' > .config/ghostty/config.ghostty

πŸ–₯️ Guake

Configure:

  • Shell: /usr/bin/fish
  • Colorscheme:
    • Japanesque
    • Peppermint (x)
      • gsettings set guake.style.background transparency 88
    • Neutron

Actually... Guake doesn't like Wayland:

  • fractional scaling blurs the terminal
  • appear on mouse-screen doesn't work (follows XWayland apps instead...)

The better solution seems to be ddterm for now, maybe it's worth checking out this Medium post on the terminal-rabbit-hole.

GNOME

gsettings set org.gnome.desktop.default-applications.terminal exec '/snap/bin/ghostty'
gsettings set org.gnome.desktop.default-applications.terminal exec-arg ''
gsettings set org.gnome.desktop.interface document-font-name 'Sans 12'
gsettings set org.gnome.desktop.interface font-antialiasing 'rgba'
gsettings set org.gnome.desktop.interface font-hinting 'slight'
gsettings set org.gnome.desktop.interface font-name 'Ubuntu Sans Light 9 @wght=500'
gsettings set org.gnome.desktop.interface font-rgba-order 'rgb'
gsettings set org.gnome.desktop.interface monospace-font-name 'InconsolataGo Nerd Font Mono 12'
gsettings set org.gnome.desktop.calendar show-weekdate true

Shell Extensions

VS Code

Firefox

  • Create profiles Work and Personal, then for each:
    • Import bookmarks and passwords.
    • Select theme.
    • Install Addons.

Chromium

rsync -au irrlicht:snap/chromium/ snap/chromium/

πŸ”πŸ—« Threema

flatpak remote-add --if-not-exists flathub \
    https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --from \
    https://releases.threema.ch/flatpak/threema-desktop/ch.threema.threema-desktop.flatpakref

πŸ“±πŸ—« Signal Desktop

wget -O- https://updates.signal.org/desktop/apt/keys.asc |
    gpg --dearmor |
    sudo tee /usr/share/keyrings/signal-desktop-keyring.gpg > /dev/null
wget -O- https://updates.signal.org/static/desktop/apt/signal-desktop.sources |
    sudo tee /etc/apt/sources.list.d/signal-desktop.sources
sudo apt update
sudo apt install signal-desktop
#!/bin/sh
set -e
MP_BTRFS=/mnt/btrfs
MP_ROOT=/mnt/root
MP_HOME=/mnt/home
KEYSLOT_SIZE=32m
DEV_ROOT=/dev/nvme0n1p3
DEV_BOOT=/dev/nvme0n1p1
DEV_EFI=/dev/nvme0n1p2
# cleanup leftover mounts from installer:
umount --quiet /target/boot/efi
umount --quiet /target/boot
umount --quiet /target/cdrom
umount --quiet /target
# create mountpoints
mkdir -pv $MP_BTRFS
mkdir -pv $MP_ROOT
mkdir -pv $MP_HOME
# create subvolumes
mount $DEV_ROOT $MP_ROOT
cd $MP_ROOT
# create a snapshot "@" from current btrfs, will become the new "/" mount later:
btrfs subvolume snapshot . @
# create @home subvol
btrfs subvolume create @home
# re-mount "bare" btrfs, root and home subvolumes:
umount $MP_ROOT
mount $DEV_ROOT $MP_BTRFS
mount $DEV_ROOT -o subvol=@ $MP_ROOT
mount $DEV_ROOT -o subvol=@home $MP_HOME
# move home dirs from @ subvol to @home:
mv $MP_ROOT/home/* $MP_HOME/
# delete everything but the subvolumes from the btrfs-root:
find $MP_BTRFS -maxdepth 1 \! -name "@*" \! -name . -exec rm -Rf {} \;
# make space for the key slot
btrfs filesystem resize -$KEYSLOT_SIZE $MP_ROOT
umount $MP_ROOT
umount $MP_HOME
umount $MP_BTRFS
# identify disk and partition number:
DISK=$(lsblk --noheadings --output pkname $DEV_ROOT)
PART_NUM=$(lsblk --noheadings --output partn $DEV_ROOT | tr -d "[:space:]")
echo $DISK
echo $PART_NUM
# encrypt the btrfs volume:
cryptsetup reencrypt --encrypt --type luks2 --reduce-device-size $KEYSLOT_SIZE $DEV_ROOT
# resize the partition to use all available space:
parted --script /dev/$DISK resizepart $PART_NUM 100%
# open the encrypted volume, will create a mapping as /dev/mapper/<name> where
# <name> is the last parameter of the "cryptsetup open" command:
cryptsetup open $DEV_ROOT root
# mount btrfs-root and enlarge it to the new partition size resized above:
mount /dev/mapper/root $MP_BTRFS
btrfs filesystem resize max $MP_BTRFS
# mount OS-root (subvol "@") from encrypted btrfs:
mount /dev/mapper/root -o subvol=@ $MP_ROOT
# identify UUID of btrfs root volume block device, store in crypttab:
UUID_ROOT=$(blkid --output export $DEV_ROOT| grep ^UUID=)
echo "root $UUID_ROOT none luks,discard" > $MP_ROOT/etc/crypttab
# update target fstab with new mounts:
sed --in-place \
's#^.* / btrfs defaults 0 1$#/dev/mapper/root / btrfs defaults,subvol=@ 0 1#' \
$MP_ROOT/etc/fstab
echo "/dev/mapper/root /home btrfs defaults,subvol=@home 0 0" >> $MP_ROOT/etc/fstab
# NOTE: the last digit in an fstab line is "fs_passno", defining the priority
# during an fsck run - official btrfs documentation recommends to set this to 0!
# mount required stuff for chroot:
mount -t proc proc $MP_ROOT/proc
mount -t sysfs sys $MP_ROOT/sys
mount --bind /dev $MP_ROOT/dev
mount --bind /run $MP_ROOT/run
mount $DEV_BOOT $MP_ROOT/boot
mount $DEV_EFI $MP_ROOT/boot/efi
# update grub and install the required cryptsetup package:
chroot $MP_ROOT update-grub
chroot $MP_ROOT apt install -y cryptsetup-initramfs
# create additional subvolumes for /tmp, /var/cache and swap:
btrfs subvolume create $MP_BTRFS/@swap
btrfs subvolume create $MP_BTRFS/@cache
btrfs subvolume create $MP_BTRFS/@tmp
btrfs subvolume list $MP_BTRFS
# move existing file from /var/cache to @cache subvol:
mv $MP_ROOT/var/cache/* $MP_BTRFS/@cache/
# create a swapfile on the @swap subvol:
btrfs filesystem mkswapfile --size 32G $MP_BTRFS/@swap/swapfile
mkdir $MP_ROOT/swap
# add subvols / swap to fstab:
echo "
/dev/mapper/root /tmp btrfs defaults,subvol=@tmp 0 0
/dev/mapper/root /var/cache btrfs defaults,subvol=@cache 0 0
/dev/mapper/root /swap btrfs defaults,subvol=@swap 0 0
/swap/swapfile none swap sw 0 0
" >> $MP_ROOT/etc/fstab
# NOTE: probably using device UUIDs instead of /dev/mapper/xx paths would be a
# good idea as they'd be independent of mapper names!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment