Skip to content

Instantly share code, notes, and snippets.

@noghartt
Last active April 19, 2024 18:28
Show Gist options
  • Star 33 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save noghartt/8388f7d8543e3eb1777cb6ed4a3d7807 to your computer and use it in GitHub Desktop.
Save noghartt/8388f7d8543e3eb1777cb6ed4a3d7807 to your computer and use it in GitHub Desktop.
My Arch Linux installation - systemd-boot + LUKS + LVM + Btrfs + swapfile + xmonad

My Arch Linux installation

Before the installation

Save things that you need to be saved

  1. The public and private GPG keys:
gpg --export --armor > public.key
gpg --export-secret-key --armor > private.key
  1. Your shared GPG and SSH files.
  2. Your data.

REMEMBER: if you have some dotfiles or other projects in your PC, make sure that you have pushed your changes.

Make sure the drive security is not frozen

If you are running a pre-installed system, you can cleaning all the storage (in my case, a SSD NVMe), you can wipe your storage to clean all things inside it. To do it on a SSD NVMe, you can do these things:

Run the following command:

nvme id-ctrl /dev/nvme0 -H |grep "Format \|Crypto Erase\|Sanitize"

Erase all data from your storage (SSD NVMe)

You can run the 'nvme sanitize` command to erase all data from the storage. You can see an example of the command here:

nvme sanitize <device> -a <action>

The possible actions that you can use is:

-a <action>
--sanact=<action>
    Sanitize Action
    000b - Reserved
    001b - Exit Failure Mode
    010b - Start a Block Erase sanitize operation
    011b - Start an Overwrite sanitize operation
    100b - Start a Crypto Erase sanitize operation

ATTENTION: When you running the sanitize command, you should replace your binary value by their decimal part, e.g. 000b = 0, 001b = 1, 010b = 2, etc...

References

Wipe on an Empty Disk

Create a temporary encrypted container on the complete device to be encrypted

cryptsetup open --type plain -d /dev/urandom /dev/nvme0n1 to_be_wiped

Wipe the container with zeros

dd if=/dev/zero of=/dev/mapper/to_be_wiped bs=4096 status=progress

Installation

Disk partitioning (UEFI)

The partiotining that follows the concerns the UEFI installation.

Introduction

First of all, I have differente names for the partitions, but don't be lost for so little:

Mount point Partition name Partition type Bootable flags Size
/boot /dev/nvme0n1p1 EFI System Yes 1 Gb
/ /dev/nvme0n1p2 Linux LVM No Remainder of the device

Where / will be a LVM-encrypted partition having a group volume containing a physical volume and rootas a logical volume.

Finally, as mentioned in the introduction, we will format the / volume as Btrfs and create two sub-volume:

  1. /root
  2. swap
  3. /home

Creation of the File System

In order to know the name of your disk, it is necessary to list the partition tables for the specified device:

fdisk -l

Let's select our disk to build the table:

cfdisk /dev/nvme0n1

Then create a new empty GPTpartition table.

And now, you can setup all the partiions in a "visual" way.

Setup the Disk Encryption

In order to enable disk encryption, we will first create a root LUKS volume, open it and then format it.

In a brief explaination, LUKS is a container format that will be used to encrypt containers, where our encryption key will be stored.

Creation of a root LUKS volume

To encrypt our / partition, we will use the cryptsetup tool:

cryptsetup --hash sha512 --use-random --verify-passhphrase luksFormat /dev/nvme0n1p2
Are you sure? YES
Enter passhphrase (twice)

Opening the root LUKS volume as block device

The / partition being encrypted, we will open the LUKS container on /dev/nvme0n1p2 disk and name it cryptlvm:

cryptsetup open /dev/nvme0n1p2 cryptlvm
Enter passhphrase

The decrypted container is now available at /dev/mapper/cryptlvm.

Setup the LVM

LVM is a logical volume manager for the Linux kernel. It is thank to to it that we can easily resize our partitions if necessary.

Create a physical volume on top of the opened LUKS container

$ pvcreate /dev/mapper/cryptlvm

Add the previously created physical volume to a volume group

$ vgcreate vg /dev/mapper/cryptlvm

Create the swap logical volume on the volume group

Create the root logical volume on the volume group

$ lvcreate -l 100%FREE vg -n root

Formatting the filesystem

$ mkfs.fat -F32 /dev/nvme0n1p1
$ mkfs.btrfs -L btrfs /dev/mapper/vg-root

Btrfs subvolumes

Subvolumes are part of the filesystem with its own and independnet file/directory hierarchy, where each subvolume can share file extents.

Create Btrfs subvolumes

$ mount /dev/mapper/vg-root /mnt
$ btrfs subvolume create /mnt/root
$ btrfs subvolume create /mnt/home
$ umount /mnt

Mounting Btrfs subvolumes

$ SSD_MOUNTS="autodefrag,compress=lzo,discard,noatime,nodev,rw,space_cache,ssd"
$ mount -o subvol=root,$SSD_MOUNTS /dev/mapper/vg-root /mnt

