Debian 13 en portátil: instalación resiliente con LUKS, btrfs y Timeshift

Índice

Cuando reinstalé mi portátil quería algo más que una instalación típica. Quería que si un día el sistema dejaba de arrancar pudiera recuperarlo en minutos. Que si metía la pata con una actualización hubiera vuelta atrás. Que si el portátil caía en manos equivocadas los datos estuvieran protegidos. Esta guía es exactamente eso: una instalación de Debian 13 pensada para la resiliencia real, no para el tutorial de turno.

La base es simple pero potente: LUKS2 para cifrar todo el disco, btrfs con subvolúmenes bien separados para que Timeshift pueda hacer snapshots de verdad, y grub-btrfs para que esos snapshots aparezcan directamente en el menú de arranque. Si el sistema revienta, entras al GRUB, seleccionas el snapshot del día anterior y listo.

Nota: Esta guía está probada en un ThinkPad X270 con NVMe de 2 TB. Los comandos son los mismos para cualquier portátil con UEFI. Solo cambia el nombre del dispositivo — en NVMe será /dev/nvme0n1, en SATA será /dev/sda.


🗂️ El esquema de particiones

Antes de empezar, hay que tener claro qué vamos a crear y por qué:

/dev/nvme0n1
├── nvme0n1p1    512 MB   FAT32    /boot/efi   (EFI — sin cifrar, lo necesita el firmware)
├── nvme0n1p2    1 GB     ext4     /boot        (GRUB — sin cifrar, más fiable así)
└── nvme0n1p3    ~resto   LUKS2  → btrfs
                                   ├── @            →  /
                                   ├── @home        →  /home
                                   ├── @snapshots   →  /.snapshots
                                   ├── @var_log     →  /var/log
                                   ├── @swap        →  /swap
                                   ├── @docker      →  /var/lib/docker
                                   └── @vms         →  /var/lib/libvirt/images

El truco está en separar /boot de la partición cifrada. Muchos tutoriales meten GRUB dentro de LUKS y luego tienen problemas. Con /boot sin cifrar el proceso es más fiable y el arranque más rápido.

Los subvolúmenes de btrfs son la clave de la resiliencia: Timeshift hace snapshots del sistema (@) sin tocar Docker (@docker) ni las imágenes de VMs (@vms), que pueden pesar decenas de GB y no tienen sentido incluir en un snapshot del sistema.


💾 Fase 1 — Preparar el USB y la BIOS

Descargar y grabar el USB

Desde cualquier Linux descargamos la ISO de Debian 13 netinst y la grabamos:

wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.0.0-amd64-netinst.iso

# Verificar que la descarga está íntegra
wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA256SUMS
sha256sum -c SHA256SUMS --ignore-missing

# Grabar en el USB — sustituir /dev/sdX por tu USB
sudo dd if=debian-13.0.0-amd64-netinst.iso of=/dev/sdX bs=4M status=progress conv=fsync

Nota: conv=fsync asegura que el contenido llega al USB antes de salir. Sin ello puedes arrancar con una imagen incompleta y no entender por qué falla.

Configurar la BIOS

En el X270 se entra pulsando F1 al arrancar. En otros portátiles suele ser Del, F2 o Esc — depende del fabricante.

Ajustes imprescindibles:

Opción Valor Por qué
Secure Boot Disabled Debian no lo soporta por defecto
Boot Mode UEFI Only Para que use la partición EFI que vamos a crear
VT-x / Intel Virtualization Enabled Para KVM si luego quieres VMs

Guardar y al reiniciar pulsar F12 (o el equivalente de tu portátil) para arrancar desde el USB.


🛠️ Fase 2 — Instalador Debian

Arrancamos con Graphical Install y seguimos hasta el paso de particionado. Los primeros pasos son los de siempre:

  • Idioma: Español / Ubicación: España / Teclado: Español
  • Hostname: el que quieras / Dominio: el tuyo o vacío
  • Contraseña root: fuerte
  • Usuario: el tuyo

