Managing Linux laptop firmware with fwupd

fwupd gives Linux admins a standard way to find and install firmware updates, but a useful fleet process still needs model coverage, reboot handling, recovery, and proof that each update finished.

Managing Linux laptop firmware with fwupd

I've seen plenty of Linux laptops with current OS packages and old firmware. The package report shows green, but the BIOS, SSD, dock, or USB receiver sits on its own update path.

fwupd is the tool that handles most of this on Linux. It detects supported hardware, reads firmware versions, matches devices against vendor metadata from the Linux Vendor Firmware Service (LVFS), installs updates, and records the outcome.

The project shipped fwupd 2.1.6 on July 1 with more parser hardening, a Coreboot verified-boot check, offline DBX hashes, reset handling, refresh fixes, and support for another Lenovo accessory path. A 2.0.21 release also landed for conservative distributions. It backports fixes for more than 250 potential security issues flagged by AI security scanners plus a correction to reboot-needed reporting.

Updating the fwupd package is necessary. It does not tell you which hardware has a supported update available, whether a reboot or separate activation completed the installation, or whether the update succeeded.

What fwupd gives you

The local service is fwupd. The fwupdmgr command is its administrative client. On most systems, fwupd uses LVFS for signed metadata and firmware supplied by hardware vendors.

According to the LVFS architecture documentation, fwupd identifies hardware by device IDs and GUIDs, then matches those values against vendor metadata. Vendor participation matters. So do the hardware revision, update protocol, and the version of fwupd on the laptop.

The fwupdmgr command reference lists the operations I use for fleet work:

  • get-devices shows what fwupd detected and the firmware version it can read.
  • get-remotes shows which metadata sources are configured and enabled.
  • refresh downloads current metadata from those configured remotes.
  • get-updates shows newer releases currently offered for detected devices.
  • activate handles devices that separate installation from activation.
  • get-history shows prior update attempts stored by fwupd.
  • get-results shows the last update result for a device.
  • check-reboot-needed checks whether an update is waiting for a reboot.

Add --json when a management script needs to parse the output. The project warns that the normal terminal format can change between fwupd versions.

Do not turn “no update” into “current”

fwupdmgr get-updates can return no available action. That result can mean the device is current on an enabled remote. It can also mean the device was not detected, the remote is disabled or stale, the exact hardware revision has no matching metadata, the vendor distributes a newer release somewhere else, or the installed fwupd version cannot use the release.

I keep these states separate:

  • fwupd missing
  • remote unavailable or disabled
  • device not detected
  • device detected, no LVFS update offered
  • update available
  • update staged, activation required
  • update staged, reboot required
  • update succeeded
  • update failed
  • manual or vendor-only update path

Keep no update offered in the report instead of quietly marking the device current. Current needs a known update source and a version comparison.

Here is a small first-pass check. It deliberately reports a cautious state instead of claiming the laptop is current:

#!/bin/sh

if ! command -v fwupdmgr >/dev/null 2>&1; then
    printf '%s\n' 'fwupd_missing'
    exit 0
fi

if ! command -v jq >/dev/null 2>&1; then
    printf '%s\n' 'jq_missing'
    exit 0
fi

devices_json=$(fwupdmgr get-devices --json 2>/dev/null) || {
    printf '%s\n' 'device_check_failed'
    exit 0
}

if ! printf '%s' "$devices_json" | jq -e '.Devices | length > 0' >/dev/null; then
    printf '%s\n' 'device_not_detected'
    exit 0
fi

remotes_json=$(fwupdmgr get-remotes --json 2>/dev/null) || {
    printf '%s\n' 'remote_check_failed'
    exit 0
}

if ! printf '%s' "$remotes_json" | jq -e '.Remotes | any(.Enabled == true)' >/dev/null; then
    printf '%s\n' 'remote_unavailable_or_disabled'
    exit 0
fi