$ mkdir -p /mnt/{boot,home}

$ mount -o discard,noatime,nodev,noexec,nosuid,rw /dev/nvme0n1p1 /mnt/boot
$ mount -o $SSD_MOUNTS,nosuid,subvol=home
Some details about the option on -o flag:
  • autodefrag: enable automatic file defragmentation for small random writes in files with a maximum file size of 64K;
  • compress=lzo: compresses files with the lzo type which is a lossless data compression algorithm that is focused on decompression speed;
  • discard: enable discarding of freed file blocks using TRIM operation (useful for SSD devices);
  • noatime: allows measurable performance gains by eliminating the need for the system to write to the file system for files that are simply read;
  • nodev: disallows creating and accessing device nodes (used in particular for special files in /dev);
  • noexec: does not allow the execution of executable binaries in the mounted file system;
  • nosuid: specifies that the filesystem cannot contain set userid files;
  • rw: allows reading and writing;
  • space_cache: control the free space cache. This greatly improves performance when reading block group free space into memory;
  • ssd: by default, Btrfs will enable or disable SSD allocation heuristics depending on whether a rotational or non-rotational device is in use.

Base system

Update the mirrors

$ pacman -S reflector
$ reflector --threads 8 --protocol http --protocol https --verbose --sort rate --country Brazil --save /etc/pacman.d/mirrorlist

Installation of the packages onto a given root file system

$ pacstrap /mnt base linux-firmware linux-zen lvm2 sudo man-db base-devel linux-tools git hdsentinel fwupd sudo

Configuration of the system

We will configure the system base in order to have a decent environment.

Generate a fstab file

$ genfstab -U /mnt >> /mnt/etc/fstab

Change root into the new system

$ arch-chroot /mnt

Create the swap file

To create the swap file, we will create a zero length file, set the No_COW attribute and make sure compression is disabled:

$ truncate -s 0 /swapfile
$ chattr +C /swapfile
$ btrfs property set /swapfile compression none

Is recommended to set the size of swap file being 4-8GB if you don't intend to use hibernation (suspend-to-disk):

$ dd if=/dev/zero of=/swapfile bs=1M count=16384 status=progress && sync

Set the permission for the file (a world-readable swap file is a huge local vulnerability):

$ chmod 600 /swapfile

Format the /swapfile file to swap and activate it:

$ mkswap /swapfile
$ swapon /swapfile

Finally, add an entry for the swap file in the fstab:

$ echo "/swapfile none swap defaults 0 0" >> /etc/fstab

Set the timezone

Install the Network Time Protocol and enable it as daemon:

$ pacman -S ntp
$ systemctl enable ntpd

Create a symlink of the timezone:

$ ln -sf /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime

Generate /etc/adjtime:

$ hwclock --systohc

Network configuration

Install and enable network management daemon:

$ pacman -S networkmanager
$ systemctl enable NetworkManager

So add a hostname to the machine:

$ echo ThinkPad > /etc/hostname

NOTE: Remember that you can replace ThinkPad with whatever you like.

Add matching entries to /etc/hosts:

127.0.0.1		localhost
::1					localhost
127.0.1.1		ThinkPad.localdomain	ThinkPad

Set the root passsword

$ passwd

Create the main user

$ useradd -mG storage,wheel -s /bin/bash someone
$ passwd someone

Finally, change the /etc/sudoers file according to the config that you want to deal with sudo command.

Create a initial ramdisk environment

$ nano /etc/mkinitcpio.conf

You should the HOOKS field with these things:

...
HOOKS=(base systemd autodetect keyboard sd-vconsole modconf block sd-encrypt lvm2 filesystems btrfs)
...

Finally, recreate the initramfs image:

mkinitcpio -p linux-zen

Setup the boot manager

Install systemd-boot into the EFI system partition

bootctl install

Configuring the loader

On /boot/loader/loader.confinsert this:

default arch
timeout 10
console-mode max
editor no
Some details about the parameters:
  • default: default entry to select;
  • timeout: menu timeout in second, useful to allow people who have multiple operating systems;
  • console-mode: changes UEFI console mode;
  • editor: whether to enable the kernel parameters editor or not. Strongly recommended to set this option to no to avoid bypass root password and gain root access.

Adding the loader

First install the microcode:

$ pacman -S amd-ucode

After it, insert on /boot/loader/entries/arch.conf this:

title Arch Linux
linux /vmlinuz-linux
initrd /intel-ucode.img
initrd /initramfs-linux.img
options rd.luks.name=$(blkid /dev/nvme0n1p2 -s UUID -o value)=cryptlvm rd.luks.options=discard root=/dev/mapper/vg-root resume=/dev/mapper/vg-root rootfstype=btrfs resume_offset=$(filefrag -v /swapfile | sed '4q;d' | awk '{print $4}' | cut -d'.' -f1) quiet nowatchdog splash rw