Particionado manual

En el paso de particionado elegir Manual y crear la tabla GPT:

Seleccionar el discoNueva tabla de particionesGPT

Partición 1 — EFI (512 MB):

  • Tipo: Partición del sistema EFI
  • Formato: FAT32
  • Punto de montaje: /boot/efi

Partición 2 — /boot sin cifrar (1 GB):

  • Tipo: Partición primaria
  • Formato: ext4
  • Punto de montaje: /boot

Partición 3 — LUKS con el resto:

  • Tipo: Volumen físico para cifrado
  • Ir a Configurar volúmenes cifrados → seleccionar la partición 3
  • Método: LUKS2
  • Contraseña: la que se pedirá en cada arranque — que sea buena
  • El instalador crea nvme0n1p3_crypt → usarlo como btrfs / Punto de montaje: /

Continuamos con:

  • Sistema base: linux-image-amd64
  • Entorno escritorio: ninguno (lo instalamos después con más control)
  • SSH server:
  • GRUB: instalar en el disco principal (/dev/nvme0n1)

Reiniciamos. El sistema ya arranca pidiendo la contraseña LUKS.


🌿 Fase 3 — Subvolúmenes btrfs

Hacer justo tras el primer arranque, antes de instalar nada más.

El instalador crea un solo volumen btrfs. Lo restructuramos en subvolúmenes para que Timeshift funcione correctamente y los snapshots tengan sentido.

su -

# Montar la raíz del volumen btrfs (sin subvolumen)
mkdir /mnt/btrfs-root
mount -o subvolid=0 /dev/mapper/nvme0n1p3_crypt /mnt/btrfs-root

# Crear los subvolúmenes
cd /mnt/btrfs-root
btrfs subvolume create @
btrfs subvolume create @home
btrfs subvolume create @snapshots
btrfs subvolume create @var_log
btrfs subvolume create @swap
btrfs subvolume create @docker
btrfs subvolume create @vms

# Copiar el contenido existente a los subvolúmenes correctos
rsync -aAX --exclude=/home --exclude=/var/log \
  /mnt/btrfs-root/ /mnt/btrfs-root/@/
rsync -aAX /mnt/btrfs-root/home/    /mnt/btrfs-root/@home/
rsync -aAX /mnt/btrfs-root/var/log/ /mnt/btrfs-root/@var_log/

# Eliminar los directorios originales (ya están en los subvolúmenes)
rm -rf /mnt/btrfs-root/home
rm -rf /mnt/btrfs-root/var/log

Actualizar /etc/fstab

Necesitamos los UUIDs de las tres particiones:

blkid /dev/nvme0n1p1               # UUID de EFI
blkid /dev/nvme0n1p2               # UUID de /boot
blkid /dev/mapper/nvme0n1p3_crypt  # UUID del volumen btrfs (dentro de LUKS)

Editamos el fstab del nuevo sistema:

nano /mnt/btrfs-root/@/etc/fstab
# EFI
UUID=<uuid-efi>    /boot/efi    vfat   umask=0077  0  1

# /boot sin cifrar
UUID=<uuid-boot>   /boot        ext4   defaults    0  2

# Subvolúmenes btrfs
UUID=<uuid-crypt>  /            btrfs  noatime,compress=zstd:1,space_cache=v2,subvol=@           0  0
UUID=<uuid-crypt>  /home        btrfs  noatime,compress=zstd:1,space_cache=v2,subvol=@home       0  0
UUID=<uuid-crypt>  /.snapshots  btrfs  noatime,compress=zstd:1,space_cache=v2,subvol=@snapshots  0  0
UUID=<uuid-crypt>  /var/log     btrfs  noatime,compress=zstd:1,space_cache=v2,subvol=@var_log    0  0
UUID=<uuid-crypt>  /swap        btrfs  noatime,space_cache=v2,subvol=@swap                       0  0
UUID=<uuid-crypt>  /var/lib/docker         btrfs  noatime,space_cache=v2,subvol=@docker          0  0
UUID=<uuid-crypt>  /var/lib/libvirt/images btrfs  nodatacow,space_cache=v2,subvol=@vms           0  0

