HOW TO INSTALL DEBIAN 12 FROM SCRATCH

2025-05-16
In this guide we will see how to set up a Debian system completely from scratch; that is, without using the official Debian installer. To do this we shall use the debootstrap(8) utility, which allows us to install a Debian base system in a subdirectory of another, already installed system[1]. The installation process will cover the following three scenarios:
  1. UEFI installation;
  2. BIOS installation;
  3. LUKS installation.
The former scenario is what you should typically follow for any modern system, such as laptops or desktop computers. The second option should only be used on older systems that don't support UEFI or on virtual machines where you can choose which firmware to use. The last option, instead, shows you how to set up a LUKS-encrypted Debian installation.

Prerequisites §

Before getting started, get yourself a GNU/Linux distribution(even a live one) that includes the debootstrap(8) command in its repository. Debian/RHEL/Arch-based distributions offer this package directly from their official repository, so you can easily install it using the distro's package manager.

Be also sure to back up any important files and to double check the commands you issue on your system. This guide will walk you through setting up a Debian installation "à la Arch"; that is, by doing everything by hand. Therefore, it assumes that you know how to work with UNIX-like systems and how to troubleshoot issues on your own.

Keep in mind that this guide was written and tested against the latest release of Debian stable and while it may also work with other Debian-based systems, they are not officially supported. I will, however, update the guide regularly to ensure compatibility and reliability.

Partitions §

As stated before, we can define three different ways to install Debian; each of them will result in a different partition layout. Let's see them.

UEFI layout:
Name Type Mount Size
/dev/sda1 EFI /boot 1 GiB
/dev/sda2 ext4 / 24 GiB
In this case, we need to reserve at least 100 MiB for the EFI partition, though you may allocate more space if you also plan to store the kernel and the initial ramdisk there. The rest of the disk can be used for the base system using any partitioning scheme of your choice. In this example, I've chosen to store everything in a single root partition.

You can format the partitions using the following commands:

$ mkfs.vfat -F32 /dev/sda1
$ mkfs.ext4 /dev/sda2
BIOS layout:
Name Type Mount Size
/dev/sda1 ext4 / 25 GiB
In this case, things are way more straightforward than before. You can use the entire disk for your base system without having to reserve any additional space for the firmware.

To format the partition, you can issue the following command:

$ mkfs.ext4 /dev/sda1
LUKS layout:

Finally, let's see the partition layout for an UEFI+LUKS partition scheme.
Name Type Mount Size
/dev/sda1 EFI /boot 1 GiB
/dev/sda2 LUKS n/a 24 GiB
As you can see the /dev/sda partition doesn't get mounted directly, instead we will need to unlock it first and then mount the device mapper associated with the LUKS partition[2].

Let's start by formatting the boot partition:

$ mkfs.vfat -F32 /dev/sda1
and then let's proceed by creating the encrypted partition:

$ cryptsetup luksFormat /dev/sda2 # create
# follow the interactive instructions...
$ cryptsetup luksOpen /dev/sda2 luks # open
$ mkfs.ext4 /dev/mapper/luks # format

Mounting §

We can now proceed to mount the previously created partitions. For a BIOS-based system, this will be a matter of issuing the following command:

$ mount /dev/sda1 /mnt
while for UEFI-based installations, you will first need to mount the root partition:

$ mount /dev/sda2 /mnt
# mount /dev/mapper/luks /mnt # for LUKS installations
then create the boot mount point and finally mount the boot partition on it:

$ mkdir -p /mnt/boot
$ mount /dev/sda1 /mnt/boot

System bootstrap §

We are now ready to bootstrap our system using the debootstrap(8) utility, this utility will download, extract and configure the base Debian system into a subdirectory:

$ debootstrap --arch amd64 stable /mnt https://deb.debian.org/debian
This will take some time depending on your internet connection and disk speed. The process is non-interactive though, so you won't be bothered with questions allowing you to let it run on its own.

Chroot §

At this point, we can chroot into the freshly installed system. In order to do that, we will need to mount a few extra things first:

$ mount --make-rslave --rbind /proc /mnt/proc
$ mount --make-rslave --rbind /dev  /mnt/dev
$ mount --make-rslave --rbind /sys  /mnt/sys
$ mount --make-rslave --rbind /run  /mnt/run
Then we can chroot using

$ chroot /mnt /bin/bash

Install essential packages §

Right now, the system lacks any essential program such as a text editor, the kernel and pretty much any kind of firmware. Issue the following command to install the bare minimum to proceed:

$ apt update && apt install vim net-tools
Also, if you are following the LUKS path, this is the right moment to install the required packages to boot from an encrypted partition:

$ apt install cryptsetup cryptsetup-bin cryptsetup-initramfs

fstab configuration §

Let's configure the /etc/fstab file by listing the partitions names and mount points. Depending on the installation scheme you have chosen, there are three different ways to set up the fstab file[3].

UEFI installation:

In this case we need to list both the root and the boot partition on the fstab file:

UUID=260dbe03-e88a-4de2-bab2-fa21eb838b91 / ext4 errors=remount-ro 0 1
UUID=8EB7-322B /boot vfat defaults 0 2
LUKS installation:

This case is quite similar to the previous one, except that we need to replace the physical root UUID with the device mapper associated with the encrypted partition:

/dev/mapper/luks / ext4 errors=remount-ro 0 1
UUID=8EB7-322B /boot vfat defaults 0 2
BIOS installation:

Finally, since the BIOS scheme consists only of one single partition, the resulting fstab file will only have one single entry:

UUID=260dbe03-e88a-4de2-bab2-fa21eb838b91 / ext4 errors=remount-ro 0 1

LUKS-only settings §

