4 of 4

Startup and Boot FailuresLesson 4 of 4

Emergency mode, bad mounts and systemd start-up hangs

Recover from emergency mode, repair fstab, and find dependency cycles, masked units and units that hang the boot.

  • systemctl default
  • systemctl rescue
  • systemctl --failed
  • systemctl list-dependencies
  • systemctl list-jobs
  • systemd-analyze verify
  • systemd-analyze critical-chain
  • findmnt --verify
  • mount -o remount,rw /
  • systemctl mask
  • systemctl cat

50 min3 exercises

After this lesson you can

  • Use emergency mode correctly: remount the root read-write, find the failed unit and fix it.
  • Repair a bad /etc/fstab line and choose nofail and timeouts for optional mounts.
  • Find an ordering cycle, a masked dependency and a hanging unit with systemd tools.

The idea

When systemd cannot reach its default target, it does not give up silently. It builds a transaction (the set of units to start and the order), runs them, and for any unit marked as required by the target, a failure either stops the target from being reached or drops the machine into emergency mode. Most start-up hangs and emergency-mode entries trace to a small number of causes.

CauseTypical signWhere to look
A bad /etc/fstab line“You are in emergency mode”, Dependency failed for ... mountjournalctl -xb, findmnt --verify
A required device that never appears90 second wait, Timed out waiting for devicesystemd-analyze blame / critical-chain
An ordering cycleTransaction order is cyclic; units droppedsystemd-analyze verify
A masked or missing dependencyUnit xyz.service is masked or not foundsystemctl list-unit-files, systemctl status
A unit with the wrong Type= or too long a timeoutStart job runs for minutes then failssystemctl list-jobs during boot, journalctl -u
A failing Requires= unitThe dependent unit “failed because of unavailable resources or another unit”systemctl status of the requiring unit

Emergency mode, step by step

In emergency mode:

  1. Log in with the root password (or use Ctrl+D to try to continue).
  2. The root file system is read-only. Before you edit anything, mount -o remount,rw /.
  3. Find the failure: journalctl -xb (end with G, search with /), systemctl --failed, findmnt --verify.
  4. Fix the cause (usually /etc/fstab).
  5. systemctl daemon-reload (systemd generates mount units from /etc/fstab and caches them), then systemctl default to continue to the default target, or reboot for a clean test.

Do not just exit: if the cause is still there you return to the same prompt.

/etc/fstab that does not stop the boot

An /etc/fstab line has six fields; the options field can carry systemd extensions:

OptionEffect
nofailA failed mount does not make boot fail, and the system does not wait for it
x-systemd.device-timeout=5sWait at most 5 seconds for the device (default 90 s)
x-systemd.automountMount on first access; the boot never waits for it
_netdevA network file system: mount after the network is up
noautoDo not mount at boot at all

Use nofail for removable or optional storage and _netdev for NFS and iSCSI. Never use nofail for a file system the system cannot run without (/, /usr, /var on many systems).

Test every edit:

sudo systemctl daemon-reload
findmnt --verify
sudo mount -a          # mounts anything in fstab not yet mounted; errors appear now, not at boot

Units that depend on each other

Two directives do different jobs, and confusing them causes most unit problems:

  • Ordering: After=x / Before=x says when, not whether.
  • Requirement: Requires=x (hard) / Wants=x (soft) says whether x must be started too.

Requires= without After= starts both together; After= without Requires= only orders them if both happen to be started. If A is After=B and B is After=A, systemd cannot order them: a cycle.

Try it

The cycle, as systemd reports it

Real output from systemd-analyze verify on two units that each wait for the other:

systemd-analyze verify ./a.service ./b.service
a.service: Found ordering cycle on b.service/start
a.service: Found dependency on a.service/start
a.service: Unable to break cycle starting with a.service/start
Requested transaction contains an unfixable cyclic ordering dependency: Transaction order is cyclic. See system logs for details.
b.service: Command /usr/bin/nothere is not executable: No such file or directory

One command found two separate problems: the cycle and a missing executable. Make systemd-analyze verify UNITFILE a reflex whenever you write or edit a unit, because it reads the unit and its dependencies without starting anything.

What systemd is doing right now

If the boot appears stuck, switch to another console (Ctrl+Alt+F2), log in, and run:

systemctl list-jobs
JOB UNIT                          TYPE  STATE
 94 dev-disk-by\x2dlabel-BACKUP.device start running
 93 backup.mount                  start waiting
 62 graphical.target              start waiting

A job in state running for a long time is what everything else is waiting on. systemctl status of it, and the journal, say why.

Masked and missing dependencies

systemctl list-unit-files | grep masked
systemctl status network-online.target
network-online.target                 masked
Loaded: masked (Reason: Unit network-online.target is masked.)

A masked unit is linked to /dev/null and cannot start, even as a dependency. systemctl unmask UNIT removes the link. A different failure, Unit foo.service not found, means the unit file does not exist (typo in Requires= or a package was removed).

Reading the dependency tree

systemctl list-dependencies app-a.service
systemctl cat app-a.service

