Skip to content

Debian

Installation

Boot from the installation media described in Bootable Installation Media. The steps below perform a manual installation using debootstrap with a Btrfs filesystem, multiple subvolumes, a swap file, and hibernation support 1. Start by dropping into a root shell.

sudo su

Identify the target disk, wipe it, and partition it.

lsblk -p
export DISK="/dev/vda"
apt update && apt install gdisk -y

# Wipe the disk and create a fresh GPT layout
sgdisk -Z $DISK
sgdisk -og $DISK

# Create EFI system partition (1 GiB)
sgdisk -n 1::+1G -t 1:ef00 -c 1:'ESP' $DISK

# Create root partition (rest of the disk)
sgdisk -n 2:: -t 2:8300 -c 2:'LINUX' $DISK

# Format the EFI partition with FAT32 filesystem
mkfs.fat -F32 -n EFI ${DISK}1

# Format the main partition with Btrfs filesystem
mkfs.btrfs -L DEBIAN ${DISK}2

# Verify the filesystem formats
lsblk -po name,size,fstype,fsver,label,uuid $DISK

Create Btrfs subvolumes and mount everything under /mnt.

# Mount the Btrfs root
mount -v ${DISK}2 /mnt

# Create essential subvolumes
btrfs subvolume create /mnt/@           # Root filesystem
btrfs subvolume create /mnt/@home       # User home data
btrfs subvolume create /mnt/@opt        # Optional software
btrfs subvolume create /mnt/@cache      # Cache data
btrfs subvolume create /mnt/@libvirt    # Virtual machines
btrfs subvolume create /mnt/@log        # Log files
btrfs subvolume create /mnt/@spool      # Spool data
btrfs subvolume create /mnt/@tmp        # Temporary files
btrfs subvolume create /mnt/@swap       # Swap file location

umount -v /mnt

# Define mount options for optimal Btrfs performance
BTRFS_OPTS="defaults,noatime,space_cache=v2,compress=zstd:1"

# Mount the root subvolume
mount -vo $BTRFS_OPTS,subvol=@ ${DISK}2 /mnt

# Create directories for other subvolumes
mkdir -vp /mnt/{home,opt,boot/efi,var/{cache,lib/libvirt,log,spool,tmp,swap}}

# Mount the remaining subvolumes
mount -vo $BTRFS_OPTS,subvol=@home ${DISK}2 /mnt/home
mount -vo $BTRFS_OPTS,subvol=@opt ${DISK}2 /mnt/opt
mount -vo $BTRFS_OPTS,subvol=@cache ${DISK}2 /mnt/var/cache
mount -vo $BTRFS_OPTS,subvol=@libvirt ${DISK}2 /mnt/var/lib/libvirt
mount -vo $BTRFS_OPTS,subvol=@log ${DISK}2 /mnt/var/log
mount -vo $BTRFS_OPTS,subvol=@spool ${DISK}2 /mnt/var/spool
mount -vo $BTRFS_OPTS,subvol=@tmp ${DISK}2 /mnt/var/tmp

# Mount swap subvolume without compression or CoW for reliability
mount -vo defaults,noatime,subvol=@swap ${DISK}2 /mnt/var/swap

# Mount the EFI partition
mount -v ${DISK}1 /mnt/boot/efi

# Verify the mounts
lsblk -po name,size,fstype,uuid,mountpoints $DISK

Install the base system with debootstrap, bind-mount kernel filesystems, and write /etc/fstab.

apt install -y debootstrap

# Install base Debian 13 (Trixie) system into /mnt
debootstrap --arch=amd64 trixie /mnt http://deb.debian.org/debian

# Mount necessary filesystems for the chroot environment
for dir in dev proc sys run; do
    mount -v --rbind "/${dir}" "/mnt/${dir}"
    mount -v --make-rslave "/mnt/${dir}"
done

# Mount EFI variables (for UEFI systems)
mount -v -t efivarfs efivarfs /mnt/sys/firmware/efi/efivars

# Get UUIDs for Btrfs and EFI partitions
BTRFS_UUID=$(blkid -s UUID -o value ${DISK}2) ; echo $BTRFS_UUID
EFI_UUID=$(blkid -s UUID -o value ${DISK}1) ; echo $EFI_UUID

# Create /etc/fstab inside the target system
cat > /mnt/etc/fstab << EOF
UUID=$BTRFS_UUID /                btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@ 0 0
UUID=$BTRFS_UUID /home            btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@home 0 0
UUID=$BTRFS_UUID /opt             btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@opt 0 0
UUID=$BTRFS_UUID /var/cache       btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@cache 0 0
UUID=$BTRFS_UUID /var/lib/libvirt btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@libvirt 0 0
UUID=$BTRFS_UUID /var/log         btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@log 0 0
UUID=$BTRFS_UUID /var/spool       btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@spool 0 0
UUID=$BTRFS_UUID /var/tmp         btrfs defaults,noatime,space_cache=v2,compress=zstd:1,subvol=@tmp 0 0
UUID=$BTRFS_UUID /var/swap        btrfs defaults,noatime,subvol=@swap 0 0
UUID=$EFI_UUID   /boot/efi        vfat  defaults,noatime 0 2
EOF

cat /mnt/etc/fstab

Enter the chroot and configure the system. Everything until exit runs inside the chroot.

chroot /mnt /bin/bash
# Set the system hostname
echo "computer" > /etc/hostname

# Configure /etc/hosts
cat > /etc/hosts << EOF
127.0.0.1       localhost
127.0.1.1       $(cat /etc/hostname)

::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters
EOF

# Set the timezone
ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime

# Install and configure locales
apt install -y locales
dpkg-reconfigure locales

