Installing Debian on a NAS in fully automated mode

Installing Debian on a NAS in fully automated mode

·5 min read·Updated on January 6, 2026

Why automate the installation

The day the system drive fails, a manual reinstall costs a full day between downloading, clicking through the installer, network configuration and forgotten options.

A fully automated install changes that: boot, wait a few minutes, the NAS is operational. Zero clicks, zero omissions, total reproducibility.

The reference hardware here, a TerraMaster F4-424 with TOS replaced by Debian 13 Trixie:

  • CPU: Intel N95 (Alder Lake-N, 4 cores, up to 3.4 GHz)
  • RAM: 8 GB DDR5
  • Network: 2x 2.5GbE Realtek RTL8125
  • Storage: ASMedia ASM1166 controller with 6 SATA ports
  • Boot: internal 4 GB USB stick

Debian on UEFI on the internal USB stick, the 4 SATA bays dedicated to data: system and storage stay cleanly separated.

Building the custom ISO

The principle: a preseed file answering every Debian installer question, injected into an ISO rebuilt with xorriso. Network parameters are externalised in a .env so they stay editable:

# .env - ISO configuration
PRESEED_IP=192.168.1.50
PRESEED_GATEWAY=192.168.1.1
PRESEED_DNS=192.168.1.1
PRESEED_HOSTNAME=nas
PRESEED_DOMAIN=home.lan
#!/bin/bash
set -euo pipefail
source .env

# Extract the Debian 13 netinst ISO
mkdir -p ./iso-extract
xorriso -osirrox on -indev debian-13-amd64-netinst.iso -extract / ./iso-extract

# Inject the preseed
cp preseed.cfg ./iso-extract/

# GRUB configuration for automatic boot
cat > ./iso-extract/boot/grub/grub.cfg << 'GRUB'
set timeout=3
menuentry "Debian Auto Install" {
    linux /install.amd/vmlinuz auto=true priority=critical \
        preseed/file=/cdrom/preseed.cfg
    initrd /install.amd/initrd.gz
}
GRUB

# Rebuild the bootable UEFI ISO
xorriso -as mkisofs \
    -o debian-nas-auto.iso \
    -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \
    -c isolinux/boot.cat \
    -b isolinux/isolinux.bin -no-emul-boot \
    -boot-load-size 4 -boot-info-table \
    -eltorito-alt-boot \
    -e boot/grub/efi.img -no-emul-boot \
    -isohybrid-gpt-basdat \
    ./iso-extract

The seventeen xorriso -as mkisofs flags aren't intuitive: they reconstruct a hybrid UEFI+BIOS ISO, and any omission produces an image that won't boot.

The preseed file

The preseed tells the installer which IP to configure, which hostname to use, which user to create. Zero interaction:

# preseed.cfg - main sections excerpt

# Locale and keyboard
d-i debian-installer/locale string fr_FR.UTF-8
d-i keyboard-configuration/xkb-keymap select fr(latin9)
d-i time/zone string Europe/Paris

# Static networking
d-i netcfg/choose_interface select auto
d-i netcfg/disable_autoconfig boolean true
d-i netcfg/get_ipaddress string 192.168.1.50
d-i netcfg/get_netmask string 255.255.255.0
d-i netcfg/get_gateway string 192.168.1.1
d-i netcfg/get_nameservers string 192.168.1.1
d-i netcfg/get_hostname string nas
d-i netcfg/get_domain string home.lan
d-i netcfg/confirm_static boolean true

# Partitioning - targets the internal USB drive (2-8 GB)
d-i partman-auto/disk string /dev/sda
d-i partman-auto/method string regular
d-i partman-auto/choose_recipe select atomic
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

# User accounts
d-i passwd/root-login boolean true
d-i passwd/user-fullname string NAS Admin
d-i passwd/username string nasadmin

# Minimal packages
tasksel tasksel/first multiselect standard
d-i pkgsel/include string sudo openssh-server python3 python3-apt \
    ca-certificates bash-completion
d-i pkgsel/upgrade select full-upgrade

An important detail: partitioning targets /dev/sda with a 2-8 GB size range, guaranteeing a SATA data disk can never be wiped by accident.

Post-install bootstrap

Once Debian is installed, the preseed runs a late_command that sets up the essentials: an authorised SSH key for nasadmin (no more password), a shadow-file fix, SSH switched to key-only, and a systemd service that will trigger Ansible on first boot.

# late_command in the preseed
d-i preseed/late_command string \
    in-target mkdir -p /home/nasadmin/.ssh; \
    in-target sh -c 'echo "ssh-ed25519 AAAA... admin@workstation" \
        > /home/nasadmin/.ssh/authorized_keys'; \
    in-target chown -R nasadmin:nasadmin /home/nasadmin/.ssh; \
    in-target chmod 700 /home/nasadmin/.ssh; \
    in-target chmod 600 /home/nasadmin/.ssh/authorized_keys; \
    in-target cp /target/tmp/provision.service \
        /target/etc/systemd/system/provision.service; \
    in-target systemctl enable provision.service

provision.service wakes up on first boot, waits for the network, then runs Ansible.

Testing before deploying

Before flashing a USB stick, a VM test avoids round trips:

# 4 GB virtual disk
qemu-img create -f qcow2 test-nas.qcow2 4G

# Launch the VM
qemu-system-x86_64 \
    -m 2048 \
    -enable-kvm \
    -bios /usr/share/ovmf/OVMF.fd \
    -cdrom debian-nas-auto.iso \
    -drive file=test-nas.qcow2,format=qcow2 \
    -nic user,hostfwd=tcp::2222-:22

Then verify over SSH from the host:

ssh -p 2222 nasadmin@localhost

If the connection works, the ISO can be flashed onto a real USB stick with dd.

Verdict

Automation is reproducibility: if the system drive fails, fifteen minutes are enough to get an operational NAS back, and the procedure can be re-run as often as needed without risk of error. Ansible then takes over for storage and services.

ShareLinkedInXBluesky

Related articles