How to Install Arch Linux with Full Disk Encryption and LVM Using systemd-boot
This guide describes how to install Arch Linux with full disk encryption, Logical Volume Management (LVM), and the minimalist systemd-boot bootloader. The setup uses two NVMe drives, as this reflects my specific hardware configuration. If you’re using only one drive, the process remains mostly the same—just adapt the LUKS and LVM steps accordingly.
Let’s get started.
Hardware for this Guide#
- CPU: AMD Ryzen 9 5900X
- GPU: AMD Radeon RX 6900 XT
- Memory: 32GB
- Two NVMe drives, each 1TB
- UEFI-enabled system (BIOS must support UEFI)
Preparing the Terrain#
Before embarking on the installation, you’ll need a bootable USB drive with Arch Linux.
Download the latest ISO from the official Arch Linux website and flash it onto your USB stick. Once plugged in, identify its device path using the lsblk command:
lsblk
Here’s how the output looked on my machine:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdb 8:16 1 14.9G 0 disk
├─sdb1 8:17 1 650M 0 part /run/media/user/ARCH_202505
├─sdb2 8:18 1 64M 0 part
└─sdb3 8:19 1 300K 0 part
My USB drive is 16GB and recognized as /dev/sdb. Now it’s time to write the ISO image onto it using dd. Caution: This command will irrevocably erase all data on the drive, so double-check the device path before proceeding.
sudo dd bs=4M if=/path/to/archlinux.iso of=/dev/sdb status=progress oflag=sync
Wait for the process to complete — it may take a few minutes. Once it’s done, you’re ready to boot from the USB drive and dive into the real journey.
At this point, we should be in front of a prompt:
root@archiso ~ #
This is our root prompt, and I’ll be shortening that to $ in this post.
This is an optional step, but if the console font is too small or not readable, we can set it up:
setfont latarcyrheb-sun32
We need an internet connection, so let’s configure the network. I connected via Ethernet, so everything worked out of the box. If you need Wi-Fi, you can set it up by launching iwctl (interaction mode with autocompletion). Here are some useful commands:
iwctl
station list # Display your wifi stations
station <INTERFACE> scan # Start looking for networks with a station
station <INTERFACE> get-networks # Display the networks found by a station
station <INTERFACE> connect <SSID> # Connect to a network with a station
We also need to update our system clock. Let’s use timedatectl to ensure the system clock is accurate:
timedatectl set-ntp true
To check the service status, we can use:
timedatectl status
I tend to use this personal naming convention to identify my hardware with different operating systems, you will see this name around when setting things up in a few places, especially when setting up the volumes in LVM. Swap it out for your own.
Disk Partitioning#
Once that’s done, we can begin building up to the installation. Everything except the /boot partition will be encrypted, ensuring a secure and flexible system layout for power users who value control and privacy.
Here is my actual disk layout (run lsblk to inspect yours):
Note: The /home logical volume spans both disks, so its device-mapper entry may appear under both cryptlvm1 and cryptlvm2 in lsblk.
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 931.5G 0 disk
├─nvme0n1p1 259:2 0 1G 0 part /boot
└─nvme0n1p2 259:3 0 930.5G 0 part
└─cryptlvm1 253:0 0 930.5G 0 crypt
├─cryptlvm1-swap 253:2 0 8G 0 lvm [SWAP]
├─cryptlvm1-root 253:3 0 32G 0 lvm /
└─cryptlvm1-home 253:4 0 1.8T 0 lvm /home
nvme1n1 259:1 0 931.5G 0 disk
└─nvme1n1p1 259:4 0 931.5G 0 part
└─cryptlvm2 253:1 0 931.5G 0 crypt
└─cryptlvm1-home 253:4 0 1.8T 0 lvm /home
Note: The volume group cryptlvm1 spans across both encrypted disks. The root (/), swap, and part of the home volume reside on the first device (cryptlvm1, from nvme0n1p2). The second device (cryptlvm2, from nvme1n1p1) is added entirely to extend the /home volume.
This setup results in full disk encryption (FDE), with the sole exception of the /boot partition, which remains unencrypted to allow the system to boot.
For partitioning, I used the gdisk utility to create and manage the GPT layout:
gdisk /dev/nvme0n1
You’ll enter the interactive gdisk prompt. Follow these steps:
Create GPT label (if asked) If the disk is empty or has no GPT, you may be prompted to create one. Type:
o
Confirm with y to create a new empty GPT.
Create EFI system partition (1 GB for /boot)
n
Partition number: 1
First sector: (press Enter for default)
Last sector: +1G
Hex code or GUID: ef00
w
Create encrypted LUKS container (rest of the disk)
n
Partition number: 2
First sector: (press Enter)
Last sector: (press Enter to use remaining space)
Hex code or GUID: 8e00
w
I prefer to partition the second (nvme1n1) disk (e.g., for clarity or better tooling support), you can create a single GPT partition that spans the entire disk and set its hex code to 8e00.
Steps:
gdisk /dev/nvme1n1
Then inside gdisk:
o # create new GPT if needed
n # new partition
[Enter] # default partition number
[Enter] # default start
[Enter] # default end (use full disk)
8e00 # Linux LVM filesystem (used for LUKS)
w # write and exit
Setting Up Disk Encryption#
Now that our partitions are in place, it’s time to secure the system. We’ll encrypt two devices: the second partition of the first disk (/dev/nvme0n1p2) and the entirety of the second disk (/dev/nvme1n1p1). These will be combined into a single LVM volume group that will host our root, swap, and home volumes — all protected by encryption.
Encrypt nvme0n1p2 (Arch system volume):
cryptsetup luksFormat /dev/nvme0n1p2
You’ll be prompted with:
WARNING!
========
This will overwrite data on /dev/nvme0n1p2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase:
Verify passphrase:
Then open it:
cryptsetup open /dev/nvme0n1p2 cryptlvm1
Encrypt nvme1n1p1:
cryptsetup luksFormat /dev/nvme1n1p1
Unlock it:
cryptsetup open /dev/nvme1n1p1 cryptlvm2
Setting Up LVM#
Now we’ll create one logical volume group (cryptlvm1) across both unlocked encrypted containers. Inside it, we’ll define three logical volumes: one for root, one for swap, and the rest for home.
pvcreate /dev/mapper/cryptlvm1 /dev/mapper/cryptlvm2
vgcreate cryptlvm1 /dev/mapper/cryptlvm1 /dev/mapper/cryptlvm2
Note: The names cryptlvm1 and cryptlvm2 used in this guide are just examples. You can choose whatever naming convention you prefer — just make sure to use the same name consistently throughout the setup.
Creating Logical Volumes#
Inside our volume group cryptlvm1, we’ll now define three logical volumes:
lvcreate -L 32G cryptlvm1 -n root # Root filesystem
lvcreate -L 8G cryptlvm1 -n swap # Swap volume
lvcreate -l 100%FREE cryptlvm1 -n home # All remaining space goes to home
This gives us the following logical volume structure:
/dev/cryptlvm1/root→ mounted as//dev/cryptlvm1/swap→ swap area/dev/cryptlvm1/home→ user data and home directory (spanning both encrypted disks)
Note: Depending on your LVM setup, logical volumes might appear as /dev/mapper/cryptlvm1-root, /cryptlvm1-swap, etc., or as /dev/cryptlvm1/root, depending on naming conventions. Just make sure to use consistent names in your bootloader and /etc/fstab.
You can verify the layout with:
lvs
Here is my actual output:
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
home cryptlvm1 -wi-ao---- <1.78t
root cryptlvm1 -wi-ao---- 32.00g
swap cryptlvm1 -wi-ao---- 8.00g
Formatting the Partitions#
Now that the logical volumes and boot partition are set up, it’s time to format everything so it can be used by the system. We’ll format the logical volumes inside our cryptlvm1 volume group — and the boot partition — as follows:
Format the Root Filesystem:
mkfs.btrfs /dev/cryptlvm1/root
Format the Home Partition
mkfs.btrfs /dev/cryptlvm1/home
Format the Swap Volume
mkswap /dev/cryptlvm1/swap
Format the EFI System Partition (Boot)
mkfs.fat -F32 /dev/nvme0n1p1
Mounting All Partitions#
With all partitions formatted, it’s time to mount them in preparation for installing Arch Linux.
Mount the Root Filesystem
mount /dev/cryptlvm1/root /mnt
Create and Mount the Home Directory
mount --mkdir /dev/crypt/home /mnt/home
Mount the EFI Boot Partition*
mount --mkdir /dev/nvme0n1p1 /mnt/boot
Activate Swap
swapon /dev/cryptlvm1/swap
Your filesystems are now mounted and ready. The final directory structure at this point looks like this:
/mnt
├── boot → /dev/nvme0n1p1
├── home → /dev/cryptlvm1/home
└── (root) → /dev/cryptlvm1/root
Installing the Base Arch Linux System#
Now that all partitions are mounted, it’s time to install a minimal, clean base system onto your new encrypted setup.
Install Essential Packages
We’ll install:
base → core Arch system
linux → the latest kernel
linux-firmware → drivers for modern hardware
pacstrap -K /mnt base linux linux-firmware
-K copies your current keyring into the new system to avoid signature issues during package install.
Configuring the New Installation#
Fstab:
The fstab file defines how disk partitions, other block devices, or remote filesystems should be mounted.
First, generate an fstab file:genfstab -U /mnt >> /mnt/etc/fstab
With the base system installed, it’s time to chroot into it and finalize the configuration.
Chroot into Your New System
This changes your root directory to the newly installed system, allowing you to configure it as if you had booted into it directly.
arch-chroot /mnt
Your prompt should now change to root@archiso /]# — you’re inside the new system.
fstab — Filesystem Table The fstab file defines how devices and partitions are mounted automatically during boot. We already generated it in the previous step, but it’s a good idea to inspect it:
cat /etc/fstab
Here’s an example of what it might look like with your setup:
# <file system> <mount point> <type> <options> <dump> <pass>
# Encrypted root volume
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx / ext4 rw,relatime 0 1
# EFI system partition
UUID=52CE-47A9 /boot vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 2
# Encrypted home volume
UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /home ext4 rw,relatime 0 2
# Swap is typically not listed in fstab but enabled via mkswap/swapon
Replace the placeholder UUIDs with actual values from blkid if you ever need to regenerate this file manually.
Install Essential Packages
Before we proceed, let’s install a few essential packages using pacman:
pacman -Syu sudo which man-db man-pages texinfo amd-ucode vim
Here’s a brief explanation of each package:
- sudo: A tool that allows a permitted user to execute a command as the superuser or another user, as specified by the security policy.
- which: A utility that shows the full path of shell commands. It’s useful to find where programs are installed.
- man-db: A database used by the man command to display manual pages for various commands and programs.
- man-pages: A collection of manual pages for Unix/Linux commands and system calls, providing help and documentation directly in the terminal.
- texinfo: A documentation system that enables you to create formatted info files, used for software documentation.
- amd-ucode: Microcode updates for AMD processors, which can help improve system stability and fix hardware-level bugs.
- vim: A highly configurable text editor, used for editing system configuration files (and coding) in a terminal.
Locale
Locales are used by glibc and other locale-aware programs or libraries for rendering text, correctly displaying regional monetary values, time and date formats, alphabetic idiosyncrasies, and other locale-specific standards.
I personally uncomment only en_US.UTF-8 UTF-8 in the /etc/locale.gen file. However, you may uncomment additional locales if needed — for example, someone living in Bulgaria might also enable bg_BG.UTF-8 UTF-8.
vim /etc/locale.conf
Add the following content (if not already there):
LANG="en_US.UTF-8"
Next, open /etc/locale.gen with vim to uncomment (remove the #) the required locales. For example, if you want to use English (US), uncomment these lines:
vim /etc/locale.gen
Find and uncomment (remove the #):
en_US.UTF-8 UTF-8
Once we are done, we need to generate them by running:
locale-gen
Timezone Setup
Link your chosen timezone to /etc/localtime. In my case, it’s Sofia, Bulgaria:
ln -sf /usr/share/zoneinfo/Europe/Sofia /etc/localtime
Set the hardware clock to UTC:
hwclock --systohc
This ensures your system clock is in sync with the hardware clock and consistent across reboots.
Configure the keyboard layout and (optionally) the font for your TTY environment:
vim /etc/vconsole.conf
Add the following content to the file:
KEYMAP=us
FONT=latarcyrheb-sun32
You can omit the FONT line or change KEYMAP=us to another layout if you prefer a different keymap.
Set Hostname#
To assign a name to your machine, open the /etc/hostname file in vim:
vim /etc/hostname
Enter your hostname, for example:
ryan
Configure /etc/hosts#
Open the /etc/hosts file using vim:
vim /etc/hosts
Add the following content:
127.0.0.1 localhost
::1 localhost
127.0.1.1 ryan.localdomain ryan
Replace “ryan” with your actual hostname if you chose a different one above.
Network Setup#
We will use systemd-networkd for managing network interfaces and systemd-resolved for DNS resolution, providing a minimal and native systemd-based networking setup. These components were already installed during the pacstrap step, so we can now enable and configure the services.
Enable the necessary services:
systemctl enable systemd-networkd.service
systemctl enable systemd-resolved.service
Edit the network configuration file:
vim /etc/systemd/network/20-wired.network
Add the following content:
[Match]
Name=enp7s0
[Network]
DHCP=yes
Make sure to replace enp7s0 with the actual name of your network interface. You can find it by running:
ip link
Link the DNS resolver file:
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
This will set up DHCP for the network interface and ensure proper DNS resolution with systemd-resolved.
Mkinitcpio
mkinitcpio is a utility used to create the initial RAM filesystem (initramfs) — a small, temporary root file system loaded into memory at boot time. Its purpose is to:
- Mount the real root filesystem
- Load essential kernel modules
- Handle early boot logic (e.g., unlocking encrypted volumes, assembling LVM)
First, make sure the lvm2 package is installed:
pacman -Syu lvm2
Open the configuration file:
vim /etc/mkinitcpio.conf
Locate the HOOKS line and make sure it looks like this:
HOOKS=(base systemd autodetect modconf kms keyboard sd-vconsole block sd-encrypt lvm2 filesystems fsck)
Finally, regenerate the initramfs:
mkinitcpio -P
The command output should look like this:
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
-> -k /boot/vmlinuz-linux -c /etc/mkinitcpio.conf -g /boot/initramfs-linux.img
==> Starting build: 6.14.4-arch1-2
-> Running build hook: [base]
-> Running build hook: [systemd]
-> Running build hook: [autodetect]
-> Running build hook: [keyboard]
-> Running build hook: [sd-vconsole]
-> Running build hook: [block]
==> WARNING: Possibly missing firmware for module: wd719x
==> WARNING: Possibly missing firmware for module: aic94xx
-> Running build hook: [sd-encrypt]
-> Running build hook: [lvm2]
-> Running build hook: [filesystems]
-> Running build hook: [fsck]
==> Generating module dependencies
==> Creating gzip-compressed initcpio image: /boot/initramfs-linux.img
==> Creating gzip-compressed initcpio image: /boot/initramfs-linux-fallback.img
==> Image generation successful
Don’t worry about those two warnings, the XPS 13 doesn’t have any hardware on board that needs those drivers.
Bootloader Installation#
Exit chroot and install the bootloader:
exit
bootctl --esp-path=/mnt/boot install
If you encounter an error (due to a potential bug), follow the steps below to resolve it:
Fixing the bug (if any error occurs):
Reformat the EFI partition and reinstall the bootloader:
mkfs.fat -F32 /dev/nvme0n1p1
mount /dev/nvme0n1p1 /mnt/boot
bootctl --esp-path=/mnt/boot install
Chroot into the new system and install the bootloader again:
arch-chroot /mnt
bootctl install
Configure the Loader#
Create the loader configuration file using vim:
vim /boot/loader/loader.conf
Add the following content:
default arch
timeout 3
editor 0
default arch: Sets Arch Linux as the default boot entry.timeout 3: Waits for 3 seconds before automatically booting.editor 0: Disables the on-boot edit prompt (set to1if you want to enable it for debugging purposes).
Create the Boot Entry#
The boot entry tells systemd-boot how to start your Arch Linux system. It defines:
- Which kernel to load
- Which initramfs to use
- How to unlock encrypted volumes and mount the root filesystem
Since this setup uses two encrypted disks (one for the root volume / and another for the extended LVM that includes /home), you need to explicitly specify both UUIDs in the boot entry. It is critical to specify both UUIDs; if you omit one, the system will not know how to unlock both encrypted containers, and booting will fail.
How to Find Your UUIDs#
First, use the blkid command to list the UUIDs of your partitions:
blkid
Example output (you will have something similar):
/dev/nvme0n1p2: UUID="11111111-aaaa-bbbb-cccc-111111111111" TYPE="crypto_LUKS"
/dev/nvme1n1p1: UUID="22222222-dddd-eeee-ffff-222222222222" TYPE="crypto_LUKS"
Copy the UUIDs into the boot entry using vim:
Open the arch.conf file in vim:
vim /boot/loader/entries/arch.conf
Use vim to insert the UUIDs into the options line:
- Press
:to enter command mode in vim. - Type
.!blkidand press Enter. This command will output the UUIDs of your partitions directly into the vim buffer.
:.!blkid
This will append the result of blkid directly into the vim editor. You will see output like:
/dev/nvme0n1p2: UUID="11111111-aaaa-bbbb-cccc-111111111111" TYPE="crypto_LUKS"
/dev/nvme1n1p1: UUID="22222222-dddd-eeee-ffff-222222222222" TYPE="crypto_LUKS"
Copy the relevant UUIDs:
- Now, simply copy the UUIDs from the
blkidoutput (in this case,11111111-aaaa-bbbb-cccc-111111111111and22222222-dddd-eeee-ffff-222222222222).
Edit the options line to specify the correct UUIDs for your LUKS-encrypted partitions.
Update the options line to look like this, replacing the UUIDs you copied:
options rd.luks.name=11111111-aaaa-bbbb-cccc-111111111111=cryptlvm1 rd.luks.name=22222222-dddd-eeee-ffff-222222222222=cryptlvm2 root=/dev/cryptlvm1/root rw
Remove the blkid output (optional):
After copying the UUIDs, you can delete the unnecessary blkid output in vim. Simply press d to delete lines in normal mode or use dd to delete the whole line.
Create the Boot Entry File#
Add the following content:
title Arch Linux
linux /vmlinuz-linux
initrd /amd-ucode.img
initrd /initramfs-linux.img
options rd.luks.name=11111111-aaaa-bbbb-cccc-111111111111=cryptlvm1 rd.luks.name=22222222-dddd-eeee-ffff-222222222222=cryptlvm2 root=/dev/cryptlvm1/root rw
Explanation of Key Options#
rd.luks.name=UUID=NAME: This option unlocks the LUKS container using the specified UUID and maps it to/dev/mapper/NAME.cryptlvm1→ First disk (/dev/nvme0n1p2) containing the root volume.cryptlvm2→ Second disk (/dev/nvme1n1p1) contributing to the LVM volume group.
root=/dev/cryptlvm1/root: Specifies the root filesystem inside the unlocked volume.rw: Mounts the root filesystem as read-write.
Now is time to set a root password:
passwd
You’ll be prompted to enter and confirm the password.
Create a New User and Set Password#
Create a new user and add them to the wheel group:
useradd -G wheel -m simeon
passwd simeon
This will create a user simeon and allow administrative access via sudo.
Set the Default Editor#
Set vim as the default editor for the session:
export EDITOR=vim
Configure sudo Permissions via visudo#
To allow the user simeon to execute administrative commands, safely edit the sudoers file with visudo:
visudo
This will open the /etc/sudoers file in vim.
In vim:
-
Press
/and typewheelto search for the line. -
Use
nto jump to:#%wheel ALL=(ALL:ALL) ALL -
Press
ito enter insert mode, then remove the#at the beginning of the line. -
Press Esc, type
:wq, and press Enter to save and exit.
This line:
%wheel ALL=(ALL:ALL) ALL
Allows users in the wheel group (including simeon) to run any command with sudo, granting administrative privileges.
Note: visudo checks for syntax errors, preventing issues that might break sudo access.
Exit the Chroot and Reboot
Your system is now fully configured. The final step is to leave the chroot environment, unmount all filesystems, and reboot into your new encrypted Arch Linux installation.
Now type exit and unmount all mounted partitions.
Use -R to recursively unmount everything under /mnt:
umount -R /mnt
and then reboot
reboot
Make sure to remove the USB stick or ISO from which you booted the installer, so the system starts from the disk.
Once the system boots, unlocks the encrypted disks, and logs you into your shell:
Log in as your user.
Then check for IP address with ip a. You should see your interface (e.g. enp1s0) with an IP assigned.
Test internet connection:
ping -c 3 archlinux.org
If you see replies, your network setup is working perfectly.
Congratulations — you now have a clean, secure, fully-encrypted Arch Linux installation using LUKS, LVM, and systemd-boot.
Conclusion
At this point, your system is fully set up and ready for customization. Whether you prefer a minimal setup or want to install a full desktop environment, the choice is yours.
I currently use a customized Hyprland environment on Omarchy, which provides a fast, efficient, and highly tailored workspace. The beauty of Arch Linux is its flexibility, allowing you to create an environment that fits your exact needs, whether you opt for lightweight setups or a more feature-rich desktop experience.
Now that your system is configured, you have a solid base to explore and experiment with various tools, applications, and configurations. The possibilities are endless!