# Configure APT sources for Debian 13 (Trixie) with contrib and non-free
cat > /etc/apt/sources.list << EOF
deb http://deb.debian.org/debian trixie main contrib non-free non-free-firmware
deb-src http://deb.debian.org/debian trixie main contrib non-free non-free-firmware

deb http://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware
deb-src http://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware

deb http://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware
deb-src http://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware
EOF

apt update

# Install kernel, system tools, and essential utilities
apt install -y linux-image-amd64 linux-headers-amd64 \
    firmware-linux firmware-linux-nonfree \
    grub-efi-amd64 efibootmgr network-manager \
    btrfs-progs sudo vim bash-completion

Set up a swap file with hibernation support. Adjust count to 1.5× your RAM size in MiB.

# Prepare swap file — disable CoW and compression, both required for a Btrfs swap file
truncate -s 0 /var/swap/swapfile
chattr +C /var/swap/swapfile
btrfs property set /var/swap compression none

# 12 GB swap for 8 GB RAM (1.5× for hibernation)
dd if=/dev/zero of=/var/swap/swapfile bs=1M count=12288 status=progress
chmod 600 /var/swap/swapfile
mkswap -L SWAP /var/swap/swapfile

# Add swap to fstab and activate it
echo "/var/swap/swapfile none swap defaults 0 0" >> /etc/fstab
swapon /var/swap/swapfile
swapon -v

# Configure GRUB for hibernation
SWAP_OFFSET=$(btrfs inspect-internal map-swapfile -r /var/swap/swapfile)
BTRFS_UUID=$(blkid -s UUID -o value ${DISK}2)
GRUB_CMD="quiet resume=UUID=$BTRFS_UUID resume_offset=$SWAP_OFFSET"
echo "GRUB_CMDLINE_LINUX_DEFAULT=\"$GRUB_CMD\"" >> /etc/default/grub
update-grub

# Configure initramfs for hibernation
cat > /etc/initramfs-tools/conf.d/resume << EOF
RESUME=/var/swap/swapfile
RESUME_OFFSET=$SWAP_OFFSET
EOF

update-initramfs -u -k all

Create a user and install GRUB, then exit the chroot.

useradd -m -G sudo,adm -s /bin/bash -c "User" user
passwd user
id user

grub-install \
  --target=x86_64-efi \
  --efi-directory=/boot/efi \
  --bootloader-id=debian \
  --recheck
update-grub

exit

Unmount and reboot into the installed system.

umount -vR /mnt
reboot

Post Installation

Update the system and install essential packages.

sudo apt update && sudo apt upgrade
sudo apt install neovim build-essential p7zip-full curl git git-lfs btop fastfetch \
    unrar libavcodec-extra ttf-mscorefonts-installer ffmpeg ssh network-manager-gnome

Install Miniconda.

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /tmp/Miniconda3-latest-Linux-x86_64.sh
bash /tmp/Miniconda3-latest-Linux-x86_64.sh

Set up snapshot support. Create a Btrfs subvolume for Firefox's profile directory so snapshots don't bloat with browser cache.

btrfs subvolume create ~/.mozilla
sudo btrfs subvolume list /

sudo apt install -y snapper btrfs-assistant inotify-tools git make

sudo snapper -c root create-config /
sudo snapper -c home create-config /home

sudo snapper -c root set-config ALLOW_USERS=$USER SYNC_ACL=yes
sudo snapper -c home set-config ALLOW_USERS=$USER SYNC_ACL=yes

sudo snapper list-configs
snapper -c root get-config
snapper -c home get-config
snapper ls
snapper -c home ls

Install grub-btrfs to make GRUB automatically detect Btrfs snapshots as boot entries.

Note

gawk is a required dependency. Without it, grub-btrfs falls back to mawk which fails to extract snapshot UUIDs.

sudo apt install gawk

cd /var/tmp
git clone https://github.com/Antynea/grub-btrfs.git
cd grub-btrfs
# Set kernel parameters so snapshots boot in overlay mode
sed -i.bkp \
  '/^#GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS=/a \
GRUB_BTRFS_SNAPSHOT_KERNEL_PARAMETERS="rd.live.overlay.overlayfs=1"' \
  config
sudo make install
sudo systemctl enable --now grub-btrfsd.service

sudo systemctl status snapper-boot.timer
sudo systemctl status snapper-timeline.timer
sudo systemctl status snapper-cleanup.timer

sudo snapper -c home set-config TIMELINE_CREATE=no

Configure the keyboard layout.

sudo apt install kbd console-setup
localectl status
sudo dpkg-reconfigure console-setup
sudo dpkg-reconfigure keyboard-configuration
cat /etc/default/keyboard

Install X11, build dependencies for suckless tools, and fonts.

sudo apt install xserver-xorg xinit
sudo apt install libx11-dev libxft-dev libxinerama-dev
sudo apt install xfonts-terminus fonts-noto-core fonts-noto-cjk fonts-noto-color-emoji fonts-noto-extra fonts-font-awesome

Install dwm, st, and dmenu.

mkdir -p ~/.local/src/

git clone https://git.suckless.org/dwm ~/.local/src/dwm
cd ~/.local/src/dwm && make clean && sudo make install && cd ~

git clone git://git.suckless.org/st ~/.local/src/st
cd ~/.local/src/st && make clean && sudo make install && cd ~

git clone git://git.suckless.org/dmenu ~/.local/src/dmenu
cd ~/.local/src/dmenu && make clean && sudo make install && cd ~

echo "exec dwm" > ~/.xinitrc

Enable audio and Bluetooth.

sudo apt install pipewire pipewire-alsa pipewire-pulse wireplumber
sudo apt install bluez
sudo systemctl enable --now bluetooth

  1. https://sysguides.com/install-debian-13-with-btrfs