Nota: nodatacow en @vms es obligatorio si vas a usarlo para VMs. Sin él los discos .qcow2 tienen un rendimiento muy degradado por el Copy-on-Write de btrfs.

Actualizar /etc/crypttab

nano /mnt/btrfs-root/@/etc/crypttab
# <nombre>              <dispositivo>           <keyfile>  <opciones>
nvme0n1p3_crypt         UUID=<uuid-nvme0n1p3>   none       luks,discard

discard activa TRIM a través de LUKS — mejora el rendimiento y la vida del SSD. El tradeoff es que revela qué bloques están en uso, algo irrelevante en un portátil personal.

Regenerar initramfs y GRUB

# Montar todo para el chroot
mount -o subvol=@ /dev/mapper/nvme0n1p3_crypt /mnt
mount -o subvol=@home    /dev/mapper/nvme0n1p3_crypt /mnt/home
mount -o subvol=@var_log /dev/mapper/nvme0n1p3_crypt /mnt/var/log
mount /dev/nvme0n1p2 /mnt/boot
mount /dev/nvme0n1p1 /mnt/boot/efi
mount --bind /dev  /mnt/dev
mount --bind /proc /mnt/proc
mount --bind /sys  /mnt/sys
mount --bind /run  /mnt/run

chroot /mnt
update-initramfs -u -k all
update-grub
exit

umount -R /mnt
reboot

💤 Fase 4 — Swapfile y hibernación

Con btrfs el swapfile tiene sus particularidades. No se puede crear como en ext4 — hay que desactivarle el CoW antes de escribir en él, si no el sistema te avisa de error al intentar activarlo.

su -

# Crear el swapfile sin Copy-on-Write (obligatorio en btrfs)
touch /swap/swapfile
chattr +C /swap/swapfile
dd if=/dev/zero of=/swap/swapfile bs=1M count=65536   # 64 GB
chmod 600 /swap/swapfile
mkswap /swap/swapfile
swapon /swap/swapfile

echo '/swap/swapfile none swap sw 0 0' >> /etc/fstab
swapon --show && free -h

Configurar la hibernación

Para que la hibernación funcione con btrfs el GRUB necesita dos datos: el UUID del volumen y el offset físico del swapfile dentro de btrfs. Este offset es diferente al offset de fichero normal — hay que calcularlo con la herramienta de btrfs.

# Offset físico del swapfile — anotar este número
btrfs inspect-internal map-swapfile -r /swap/swapfile

# UUID del volumen LUKS abierto
blkid /dev/mapper/nvme0n1p3_crypt | grep -o 'UUID="[^"]*"'

nano /etc/default/grub

Añadir o modificar esta línea con tus valores:

GRUB_CMDLINE_LINUX="resume=UUID=<uuid-crypt> resume_offset=<offset>"
update-grub
update-initramfs -u -k all

# Probar que funciona — el equipo se apaga por completo
systemctl hibernate

Al encender, GRUB recupera la sesión donde la dejaste.


⚡ Fase 5 — Post-instalación: KDE, TLP y Timeshift

Paquetes base y KDE

su -
apt update && apt upgrade -y

# Base del sistema
apt install -y git curl wget rsync htop vim build-essential btrfs-progs acpid

# WiFi + Bluetooth (mayoría de portátiles Intel)
apt install -y firmware-iwlwifi bluetooth bluez

# Gestión de energía
apt install -y tlp tlp-rdw thermald powertop
systemctl enable tlp

# KDE Plasma
apt install -y task-kde-desktop kde-standard \
  plasma-workspace-wayland plasma-pa powerdevil bluedevil \
  xserver-xorg-input-libinput

# Audio PipeWire (estándar en Debian 13)
apt install -y pipewire pipewire-pulse wireplumber