list-dependencies shows what a unit pulls in (--reverse shows what pulls it in), and systemctl cat shows the file plus any drop-ins, which is where a surprising After= or an override often hides.

When the boot still hangs

SymptomCheckFix
Stops after “Reached target Local File Systems”list-jobs, journal for network mounts_netdev, nofail, or a dependency on network-online.target
90 s then emergency modeTimed out waiting for deviceWrong UUID/label; fix or add nofail
A start job is running for ...Name in the messagestatus and journalctl -u of that unit; fix Type= or the timeout
Unit starts then dies in 5 s, repeatingRestart= loop with StartLimitsystemctl status shows start-limit-hit; fix the cause, then systemctl reset-failed
Network-dependent units fail only at bootThey start before the network is upAfter=network-online.target and Wants=network-online.target
Works after systemctl start, fails at bootOrdering/dependency missing, or the unit is not enabledAdd After=; systemctl enable

Going further

The safety net. systemd.unit=rescue.target on the kernel line gets a repair shell without editing the file system first; systemd.mask=unit skips one unit for one boot, which is a quick way to get past a unit that hangs the machine so you can fix it from the inside. Beyond the exam: systemd-analyze plot > boot.svg draws the whole start-up as a timeline, useful for explaining a slow boot to someone else.

Practice

0 of 3 done

Do these on your own system. They get harder. Check each result, then tick it off.

  1. Warm-up

    Out of emergency mode

    Setup (lab VM): append a line for a UUID that does not exist (UUID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee /backup xfs defaults 0 0) to /etc/fstab and reboot. The machine stops in emergency mode. Log in, work out which entry failed, repair the file, and continue the boot.

    Check your result
    • In the emergency shell: journalctl -xb | grep -i backup names the failed mount.
    • findmnt --verify shows no [E] after the fix.
    • The machine reaches the normal target (systemctl is-system-running prints running).
    Hint

    Emergency mode mounts / read-only. Run mount -o remount,rw / before you edit the file. Then systemctl daemon-reload and systemctl default (or reboot).

    Solution
    journalctl -xb | grep -i backup | head
    mount -o remount,rw /
    sed -i '/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/d' /etc/fstab
    systemctl daemon-reload
    findmnt --verify
    systemctl default
  2. Practice

    A mount that must not stop boot

    A removable backup disk (/dev/disk/by-label/BACKUP, XFS) is mounted on /backup. The server must boot normally whether or not the disk is plugged in, and must not wait more than 5 seconds for it. Write the /etc/fstab line, test it without rebooting (unmounted and with the label missing), and show that findmnt --verify is clean.

    Check your result
    • The line contains nofail and x-systemd.device-timeout=5.
    • sudo systemctl daemon-reload && sudo mount /backup fails quickly without hanging when the disk is absent, and the system stays up.
    • findmnt --verify prints no [E] lines.
    Hint

    nofail stops a failed mount from being a boot failure. x-systemd.device-timeout= limits how long systemd waits for the device. The mount point must exist.

    Solution
    sudo mkdir -p /backup
    echo 'LABEL=BACKUP /backup xfs defaults,nofail,x-systemd.device-timeout=5 0 0' | sudo tee -a /etc/fstab
    sudo systemctl daemon-reload
    time sudo mount /backup || true
    findmnt --verify
  3. Challenge

    A unit that never starts

    Setup (lab VM):

    sudo tee /etc/systemd/system/app-a.service <<'EOT'
    [Unit]
    Description=App A
    After=app-b.service
    Requires=app-b.service
    [Service]
    ExecStart=/usr/bin/sleep infinity
    [Install]
    WantedBy=multi-user.target
    EOT
    sudo tee /etc/systemd/system/app-b.service <<'EOT'
    [Unit]
    Description=App B
    After=app-a.service
    Requires=app-a.service
    [Service]
    ExecStart=/usr/bin/sleep infinity
    EOT
    sudo systemctl mask network-online.target
    sudo systemctl daemon-reload

    systemctl start app-a fails. Find both problems with systemd's own tools (one is a cycle between the two units; the other is the masked target that other units pull in), repair them so app-a starts and stays up after reboot, and remove the mask without leaving other changes behind.

    Check your result
    • systemd-analyze verify /etc/systemd/system/app-a.service prints nothing about cycles.
    • systemctl is-active app-a app-b prints active twice; systemctl is-enabled app-a is enabled.
    • systemctl is-enabled network-online.target is static, not masked.
    Hint

    systemctl start app-a prints "Transaction order is cyclic". systemd-analyze verify points at the loop. Break it by removing the circular After= from one unit. systemctl list-unit-files | grep masked finds masked units; systemctl unmask removes the link to /dev/null.

    Solution
    sudo systemctl start app-a; echo "rc=$?"
    systemd-analyze verify /etc/systemd/system/app-a.service /etc/systemd/system/app-b.service
    sudo sed -i '/^After=app-a.service/d;/^Requires=app-a.service/d' /etc/systemd/system/app-b.service
    sudo systemctl list-unit-files | grep masked
    sudo systemctl unmask network-online.target
    sudo systemctl daemon-reload
    sudo systemctl enable --now app-a
    systemctl is-active app-a app-b