# Avoid prompting to enable LVFS on systems that use managed or offline remotes.
fwupdmgr --no-remote-check refresh >/dev/null 2>&1
refresh_rc=$?

case "$refresh_rc" in
    0|2) ;;
    *)
        printf '%s\n' 'metadata_refresh_failed'
        exit 0
        ;;
esac

# Avoid the interactive offer to upload unreported update history.
fwupdmgr --no-remote-check --no-unreported-check get-updates >/dev/null 2>&1
rc=$?

case "$rc" in
    0) printf '%s\n' 'update_available' ;;
    2) printf '%s\n' 'no_update_offered' ;;
    *) printf 'check_failed_%s\n' "$rc" ;;
esac

This check refreshes metadata first and uses the JSON from get-devices and get-remotes to avoid turning an empty result into a success. The documented exit codes then separate an offered update from a command that completed with no available action. It covers pre-installation state only. Staged, activation, reboot, and final-result states need a separate collection after the update. I still retain the full device, remote, and history JSON when investigating an exception.

Test each hardware model

Build the pilot from the physical models in use. I take one or two laptops from every model and important hardware revision, then include the docks and attached devices people actually rely on.

For each model:

  • Record the distribution and fwupd version so you know which updater branch produced the result.
  • Save detected device names, GUIDs, and current firmware versions so the test can be repeated against the same hardware.
  • Check enabled remotes and metadata age before treating an empty update list as meaningful.
  • Record the offered release, update protocol, and installation requirements before approving it for a model.
  • Note whether AC power, a minimum battery level, reboot, shutdown, cable disconnect, device replug, or separate activation is required.
  • Write down the recovery path before installing anything that can affect boot or attached hardware.

UEFI capsule updates are often staged to the EFI System Partition and applied during reboot before the bootloader starts. Many USB, Thunderbolt, and other peripheral updates can run without a reboot. The LVFS architecture guide documents that difference, and it changes how I schedule and verify the work.

Power and boot state matter too. The LVFS telemetry documentation uses missing AC power and a missing /boot/efi partition as examples of real update failures. An update job should check those conditions before it starts, not discover them after a user has been told the laptop is done.

Keep the result after the reboot

Installing firmware is one step. After the laptop returns, collect fresh get-devices output, the new firmware version, get-results, get-history, and check-reboot-needed. The new device output also shows whether a component still needs activation.

Retain at least:

  • Save the device model and firmware component so failures can be grouped by exact hardware.
  • Keep the previous and new versions so the result can be checked against vendor release notes.
  • Record the fwupd version and update source that produced the result.
  • Keep the install start time, final result, and any reboot or activation requirement.
  • Preserve the failure text with the next action rather than replacing it with a generic red status.
  • Assign a person or team to any exception that stays open.

fwupd can optionally send update history to LVFS. That helps vendors see broad success and failure patterns, but it is not your organization's compliance record. The LVFS firmware-testing documentation notes that ordinary reports can be modified before submission and should not be treated as trusted evidence. Keep your own result in the system where you manage the fleet.

Decide how much to automate

For a small fleet with a few well-tested models, a scheduled metadata refresh and a controlled update window may be enough. Larger or more varied fleets may need an approved-firmware list, a private mirror, or a manual path for hardware that is not covered by LVFS. The LVFS offline-firmware guide documents those options.

I automate discovery before installation. Once the model coverage and recovery path are known, move a small pilot group through the complete update and reboot. Expand by model, not by a random percentage of the entire Linux fleet.

fwupd does not replace endpoint management. Your management system still has to schedule the check, decide which devices are eligible, retain the result, and route failures. FileWave's current Custom Fields documentation describes client-script fields for macOS and Windows, so I would not pretend this Linux script is a shipping FileWave workflow today. The status categories above are still the shape I would want in any mixed-fleet report: offered, staged, reboot pending, succeeded, failed, unsupported, or manual.

Start by finding which devices actually have a supported firmware path, then prove the update finished on each model you plan to automate.

Sources