# Plymouth — prompt LUKS con logo en el arranque
apt install -y plymouth plymouth-themes
plymouth-set-default-theme -R bgrt

systemctl enable sddm
reboot

TLP — protección de batería

TLP es imprescindible en cualquier portátil que pase tiempo enchufado. Sin umbrales de carga la batería pasa el día al 100% y se degrada mucho antes de lo necesario.

nano /etc/tlp.conf

Descomentar y ajustar estos valores:

# Carga: empieza al bajar del 75%, para al llegar al 80%
START_CHARGE_THRESH_BAT0=75
STOP_CHARGE_THRESH_BAT0=80

# Si el portátil tiene segunda batería (ThinkPad X270, X250, etc.)
START_CHARGE_THRESH_BAT1=75
STOP_CHARGE_THRESH_BAT1=80

# CPU en batería: ahorro de energía
CPU_SCALING_GOVERNOR_ON_BAT=powersave
CPU_ENERGY_PERF_POLICY_ON_BAT=power
tlp start
tlp-stat -b   # verificar que los umbrales están aplicados en hardware

Nota: Los umbrales de batería solo funcionan en hardware compatible. En ThinkPad con kernel reciente funciona sin problema vía thinkpad_acpi. En otros portátiles puede no estar soportado — tlp-stat -b te lo indica claramente.

Timeshift + grub-btrfs — la combinación que lo cambia todo

Timeshift en modo btrfs hace snapshots instantáneos sin copiar datos, aprovechando el CoW del sistema de ficheros. grub-btrfs añade esos snapshots al menú de GRUB automáticamente — sin tocar nada a mano.

apt install -y timeshift inotify-tools

# grub-btrfs desde GitHub — el de los repos de Debian suele estar desactualizado
apt install -y gawk   # necesario — mawk no es compatible con este script
git clone https://github.com/Antynea/grub-btrfs.git /tmp/grub-btrfs
cd /tmp/grub-btrfs && make install

systemctl enable --now grub-btrfsd

Abrir Timeshift desde el menú de KDE y configurar:

  1. Tipo de snapshot: BTRFS (no RSYNC)
  2. Dispositivo: seleccionar /dev/mapper/nvme0n1p3_crypt
  3. Retención de snapshots recomendada:
Tipo Retener Cuándo
Arranque 3 En cada arranque
Diario 5 Una vez al día
Semanal 3 Una vez a la semana
Mensual 2 Una vez al mes
  1. Incluir /home/tuusuario si quieres snapshot de home
  2. Excluir: /var/lib/docker, /var/lib/libvirt/images, /swap
  3. Crear el primer snapshot manual → botón Crear → “instalación base”

Desde ese momento, antes de cada arranque Timeshift crea un snapshot y grub-btrfsd actualiza el menú de GRUB. Si el sistema deja de arrancar, en el propio GRUB tienes la opción de entrar a cualquier snapshot anterior.

Tapa → hibernar

nano /etc/systemd/logind.conf
HandleLidSwitch=hibernate              # en batería → hibernar
HandleLidSwitchExternalPower=suspend   # enchufado → suspender (más rápido)
HandleLidSwitchDocked=ignore           # con monitor externo → no hacer nada
systemctl restart systemd-logind

✅ Qué tienes cuando terminas

  • Disco cifrado con LUKS2 — sin la contraseña los datos son inaccesibles
  • btrfs con subvolúmenes independientes — Docker y VMs no ensucian los snapshots del sistema
  • Snapshots automáticos — en cada arranque, diario, semanal y mensual; en modo btrfs son instantáneos
  • Restauración desde GRUB — si el sistema no arranca, seleccionas el snapshot anterior directamente en el menú de arranque, sin USB de rescate
  • TLP con umbrales de carga — la batería no se pasa el día al 100% degradándose
  • Hibernación funcional — cierras la tapa, al día siguiente abres y sigues donde lo dejaste

En el siguiente artículo: personalización del terminal con zsh, oh-my-zsh, powerlevel10k y tmux.