Graphical environment

Install the window manager

First of all, install xorg:

$ pacman -S xorg-server

Finally, install the xmonad:

$ pacman -S xmonad xmonad-contrib

Install a display manager

$ pacman -S lightdm
$ systemctl enable lightdm

Install a AUR helper

$ git clone https://aur.archlinux.org/yay.git
$ cd yay
$ makepkg -sri
$ cd .. && rm -r yay

Keyring

pacman -S gnome-keyring seahorse

Terminal

$ pacman -S alacritty tmux

Shell

$ pacman -S fish

Image manipulations

pacman -S gimp inkscape

NTFS

$ pacman -S ntfs-3g

Sound system

$ pacman -S pulseaudio alsa-utils pavucontrol alsamixer

Other

In order to save the read and write cycles of the SSD and thus extend the lifetime of the SSD, the browser profile can be moved in RAM with profile-sync-daemon:

$ pacman -S profile-sync-daemon

Finally, enable and activate the service:

$ systemctl --user enable psd --now

After installation

Enable fstrim

First enable and start fstrim timer. On Arch-based system you can do it running this commands:

$ sudo systemctl enable fstrim.timer
$ sudo systemctl start fstrim.timer

After this, if you running with LUKS encryption, you should add some things on the Arch entry from systemd-boot, inside /boot/loader/entries/arch.conf, add it:

...
options rd.luks.name=<UUID>=cryptlvm ... rd.luks.options=discard
...

Reboot the system. And now, you can check if it's running correctly executing this command: lsblk --discard. If has no-zero entries on DISC-GRAN and DISC-MAX columns means TRIM is enabled.

References

Disable watchdog

Append on your boot parameters (probably /boot/loader/entries/arch.conf) the following option:

...
options rd.luks.name=<UUID>=cryptlvm ... nowatchdog
...

After it, you can optionally disable the loading of the module responsible of the hardware watchdog, too. Do it blacklisting the related module.

Creating a .conf inside /etc/modprobe.d/ and append a line for the module you want to blacklist with blacklist keyword. Here an example:

# /etc/modprobe.d/nowatchdog.conf
blacklist iTCO_wdt

References

Troubleshooting

Thinkpad T14 Gen 1 Brazillian keyboard layout

If you are using this installation on a Thinkpad T14 Gen 1 with a Brazillian keyboard layout (ABNT2) like me, you should use this rule to turns the keyboard usable. You should run this command:

setxkbmap -model thinkpad60 -layout br -variant anbt2

Or just paste the "text" below on the following path: /etc/X11/xorg.conf.d:

Section "InputClass"
	Identifier "system-keyboard"
	MatchIsKeyboard "on"
	Option "XkbLayout" "br"
	Option "XkbModel" "thinkpad60"
	Option "XkbVariant" "abnt2"
EndSection

Other installation tutorials

@Jumaku
Copy link

Jumaku commented Jul 11, 2023

Hi there,

thanks a lot for sharing this guide. It is actually exactly what I was looking for.
While following the steps I stumbled over following errors:

$ mount -o $SSD_MOUNTS,nosuid,subvol=home

mount: bad usage`

$ btrfs property set /swapfile compression none

ERROR: failed to set compression for /swapfile: Invalid argument`

$ mkinitcpio -p linux

has to be mkinitcpio -p linux-zen ?

Well I ended up beeing not able to boot the system and my knowledge is too limited regarding arch to fix it. Thanks for the efforts. Think maybe about refreshing this guide or putting it down, since some people can waste quite some time with it.

@noghartt
Copy link
Author

Hey, @Jumaku, how are you?

In case, I assume that this setup is a little outdated, I did some changes on it, but until the last time I use it, it was working.

Wrong mount (bad usage)

$ mount -o $SSD_MOUNTS,nosuid,subvol=home

mount: bad usage

For this error, could you share your hardware specs? This $SSD_MOUNTS works with my hardware, that is a SSD NMVe, did you have the same hardware? Linux handle these partitions in different way, like /dev/nvme0n1, or /dev/sda, etc.

Could you confirm how you're targeting these devices?

Handling swapfile on btrfs

$ btrfs property set /swapfile compression none

ERROR: failed to set compression for /swapfile: Invalid argument

For this error, it seems that your swapfile hasn't been correctly created. I found a thread on Arch Linux forum that can helps you, see here.

Handling kernel version on mkinitcpio

has to be mkinitcpio -p linux-zen ?

Yes, you're right! It has a typo on my tutorial, I'll fix it, thanks. You run the mkinitcpio based on the installed kernel, in case, for this tutorial: linux-zen.

@Wolfsrabe
Copy link

Hi @noghartt
The first time i got the error "wrong fs type, bad option, bad superblock" while mounting with the parameters in your "SSD_MOUNTS" variable. After researching i think the problem is the parameter "space_cache". If you use "space_cache=v2" it works.
But i dont know details and the differences between this parameters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment