You just installed Debian 13 on a server that’s supposed to be boring. Then the NIC vanishes, storage goes weird, or the installer
politely informs you that “some firmware is missing” and leaves you holding a machine that can’t reach your mirror to fix itself.
This is the part where people start “just downloading random blobs” and rebooting until hope arrives. Don’t. Firmware issues are
diagnosable, fixable, and—if you do it right—won’t surprise you again on the next kernel upgrade.
What’s actually going on (and why Debian behaves this way)
“Missing firmware” on Debian almost never means your kernel driver is missing. It means the driver loaded, tried to initialize
hardware, and then requested a firmware file from userspace (typically via udev/systemd-udevd) from
/lib/firmware. The driver is present; the microcode blob isn’t.
On modern NICs and HBAs, firmware is not optional decoration. It’s how the device brings up link training, offload engines, queue
management, PHY behavior, and sometimes the entire ability to enumerate or reset cleanly. Many drivers will attach without firmware,
but they’ll expose a “netdev” that never links, or an HBA that shows PCIe presence but fails to bring up SAS/SATA phys.
Debian historically separated “free” software from “non-free” firmware packages. That’s a policy decision with technical
consequences: the installer and the default apt sources may not include firmware packages you need to boot with network or storage.
Debian 12 introduced non-free-firmware as a first-class repository component, which helped, but upgrades, custom images,
and minimal installs can still land you in firmware purgatory.
The correct fix is not “copy a random file into /lib/firmware once and forget it.” The correct fix is:
install the right Debian firmware packages (so you get updates, hashes, and dependency tracking),
ensure they’re available in the initramfs if needed,
and verify the device actually loaded the firmware after reboot.
One operational rule: if the machine needs the firmware to access the network that provides the firmware, you need an out-of-band
path. That can be physical media, a local mirror, a second NIC that works, or IPMI/iKVM plus a mounted ISO. If you’re laughing,
it’s because you’ve already been there.
Joke #1: Firmware is like coffee—everyone pretends they can operate without it until production starts asking questions.
A few facts and history worth knowing
- Firmware loading moved from “weird edge case” to default behavior: many PCIe devices ship with minimal ROMs and rely on OS-loaded blobs for features and fixes.
- Linux uses a standard request mechanism: drivers call
request_firmware()and the kernel asks userspace to provide the file from/lib/firmware. - Debian’s repo split is policy-driven: historically “main” stayed free; firmware often wasn’t, so it lived in “non-free”.
- Debian 12 introduced
non-free-firmware: firmware packages moved into a dedicated component to make installs less painful without mixing everything into “non-free”. - Installer behavior depends on the image: some installer ISOs include firmware; others intentionally don’t. Same installer, different outcome.
- NIC firmware often affects link and offloads: you can see the interface but still have no carrier, unstable link, or odd checksum/TSO behavior.
- HBA firmware and kernel drivers are separate layers: the PCIe device can enumerate, yet the SAS transport never comes up if firmware isn’t loaded or is incompatible.
- Initramfs matters for boot-critical storage: if your root filesystem sits behind an HBA that needs firmware, the blob must be available early (inside initramfs), not only after root mounts.
- Firmware packages are updated for security too: it’s not only “make the device work,” it’s also “avoid shipping old vulnerable microcode/firmware”.
Fast diagnosis playbook
When the box has no network or storage after install, you don’t have time for folklore. Run this sequence and stop when you have a
decisive signal.
First: confirm the kernel sees the hardware at all
- PCIe visible? If
lspcidoesn’t show the device, this is not a firmware package problem. Think BIOS settings, slot power, bifurcation, broken riser, or a dead card. - Driver bound? If the device is visible but has no driver, you’re in “missing driver/module” land, not “missing firmware”.
Second: look for firmware requests and failures
dmesgtells the truth: search for “firmware”, “Direct firmware load failed”, or “failed to load”. That gives you the exact filename the driver wants.- Map filename to package: Debian packages put files under
/lib/firmware. If the file doesn’t exist, install the package that provides it.
Third: decide if this needs initramfs
- Root on that device? If the missing firmware affects the device that provides root (HBA, NVMe behind a controller, etc.), you must ensure the firmware lands in initramfs and regenerate it.
- Non-root NIC? For a NIC, initramfs is usually not required unless you netboot, use iSCSI root, or need network in early boot for remote unlock.
Fourth: fix repo config before you “fix firmware”
- Enable
non-free-firmware: don’t sideload blobs manually unless you are air-gapped and disciplined about it. - Update, install, rebuild initramfs, reboot: do it once, do it cleanly, then verify with logs.
Hands-on tasks: commands, outputs, and decisions (12+)
Task 1: Identify the exact hardware (PCI IDs)
cr0x@server:~$ sudo lspci -nn | egrep -i 'ethernet|network|fibre|sas|raid|scsi'
03:00.0 Ethernet controller [0200]: Intel Corporation Ethernet Controller X710 for 10GbE SFP+ [8086:1572]
04:00.0 Serial Attached SCSI controller [0107]: Broadcom / LSI SAS3008 PCI-Express Fusion-MPT SAS-3 [1000:0087]
What it means: The kernel can see both the NIC and HBA at the PCIe layer. Good.
The bracketed IDs (8086:1572, 1000:0087) are your ground truth.
Decision: If your device isn’t listed here, stop chasing firmware packages. Check BIOS/UEFI, seating, power, and PCIe topology.
Task 2: Check which driver is bound (or not)
cr0x@server:~$ sudo lspci -nnk -s 03:00.0
03:00.0 Ethernet controller [0200]: Intel Corporation Ethernet Controller X710 for 10GbE SFP+ [8086:1572]
Subsystem: Intel Corporation Ethernet Converged Network Adapter X710-2 [8086:0000]
Kernel driver in use: i40e
Kernel modules: i40e
What it means: The NIC is using i40e, so the driver exists and bound.
Decision: If “Kernel driver in use” is missing, you may need a different kernel, a module, or to fix Secure Boot/module signing—not firmware.
Task 3: Find firmware errors in the kernel log
cr0x@server:~$ sudo dmesg -T | egrep -i 'firmware|direct firmware|failed to load|i40e|mpt3sas' | tail -n 30
[Mon Dec 30 09:12:18 2025] i40e 0000:03:00.0: Direct firmware load for i40e/ddp/ppm.pkg failed with error -2
[Mon Dec 30 09:12:18 2025] i40e 0000:03:00.0: The DDP package was not found at i40e/ddp/ppm.pkg
[Mon Dec 30 09:12:19 2025] mpt3sas 0000:04:00.0: Direct firmware load for mpt3sas/mpi3mr.fw failed with error -2
[Mon Dec 30 09:12:19 2025] mpt3sas 0000:04:00.0: firmware: failed to load mpt3sas/mpi3mr.fw (-2)
What it means: Error -2 is “file not found.” The driver is asking for specific filenames.
Decision: You need packages that provide /lib/firmware/i40e/ddp/ppm.pkg and /lib/firmware/mpt3sas/mpi3mr.fw, or at least the correct set for your device generation.
Task 4: Confirm the firmware file is missing (don’t assume)
cr0x@server:~$ ls -l /lib/firmware/i40e/ddp/ppm.pkg /lib/firmware/mpt3sas/mpi3mr.fw
ls: cannot access '/lib/firmware/i40e/ddp/ppm.pkg': No such file or directory
ls: cannot access '/lib/firmware/mpt3sas/mpi3mr.fw': No such file or directory
What it means: Not present. This is not a “driver bug” yet. It’s a missing file.
Decision: Fix apt sources and install the proper firmware packages. Avoid manual downloads unless you have a controlled, audited bundle.
Task 5: Inspect apt components (is non-free-firmware enabled?)
cr0x@server:~$ grep -R --no-filename -E '^[[:space:]]*deb ' /etc/apt/sources.list /etc/apt/sources.list.d/*.list 2>/dev/null
deb http://deb.debian.org/debian trixie main
deb http://deb.debian.org/debian-security trixie-security main
deb http://deb.debian.org/debian trixie-updates main
What it means: Only main is enabled. Firmware packages commonly live in non-free-firmware.
Decision: Add non-free-firmware (and usually contrib) to the same lines.
Task 6: Update sources safely (edit once, don’t freestyle)
cr0x@server:~$ sudo sed -i 's/ main$/ main contrib non-free-firmware/' /etc/apt/sources.list
cr0x@server:~$ tail -n 3 /etc/apt/sources.list
deb http://deb.debian.org/debian trixie main contrib non-free-firmware
deb http://deb.debian.org/debian-security trixie-security main contrib non-free-firmware
deb http://deb.debian.org/debian trixie-updates main contrib non-free-firmware
What it means: You’ve enabled the component that actually contains the firmware you need.
Decision: Now apt should be able to see firmware packages. If you still have no network, you’ll need an offline path—keep reading.
Task 7: Refresh apt metadata and check for firmware packages
cr0x@server:~$ sudo apt update
Hit:1 http://deb.debian.org/debian trixie InRelease
Hit:2 http://deb.debian.org/debian-security trixie-security InRelease
Hit:3 http://deb.debian.org/debian trixie-updates InRelease
Reading package lists... Done
Building dependency tree... Done
All packages are up to date.
What it means: Repos are reachable and apt metadata is refreshed.
Decision: If apt can’t reach the repos because your only NIC needs firmware, jump to the offline install steps in the checklist section.
Task 8: Install the general firmware bundle (often sufficient)
cr0x@server:~$ sudo apt install -y firmware-linux firmware-linux-nonfree firmware-misc-nonfree
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed:
firmware-linux firmware-linux-nonfree firmware-misc-nonfree
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 78.4 MB of archives.
After this operation, 210 MB of additional disk space will be used.
Setting up firmware-linux-nonfree (20250801-1) ...
Setting up firmware-misc-nonfree (20250801-1) ...
Setting up firmware-linux (20250801-1) ...
What it means: These meta-ish packages cover a large portion of common blobs. On server NICs and HBAs, this often fixes things immediately.
Decision: If you have a specific filename still missing in dmesg, you may need a more targeted package too. Verify next.
Task 9: Find which package provides a specific firmware file
cr0x@server:~$ apt-file update
cr0x@server:~$ apt-file search -x '(^|/)i40e/ddp/ppm\.pkg$'
firmware-misc-nonfree: /lib/firmware/i40e/ddp/ppm.pkg
What it means: apt-file maps filenames to packages. This is the clean way to end arguments quickly.
Decision: Install the identified package(s). If you’re in a minimal environment without apt-file, use dpkg -S after installing likely firmware bundles.
Task 10: Rebuild initramfs when storage is involved
cr0x@server:~$ sudo update-initramfs -u -k all
update-initramfs: Generating /boot/initrd.img-6.12.0-1-amd64
W: Possible missing firmware /lib/firmware/mpt3sas/mpi3mr.fw for module mpt3sas
What it means: Initramfs generation warns when a module might need firmware not included.
It’s a clue, not gospel—some warnings are conservative.
Decision: If it’s still warning about the exact file you saw in dmesg, you haven’t installed the right package yet (or the firmware file name differs for your hardware generation).
Task 11: Verify the firmware file now exists
cr0x@server:~$ ls -l /lib/firmware/i40e/ddp/ppm.pkg
-rw-r--r-- 1 root root 1048576 Aug 1 12:01 /lib/firmware/i40e/ddp/ppm.pkg
What it means: The file is present. The driver can load it on next device reset or reboot.
Decision: Reboot if this is a boot-critical device, or unload/reload the module if safe (usually safer to reboot on servers).
Task 12: Confirm the NIC actually comes up and links
cr0x@server:~$ ip -br link show
lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
enp3s0f0 DOWN 3c:fd:fe:aa:bb:cc <BROADCAST,MULTICAST>
What it means: Interface exists. It’s DOWN. That may be administrative (not brought up) or physical (no carrier).
Decision: Bring it up and check carrier; if no carrier, check optics, switchport, and firmware/driver logs.
Task 13: Bring the interface up and check carrier + speed
cr0x@server:~$ sudo ip link set enp3s0f0 up
cr0x@server:~$ ip -br link show enp3s0f0
enp3s0f0 UP 3c:fd:fe:aa:bb:cc <BROADCAST,MULTICAST,UP,LOWER_UP>
cr0x@server:~$ sudo ethtool enp3s0f0 | egrep -i 'link detected|speed|duplex'
Speed: 10000Mb/s
Duplex: Full
Link detected: yes
What it means: Link is up at 10G, which strongly suggests the NIC firmware situation is resolved (at least enough to function).
Decision: If Link detected: no, don’t immediately blame firmware. Check optics/DAC compatibility, switch config, and dmesg for repeated resets.
Task 14: Confirm HBA is enumerating devices
cr0x@server:~$ sudo lsscsi -g
[0:0:0:0] disk ATA INTEL SSDSC2 DL2C /dev/sda /dev/sg0
[1:0:0:0] disk SEAGATE ST16000NM00 SN03 /dev/sdb /dev/sg1
[1:0:1:0] disk SEAGATE ST16000NM00 SN03 /dev/sdc /dev/sg2
What it means: The SCSI midlayer sees disks. Your HBA is not just present; it’s functional.
Decision: If nothing shows up, check dmesg for HBA firmware load failures or link down on phys.
Task 15: Verify module firmware messages after reboot
cr0x@server:~$ sudo journalctl -b -k | egrep -i 'firmware|i40e|mpt3sas' | tail -n 40
Dec 30 09:24:01 server kernel: i40e 0000:03:00.0: firmware version 9.30 0x8000c6a8 1.2762.0
Dec 30 09:24:02 server kernel: i40e 0000:03:00.0: DDP package loaded successfully
Dec 30 09:24:02 server kernel: mpt3sas 0000:04:00.0: firmware version 16.00.12.00
Dec 30 09:24:02 server kernel: scsi host1: mpt3sas
What it means: This is the success state: explicit firmware version lines and no “failed to load” spam.
Decision: Capture these logs in your build/commissioning notes. On the next kernel update, if things break, you’ll know what “good” looked like.
Fix it the right way: repositories, packages, and initramfs
1) Prefer packaged firmware over manual file drops
Copying a blob into /lib/firmware works—until it doesn’t. It bypasses dpkg ownership, makes auditing miserable, and
quietly breaks on reinstall because nobody remembers the artisanal file you scp’d at 2 a.m.
The packaged approach gives you:
versioned updates,
checksums from the archive,
predictable file locations,
and clean removal if you’re building minimal images.
2) Enable non-free-firmware explicitly
On Debian 13, the sane default for real hardware is main contrib non-free-firmware for the base repositories.
You can keep non-free off if you want; firmware is separated for a reason.
If you manage fleets, don’t leave this to chance. Bake it into your provisioning (preseed/autoinstall, golden images,
config management).
3) Install the right firmware packages
Start with the common sets:
firmware-linux,
firmware-linux-nonfree,
firmware-misc-nonfree.
These cover a lot: Intel NIC bits, Realtek bits, various controller firmware, and general microcode-like payloads.
Then go targeted. Use the filename from dmesg and map it to a package via apt-file search.
That keeps you honest and prevents the “install 600MB of firmware because one file is missing” habit.
4) Decide if initramfs needs the firmware
Here’s the dividing line: if the firmware is required before the root filesystem is mounted, it must be in initramfs.
Storage controllers for root disks are the classic case. So are iSCSI-root NICs and early-boot remote-unlock networking.
Debian’s initramfs tooling generally includes firmware referenced by included modules, but corner cases exist:
modules loaded later, driver requests after a reset, or firmware name changes between kernel versions.
If you see warnings during update-initramfs, treat them as a prompt to verify, not as background noise.
5) Reboot deliberately (or reset the device cleanly)
Firmware is loaded when the driver initializes the device. You can sometimes trigger that with a module reload.
On servers, a clean reboot is often the cheapest operation, especially when storage is involved.
“Let’s just rmmod the HBA driver on the live root volume” is the kind of career development that happens unexpectedly.
Quote (paraphrased idea) from Gene Kim: high-performing operations teams reduce failure by making work visible and repeatable, not heroic.
NIC and HBA specifics: what fails and how it looks
NICs: the device is there, the network is not
NIC firmware failures tend to present in three ways:
- Interface missing: rare for “firmware missing,” more common for driver/module issues or PCI problems.
- Interface present, no carrier: firmware blob or PHY configuration issues; also optics/DAC mismatch.
- Interface works but is unstable: repeated resets in dmesg, flapping link, checksum offload oddities, or performance collapse under load.
The most productive habit: when networking is broken, always grab dmesg lines around the driver name. On Intel server NICs,
you’ll often see a request for DDP packages or NVM-related messages. On Broadcom, you’ll see bnx2/bnx2x
firmware names. On Realtek, you’ll see specific rtl_nic/ payloads.
HBAs: boot can work, then storage falls apart (or never appears)
HBAs are where firmware mistakes get expensive because they couple directly to data availability.
You can have a perfectly installed OS and still see zero disks if the HBA’s microcode can’t be loaded or the driver/firmware combo is mismatched.
Failure modes you’ll actually see:
- PCIe enumerates, no SCSI hosts: driver loads but can’t initialize the controller without firmware.
- SCSI host exists, no targets: controller initializes but can’t bring phys up; sometimes cabling/backplane, sometimes firmware.
- Targets appear intermittently: resets, timeouts, command abort storms; could be firmware, could be marginal SAS cables, could be expander/backplane quirks.
On HBAs, treat firmware as part of compatibility, not just a blob you need once. Kernel updates can change driver expectations,
and vendors occasionally ship firmware that “works” until you hit a specific queue depth or error recovery path.
Joke #2: The only thing more permanent than a temporary workaround is a storage workaround someone forgot to document.
Three corporate mini-stories (all anonymized, all plausible)
Incident caused by a wrong assumption: “The interface exists, so firmware must be fine”
A mid-sized company rolled out Debian on a new batch of servers with dual-port 10G NICs. The kickstart-like automation (yes, Debian can be automated too)
verified that ip link showed the expected interface names and then proceeded to configure VLANs, bonds, and routing.
The acceptance check was simple: “interface present” and “MAC address matches inventory.”
First week, everything looked normal—until traffic ramped up. Under sustained load, the NIC would reset, drop the bond, and recover.
The monitoring saw it as occasional packet loss and odd latency spikes. The network team blamed the switch. The server team blamed the
network team, as is tradition. Nobody wanted to look at dmesg because the box “obviously had a NIC.”
The kernel logs told a different story: the driver was operating without a DDP package and repeatedly falling back. It wasn’t a binary
“works/doesn’t work” situation. It was “works until you ask it to do what you bought it for.”
The missing firmware wasn’t preventing enumeration; it was preventing the NIC from using the right datapath and behaving consistently.
The fix was boring: enable non-free-firmware, install the correct firmware package, reboot, and then update the acceptance test.
The new test required “link up,” “no firmware load failures,” and “no resets under a synthetic load test.”
Same hardware. Same kernel. Different outcome because they stopped assuming.
Optimization that backfired: minimal images and “no extra packages” purity
Another org built minimal Debian images for edge deployments. The goal was sensible: reduce attack surface, reduce patch churn, keep images small.
They aggressively removed “unnecessary” packages, including firmware bundles, and relied on the fact that “most of our hardware works out of the box.”
Then procurement swapped a NIC model during a supply crunch. Still the same vendor, still a “supported” chipset, just a different revision.
Suddenly, half the fleet provisioned fine and half showed no network after first boot. The automation couldn’t even report failure
because the reporting path was the network.
Engineers responded with the classic emergency hack: add a one-off step that scp’d a firmware file onto the host during provisioning.
It “fixed” the initial boot. It also created a new problem: on the next kernel update, the firmware file name changed subtly, the initramfs
didn’t include it, and rebooting became a roulette wheel. The images were minimal, but the operational mess was maximal.
The eventual repair was to treat firmware as platform dependency, not bloat. They still kept the image tight, but they whitelisted
specific firmware packages required by their approved hardware matrix, and they validated initramfs contents as part of CI.
Minimalism survived. Unreproducible hacks did not.
Boring but correct practice that saved the day: staging reboots and capturing “good boot” logs
A financial services shop had a conservative change process. Not glamorous. But it worked.
They maintained a staging environment with the same NIC/HBA mix as production and required that every kernel/firmware update
pass a reboot test plus a storage scrub and a sustained network throughput test.
One month, an update introduced a new firmware requirement for an HBA feature path. On systems where the firmware package was present,
everything was fine. On systems that had been hand-built years earlier (and had drifted), the package was missing.
Staging caught it because the update triggered an initramfs warning and a post-reboot check saw degraded storage paths.
The win wasn’t clever debugging; it was documentation. They had a “golden boot log” snippet for each platform that included
the expected firmware version lines and the absence of firmware failures. The on-call could compare staging/production in minutes.
They fixed drift by enforcing repo components and firmware packages via configuration management, then rebuilt initramfs across the fleet.
Production never saw an outage. The only casualty was someone’s belief that careful process can’t coexist with engineering pride.
Common mistakes: symptom → root cause → fix
1) “Installer says firmware missing, but system boots fine. I’ll ignore it.”
Symptom: Boot succeeds, but NIC performance is erratic or storage throws timeouts later.
Root cause: Device runs in a degraded mode without firmware, or only fails under certain features/load.
Fix: Read dmesg for exact firmware filenames, enable non-free-firmware, install the correct packages, reboot, and verify firmware versions in logs.
2) “I added non-free, but apt still can’t find firmware packages.”
Symptom: apt install firmware-… says “Unable to locate package”.
Root cause: You enabled the wrong component (non-free instead of non-free-firmware), or your sources are pointing at the wrong suite.
Fix: Ensure each deb line includes non-free-firmware and matches your release codename; run apt update, then retry.
3) “The firmware file exists, but dmesg still says it can’t load it.”
Symptom: /lib/firmware/… contains the file, yet log shows load failures.
Root cause: Wrong filename/path for that driver version, initramfs missing it for early boot, or permissions/SELinux-like constraints (rare on Debian default).
Fix: Confirm the exact requested path in dmesg; verify the file exists at that exact path. If boot-critical, rebuild initramfs and ensure it contains the file.
4) “Network is dead so I can’t apt install the firmware that fixes the network.”
Symptom: No interface, no link, or no DHCP; apt can’t reach mirrors.
Root cause: Single NIC needs firmware and you didn’t provide it during install.
Fix: Use offline installation: mount the Debian ISO, add it as an apt source, install firmware packages from it (if present), or use a controlled USB/ISO with required .deb packages.
5) “I rebuilt initramfs and now the system won’t boot.”
Symptom: Boot drops to initramfs shell or can’t find root.
Root cause: Boot-critical driver/firmware not included, or you changed initramfs hooks/modules incorrectly.
Fix: Boot previous kernel/initramfs from GRUB if available. Install missing firmware packages, run update-initramfs -u -k all, and keep at least one known-good boot entry.
6) “I blacklisted the driver because it was spamming firmware errors.”
Symptom: Log is quieter, but now you have no NIC/storage.
Root cause: Treating the symptom instead of installing firmware.
Fix: Remove the blacklist, install the correct firmware packages, and validate stable operation. Logging is not your enemy; it’s your witness.
Checklists / step-by-step plan
Checklist A: Online fix (you still have working network somehow)
-
Capture the evidence:
cr0x@server:~$ sudo dmesg -T | egrep -i 'firmware|direct firmware|failed to load' | tail -n 80 [Mon Dec 30 09:12:18 2025] i40e 0000:03:00.0: Direct firmware load for i40e/ddp/ppm.pkg failed with error -2Decision: Note the exact filename(s). Don’t guess.
-
Enable the right repository component:
cr0x@server:~$ sudo editor /etc/apt/sources.listDecision: Ensure
main contrib non-free-firmwareexists on the relevant lines. -
Update and install firmware packages:
cr0x@server:~$ sudo apt update cr0x@server:~$ sudo apt install -y firmware-linux firmware-linux-nonfree firmware-misc-nonfreeDecision: If a specific file is still missing, map it with
apt-fileand install the exact provider. -
Rebuild initramfs if storage/early boot depends on it:
cr0x@server:~$ sudo update-initramfs -u -k allDecision: Warnings about your device’s firmware mean you’re not done.
-
Reboot and verify firmware loaded:
cr0x@server:~$ sudo rebootcr0x@server:~$ sudo journalctl -b -k | egrep -i 'firmware|failed to load' | tail -n 60 Dec 30 09:24:02 server kernel: i40e 0000:03:00.0: DDP package loaded successfullyDecision: No “failed to load” lines and explicit firmware version messages is your success criteria.
Checklist B: Offline fix (no working network)
This is where many guides get hand-wavy. You need a repeatable approach that doesn’t rely on the broken interface.
Pick one:
- Mount an installer ISO and use it as an apt source (works if the ISO contains what you need).
- Use a controlled USB with .deb firmware packages from your internal mirror.
- Temporarily use a known-working USB NIC to bootstrap the real NIC (yes, it’s unglamorous; it’s also effective).
Option 1: Use a mounted ISO as a repository
cr0x@server:~$ sudo mkdir -p /mnt/iso
cr0x@server:~$ sudo mount -o ro /dev/sr0 /mnt/iso
cr0x@server:~$ ls /mnt/iso
README.html dists doc install.amd pool
What it means: The ISO is mounted and contains Debian repository structure.
Decision: Add it to apt and try installing firmware from it.
cr0x@server:~$ echo "deb [trusted=yes] file:/mnt/iso trixie main contrib non-free-firmware" | sudo tee /etc/apt/sources.list.d/iso.list
deb [trusted=yes] file:/mnt/iso trixie main contrib non-free-firmware
cr0x@server:~$ sudo apt update
Get:1 file:/mnt/iso trixie InRelease
Reading package lists... Done
What it means: apt can read package metadata from local media.
Decision: Install firmware packages. If apt can’t find them, this ISO image likely doesn’t include the needed firmware packages.
cr0x@server:~$ sudo apt install -y firmware-linux firmware-linux-nonfree firmware-misc-nonfree
Reading package lists... Done
Building dependency tree... Done
Selecting previously unselected package firmware-linux-nonfree.
Setting up firmware-linux-nonfree (20250801-1) ...
Option 2: Install firmware .deb packages from USB (controlled bundle)
cr0x@server:~$ sudo mount /dev/sdb1 /mnt
cr0x@server:~$ ls /mnt
firmware-linux-nonfree_20250801-1_all.deb
firmware-misc-nonfree_20250801-1_all.deb
firmware-linux_20250801-1_all.deb
What it means: You have the packages locally.
Decision: Install via dpkg, then fix deps via apt if possible (from ISO or later network).
cr0x@server:~$ sudo dpkg -i /mnt/firmware-linux-nonfree_20250801-1_all.deb /mnt/firmware-misc-nonfree_20250801-1_all.deb /mnt/firmware-linux_20250801-1_all.deb
Selecting previously unselected package firmware-linux-nonfree.
Unpacking firmware-linux-nonfree (20250801-1) ...
Setting up firmware-linux-nonfree (20250801-1) ...
Decision: After installing, rebuild initramfs if needed and reboot.
Checklist C: Verify and lock it in (so it doesn’t regress)
-
Confirm no firmware failures on boot:
cr0x@server:~$ sudo journalctl -b -k | egrep -i 'Direct firmware load|failed to load' || trueDecision: Empty output is good. Any lines here require action.
-
Check package ownership (no mystery blobs):
cr0x@server:~$ dpkg -S /lib/firmware/i40e/ddp/ppm.pkg firmware-misc-nonfree: /lib/firmware/i40e/ddp/ppm.pkgDecision: If dpkg doesn’t own a firmware file, you have an unmanaged artifact. Decide whether to replace it with packaged firmware.
-
Make sure initramfs contains the needed firmware (boot-critical paths):
cr0x@server:~$ lsinitramfs /boot/initrd.img-$(uname -r) | grep -E 'mpt3sas/|i40e/' usr/lib/firmware/i40e/ddp/ppm.pkg usr/lib/firmware/mpt3sas/mpi3mr.fwDecision: If the firmware is required before root mount and it’s missing here, rebuild initramfs and investigate hooks/modules.
FAQ
1) Why does Debian install without the firmware I obviously need?
Because Debian’s default stance is conservative about what goes into “main,” and firmware licensing often doesn’t qualify.
Depending on the installer image and your choices, you can end up without firmware packages even if the kernel driver is present.
2) Is enabling non-free-firmware enough, or do I need non-free too?
For firmware specifically, non-free-firmware is the key component. You may still want contrib for some packaging relationships,
but you don’t need non-free unless you want other non-free software beyond firmware.
3) The installer warned about missing firmware, but my NIC works now. Should I still care?
Yes. The warning usually means “a driver asked for something and didn’t get it.” Sometimes the device works in a fallback mode; sometimes it fails later.
Check journalctl -b -k for firmware load failures and resolve them intentionally.
4) Can I just download the firmware file and place it in /lib/firmware?
You can, but you’re taking on lifecycle management: updates, provenance, auditing, and ensuring initramfs includes it when needed.
In production, prefer Debian firmware packages unless you are air-gapped and manage a vetted internal bundle.
5) Why do I need to rebuild initramfs? I already installed the package.
If the device needs firmware before the root filesystem is mounted (common for storage controllers that host root), the blob must be available in initramfs.
Installing a package only guarantees it’s on disk, not in the initramfs image already generated.
6) How do I know which package contains my missing firmware file?
Use apt-file search against the filename reported in dmesg. That’s the fastest accurate mapping from “missing file” to “install this package.”
7) After installing firmware, do I need to reboot?
Often yes. Drivers typically load firmware during device initialization. You can sometimes reload the module for a NIC,
but on HBAs and storage paths, rebooting is safer and usually faster than debugging a half-reset controller.
8) What if I’m on a remote server with no out-of-band and the only NIC needs firmware?
You’re in a bad architecture corner. Practically: use a temporary USB NIC (if you can physically access it),
or boot from rescue media with firmware included, or arrange out-of-band access for the future.
Operationally: don’t deploy servers without a recoverable path for network bootstrap.
9) Is this a kernel bug or a Debian bug?
Usually neither. It’s a packaging/repository availability issue plus modern hardware design. You confirm by checking:
driver is present and bound, firmware file requested, firmware file absent. That’s not a bug; it’s a missing dependency.
Conclusion: next steps that reduce future pain
Fixing “missing firmware” on Debian 13 isn’t hard. Doing it in a way that won’t bite you later is the real job.
The recipe is consistent: identify the device and the requested firmware filename, enable non-free-firmware, install the
proper package, rebuild initramfs if early boot needs it, reboot, and verify in logs.
Next steps I’d actually do on a fleet:
- Standardize apt components (include
non-free-firmware) via configuration management. - Add a commissioning check that fails if
journalctl -b -kcontains firmware load failures. - Record “good boot” firmware versions per platform model so regressions are easy to spot.
- Ensure an offline bootstrap path exists (ISO repo, internal mirror, or out-of-band) before you need it.
You don’t need heroics. You need repeatable mechanics and the discipline to believe your logs.