LUKS installations require to set up an additional configuration file: /etc/crypttab. This file controls which partition to decrypt during the boot process and is also used by the initial ram disk generator to build the initramfs image.

Add the following line to the file:

luks UUID=355f7b01-c0de-4f1e-8e29-170d39caada4 none luks,discard
be sure to replace the UUID value with the UUID of the crypto_LUKS device from lsblk(8). In other words, from the following output:

NAME     FSTYPE      FSVER LABEL  UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
sda                                                                                   
├─sda1   vfat        FAT32        8EB7-322                              1G        1%  /boot
└─sda2   crypto_LUKS 2            355f7b01-c0de-4f1e-8e29-170d39caada4                
  └─luks ext4        1.0          5dabf729-cf4f-4c14-9256-05dbd411122a  24G       1%  /

you want to copy the second-to-last UUID, not the last one.

Apt sources §

We can now configure apt's sources.list file. We will use the new deb822 debian.sources syntax which is more intuitive and less verbose than the old one[4]: Edit the /etc/apt/sources.list.d/debian.sources and add the following content:

Types: deb deb-src
URIs: https://deb.debian.org/debian
Suites: bookworm bookworm-updates
Components: main non-free-firmware
Enabled: yes
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg

Types: deb deb-src
URIs: https://security.debian.org/debian-security
Suites: bookworm-security
Components: main non-free-firmware
Enabled: yes
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
After that, perform a system update using the following commands:

$ apt update
$ apt upgrade

Timezone & locales §

Let's now proceed to configure the rest of the system. We will start by setting the timezone and the locales:

$ /usr/sbin/dpkg-reconfigure tzdata
$ apt install locales
$ /usr/sbin/dpkg-reconfigure locales
In both cases an interactive, text-based UI will be shown which will guide you to the configuration process.

Network configuration §

No kind of network configuration is available out of the box, we need to configure it manually, even if you want to stick to a simple wired DHCP network. To configure it, add the following content to /etc/network/interfaces:

# Loopback interface
auto lo
iface lo inet loopback

# Primary interface
auto enp1s0
iface enp1s0 inet dhcp
be sure to replace the enp1s0 with your actual NIC. Then, we can proceed to configure the hostname:

$ echo "laptop" > /etc/hostname
I also suggest adding the hostname to the /etc/hosts file in order to resolve it to localhost:

127.0.0.1       localhost laptop
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters

Kernel & firmware §

Now we will install the kernel and the firmware. Since we already set up the /etc/fstab and the /etc/crypttab files, the kernel installer should be able to configure both the kernel and the initramfs correctly:

$ apt install linux-image-amd64 firmware-linux
Before proceeding, we need to include some additional paths(e.g. /usr/sbin) into our environment. The fastest way to do it, is to issue the following command:

$ su - root

User configuration §

Let's configure the root password by issuing the passwd command, then let's create a new user with the following command[5]:

$ useradd -m -G wheel,video,audio,system,disk,sudo,network -s /bin/bash <USER_NAME>
$ passwd <USER_NAME>
We can then install sudo with the following command:

$ apt install sudo
Since our user is already inside the "sudo" group, there's not need to edit the sudoers file.

Bootloader §

We can now complete the base installation process by configuring the bootloader(GRUB). Let's start by installing it:

$ apt install grub2
if you are running an UEFI system, you will also need to install the following package:

$ apt install grub-efi
If, on top of that, you are doing a LUKS installation, you will first need to modify the /etc/default/grub file by adding the following two lines:

[...]
GRUB_CMDLINE_LINUX="cryptdevice=UUID=355f7b01-c0de-4f1e-8e29-170d39caada4:luks"
GRUB_ENABLE_CRYPTODISK=y
[...]
where: After that, we can proceed by installing the bootloader.

On an UEFI-based system, issue the following command:

$ grub-install --target=x86_64-efi --efi-directory=/boot --recheck
while, on a BIOS-based system, run the following one instead:

$ grub-install --target=i386-pc /dev/sda --recheck
Finally, for both system types, execute the following command:

$ update-grub

Reboot & post-installation §

Now that the installation process is completed, we can exit chroot, unmount the partitions and reboot to our freshly installed system. On the next reboot, you will be prompted for the LUKS decryption password if you chose that configuration; otherwise, the boot process should complete without any user interaction.

At this point you are left with a fully working Debian system with no extra packages installed on top of it. If you want to install some common use packages, you can run tasksel to add any of the following package groups:

$ tasksel --list-tasks
desktop                       Debian desktop environment
gnome-desktop                 GNOME
xfce-desktop                  Xfce
gnome-flashback-desktop       GNOME Flashback
kde-desktop                   KDE Plasma
cinnamon-desktop              Cinnamon
mate-desktop                  MATE
lxde-desktop                  LXDE
lxqt-desktop                  LXQt
web-server                    web server
ssh-server                    SSH server
laptop                        laptop
Tip: by default, Debian enables a systemd timer to automatically download and install software updates.

While this behaviour might be useful in certain scenarios, if you are following this guide you may prefer managing system updates by yourself. To stop Debian to automatically install updates, you can disable and mask these systemd timers/services:

$ systemctl disable apt-daily.service
$ systemctl disable apt-daily.timer
$ systemctl mask apt-daily.timer
$ systemctl mask apt-daily.service

$ systemctl disable apt-daily-upgrade.service
$ systemctl disable apt-daily-upgrade.timer
$ systemctl mask apt-daily-upgrade.service
$ systemctl mask apt-daily-upgrade.timer

Conclusions §

At this point, you should have a fully working Debian installation installed completely from scratch. If you have any questions or if you'd like to report something, feel free to contact me via my email. 👋

References §