LFCS Mock Exam · Set 3
This is the third and final LFCS paper in this course, and it's deliberately lopsided in the way real exam prep should be once you know your weak spots. Nineteen performance-based tasks, one unbroken 120-minute sitting, still worth exactly 100 points split 25 / 25 / 20 / 20 / 10 across the same five official domains as Set 1 and Set 2 — but not split evenly across tasks this time. Networking gets six tasks here and Storage gets five, while Operations Deployment and Essential Commands shrink to three apiece to make room, because the two domains most real incident reviews actually live in deserve more independent repetitions under a clock, not just a bigger point total. Every task keeps the same shape as the other two papers — a plain-language brief, a done when check you run yourself against a real terminal, and a full worked solution folded under a summary that stays closed until you've either passed the check or given up on it — and every scenario below is new, not a reskin of anything from Set 1 or Set 2. Build the machine the night before, skim the pre-flight section, then start one timer and don't stop it until task nineteen is behind you.
Imagine the first two practice rounds you did were fixing whatever needed it that day, spread evenly across a whole house — a wobbly shelf here, a stuck window there. This one is different on purpose: today almost every job is either "get this box to the exact right address across town" or "make everything already in the garage fit without anything falling over when the door opens" — six delivery jobs, five packing jobs, and just a handful of smaller chores squeezed in around them. It's not that the house stopped having other problems. It's that you already know from the last two rounds which two kinds of job slow you down the most, so today's drill gives you more practice at exactly those, on the clock, before the day someone else times you for real.
Before you start — exam conditions
☺ Like you're 10: This drill only teaches you something if you run it exactly like the real thing — same clock, same rules, no notes you wouldn't have on the day.
Everything that made Set 1 and Set 2 honest applies here too, plus a slightly longer shopping list because this paper's lean into Networking and Storage touches tools the earlier two barely needed.
One — build the machine the night before, with more moving parts than usual. A throwaway VM works, but this paper wants at least three spare block devices attached (one for an LVM snapshot, two for a software RAID1 mirror) plus a second interface or two for VLAN/bridge/bond-adjacent work, and — for the network-block-device task — either a second small VM to act as the NBD server or nbd-server running locally as a stand-in against a loopback export. Install everything the paper touches before the clock starts:
# RHEL/Fedora family sudo dnf install -y lvm2 mdadm quota nbd haproxy sssd sssd-ldap nftables git openssl \ libvirt libvirt-daemon virt-install sudo modprobe 8021q nbd # Debian/Ubuntu family # sudo apt install -y lvm2 mdadm quota nbd-client nbd-server haproxy sssd nftables git \ # openssl libvirt-daemon-system virtinst lsblk # confirm the spare disks for the snapshot + RAID tasks are visible and empty ip -br link # confirm at least two extra interfaces exist for VLAN/bridge work
Two — one timer, started once. 120 minutes, no pausing for a stubborn typo or a VM that needs a reboot. Time lost fighting your own lab environment is real data about exam-day friction, not a reason to stop the clock.
Three — only the permitted resources, open before you start. Man pages, documentation installed by the distribution, and packages that ship with it — accessed from inside the exam terminal itself. No browser, no search engine, no this course, no AI assistant.
Four — no copying commands from the solution, and no AI assistance. If you get stuck, spend the budget, fail the task, and read the solution after the sitting ends.
Five — read all nineteen tasks before you touch a terminal. With six networking tasks bunched together, five minutes spent skimming the whole paper is what stops you from accidentally spending twenty minutes on the fourth one when the sixth was the two-command freebie.
This is an independent, unofficial study resource, not affiliated with the CNCF or The Linux Foundation. The 19-task shape, the 120-minute pace, the 100-point sheet and the 67% pass mark are a study design built around the domain weights and logistics documented on LFCS — the exam — including this paper's own choice to run six Networking tasks and five Storage tasks instead of four apiece, which is this course's authoring decision, not a claim about how the real exam allocates its 17–20 tasks. Price, duration, retake terms, the allowed-resource list and the domain weights themselves are all revised over time. Confirm everything current on the official LFCS certification page and the candidate handbook before you register or pay for anything.
Your time budget
☺ Like you're 10: Give every domain a slice of the two hours sized to how many jobs live in it, not to how important the topic sounds — Networking gets the biggest slice here because it has the most jobs, not because it matters more than Storage.
Nineteen tasks in 120 minutes still averages under six and a half minutes each once you carve out five minutes to read and six to verify at the end, but this paper's shape is lumpier than Set 1 or Set 2: Operations Deployment and Essential Commands get three bigger, denser tasks apiece, while Networking's six tasks and Storage's five are individually smaller and faster — the trade this paper is deliberately making is fewer points per task in exchange for more independent chances to get a wire or a disk right under pressure.
| Block | Tasks | Budget | Running total | Note |
|---|---|---|---|---|
| Read the whole paper | — | 5 min | 5 | Six Networking tasks in a row rewards spotting the cheap ones early. |
| Operations Deployment | OD1–OD3 | 9+7+8 = 24 min | 29 | Three dense tasks. OD1's libvirt VM is the longest single pole on the paper. |
| Networking | N1–N6 | 6+6+5+5+6+6 = 34 min | 63 | The biggest block on the clock. N5's HAProxy health check is the fiddliest. |
| Storage | S1–S5 | 5+5+5+5+5 = 25 min | 88 | Five even five-minute tasks — the most evenly paced block on the paper. |
| Essential Commands | E1–E3 | 5+6+6 = 17 min | 105 | E1's git bisect is fast once you trust the binary search; don't manually check every commit. |
| Users & Groups | U1–U2 | 4+5 = 9 min | 114 | Only 10 points, but two of the cheapest tasks on the whole paper. Never skip these. |
| Verify & sweep flagged tasks | — | 6 min | 120 | Re-run every done-when. Finish the nearly-done before starting anything new. |
The flag-and-move rule still applies exactly as it did on the first two papers: when a task passes roughly its budgeted minutes without a passing done-when check, stop, write one line about where you stalled, leave whatever you've applied in place, and move to the next task. On a paper this heavy on Networking, the rule matters more than usual — six tasks means six chances to fall down the same rabbit hole, and only one of them is worth more than five points.
More tasks in a domain is not the same as more points in a domain — this paper proves the distinction on purpose. Networking and Storage still cap out at 25 and 20 points respectively, exactly like Set 1 and Set 2; what changed is that those points are now spread across six and five smaller, independent tasks instead of four apiece. That trade is worth making once you already know Networking or Storage is your weak spot: more repetitions under time pressure teach pacing and pattern recognition in a way one large task worth the same total points cannot.
Operations Deployment — OD1 to OD3 (25 points)
☺ Like you're 10: Three bigger jobs this time — bringing a whole virtual machine to life, teaching this box to trust and install new software from a private source, and rescuing a machine that won't finish booting.
Fewer, denser tasks than Set 1 or Set 2 to make room for Networking and Storage below. Background: LFCS — the exam's Operations Deployment section.
OD1 · Bring a pre-built VM to life with libvirt and make it survive a reboot
A qcow2 disk image for a new edge node, /var/lib/libvirt/images/edge-01.qcow2, has been staged on this host by an earlier build step. It needs to become a running, named libvirt domain — 2 vCPUs, 2048 MB of RAM, attached to the default virtual network — that comes back up automatically the next time this host reboots, without anyone running a manual virsh start.
Your task:
- Define and start a domain named
edge-01around the existing disk image, without reinstalling an OS onto it. - Confirm it's actually running, not just defined.
- Enable autostart so the hypervisor brings it up on the next host boot.
Done when: virsh list --all shows edge-01 in state running; virsh dominfo edge-01 reports Autostart: enable; and virsh domifaddr edge-01 returns at least one lease once the guest has finished booting. (9 points)
Show the worked solution
virt-install \ --name edge-01 \ --memory 2048 \ --vcpus 2 \ --disk path=/var/lib/libvirt/images/edge-01.qcow2,format=qcow2 \ --import \ --os-variant generic \ --network network=default \ --graphics none \ --noautoconsole virsh list --all # edge-01 ... running
virsh autostart edge-01 virsh dominfo edge-01 | grep -i autostart # Autostart: enable # give the guest a minute to boot and request a DHCP lease, then: virsh domifaddr edge-01
Why: --import is the whole trick — it skips the OS installer entirely and boots the existing qcow2 disk as-is, which is the realistic shape of "bring an already-built image online" rather than "install a new OS from scratch," a task that would never fit inside a points budget this small. virsh autostart writes a symlink under /etc/libvirt/qemu/autostart/ pointing at the domain's XML definition, so it's libvirtd itself — not a cron job, not a systemd unit you'd have to write by hand — that brings the guest back after the host reboots.
OD2 · Add a signed private package repository and pin an exact version
A vendor tool, telemetry-agent, ships from a private RPM repository at https://repo.mission.internal/rpm/el9/, GPG-signed. It needs to be installed at exactly version 2.4.1 — not whatever is newest — and it needs to stay at that version even after a routine dnf update runs later.
Your task:
- Add the repository with signature checking enabled and import its GPG key.
- Install exactly version
2.4.1oftelemetry-agent. - Lock that package so a later
dnf updatecannot silently move it to a newer version.
Done when: dnf repolist shows mission-tools enabled; rpm -q telemetry-agent reports exactly telemetry-agent-2.4.1; and after dnf versionlock list shows the package locked, running dnf update telemetry-agent reports nothing to do. (8 points)
Show the worked solution
# /etc/yum.repos.d/mission-tools.repo [mission-tools] name=Mission Tools baseurl=https://repo.mission.internal/rpm/el9/ enabled=1 gpgcheck=1 gpgkey=https://repo.mission.internal/RPM-GPG-KEY-mission
rpm --import https://repo.mission.internal/RPM-GPG-KEY-mission dnf makecache dnf repolist # mission-tools ... enabled dnf list --showduplicates telemetry-agent # confirm 2.4.1 is an available build dnf install -y telemetry-agent-2.4.1 rpm -q telemetry-agent dnf install -y python3-dnf-plugin-versionlock # if not already present dnf versionlock add telemetry-agent dnf versionlock list dnf update telemetry-agent # "Nothing to do."
Why: gpgcheck=1 without a correctly imported key just makes every install from the repo fail loudly rather than silently accept an unsigned or tampered package — that failure is the entire point of the check, and skipping the rpm --import step is the most common way this task looks finished right up until the first real install command. Installing a specific version and locking it are two separate actions that people conflate: dnf install telemetry-agent-2.4.1 only controls what lands today, while versionlock is the thing that stops a routine, unrelated dnf update from quietly pulling 2.5.0 over it next week.
OD3 · Recover a host stuck in emergency mode after a bad fstab edit
This host was rebooted after someone hand-edited /etc/fstab and it now drops into emergency mode instead of reaching a normal login — a systemd dependency failure tied to a mount unit. You have an emergency shell.
Your task:
- From the emergency shell, identify which fstab entry is actually causing the failure, without guessing.
- Fix the entry so the host can boot normally again.
- Prove the fix is correct before committing to another reboot.
Done when: mount -a completes with no errors from the emergency shell; after a reboot, systemctl is-system-running reports running (or degraded with no failed mount units specifically); and systemctl list-units --failed shows no .mount units. (8 points)
Show the worked solution
# from the emergency shell journalctl -xb | grep -iE "fstab|mount|dependency failed" # ... reveals a mismatched UUID or a bad mount point on one specific fstab line
mount -o remount,rw / # the emergency shell often mounts root read-only
vi /etc/fstab # fix the offending line — corrected UUID, or add nofail
# for a device that isn't always guaranteed to be present
mount -a # rehearse the fix exactly as boot would apply it,
# WITHOUT committing to a second reboot yet
systemctl default # attempt to continue the current boot without rebooting
reboot # once mount -a is clean, confirm with a real reboot
systemctl is-system-running
systemctl list-units --failedWhy: mount -a is the safe rehearsal for an fstab edit — it applies the file exactly as the boot sequence would, without committing to a second reboot that could strand you again if the fix is still wrong, which is precisely the mistake that put this host into emergency mode the first time. A device or filesystem that isn't guaranteed to always be present belongs behind the nofail option rather than a plain required entry, because systemd's default assumption is that a missing device is a boot-blocking failure worth stopping for, not a soft warning to log and move past.
Networking — N1 to N6 (25 points)
☺ Like you're 10: Six jobs about making sure the right message reaches the right place — a private lane on a shared wire, a reply that goes back out the door it came in, a shared fence between two rooms, a newer kind of address, traffic split between two helpers, and a name that points to the right desk.
The biggest single block on this paper, both in points and in task count. Per LFCS — the exam and Platform Engineering's own Networking chapter, this is also the domain where real incidents concentrate. Use ip, not ifconfig; ss, not netstat; nft, not raw iptables; resolvectl, not a hand-edited resolv.conf, wherever the host runs systemd-resolved.
N1 · Tag a VLAN subinterface for isolated management traffic
Management traffic for the telemetry rack needs to move onto VLAN 100, isolated from the untagged production LAN sharing the same physical NIC, eth0. The subinterface needs address 10.100.0.5/24 and must survive a reboot.
Your task:
- Create the tagged VLAN subinterface
eth0.100oneth0and assign it the required address. - Persist it using your distribution's real network configuration mechanism.
- Confirm the tag and address survive a service restart, not just a live check.
Done when: ip -d link show eth0.100 reports vlan protocol 802.1Q id 100; ip addr show eth0.100 lists 10.100.0.5/24; and restarting systemd-networkd (or the NetworkManager equivalent) leaves both in place. (5 points)
Show the worked solution
ip link add link eth0 name eth0.100 type vlan id 100 ip addr add 10.100.0.5/24 dev eth0.100 ip link set eth0.100 up ip -d link show eth0.100 # confirm: vlan protocol 802.1Q id 100
# /etc/systemd/network/10-eth0.network (the parent interface — must reference the VLAN) [Match] Name=eth0 [Network] VLAN=eth0.100 # /etc/systemd/network/20-eth0.100.netdev [NetDev] Name=eth0.100 Kind=vlan [VLAN] Id=100 # /etc/systemd/network/20-eth0.100.network [Match] Name=eth0.100 [Network] Address=10.100.0.5/24
systemctl restart systemd-networkd ip -d link show eth0.100 ip addr show eth0.100
Why: an 802.1Q tag is how one physical NIC and one upstream switch port carry several logically isolated networks at once — but the switch port on the other end has to be configured as a trunk carrying VLAN 100, or the tagged frames never arrive, which is the single most common reason this task looks correct on the host and still passes no traffic. The parent interface's .network file has to explicitly list VLAN=eth0.100 under [Network]; without that line, networkd creates the tagged device from the .netdev file but never actually attaches it to eth0.
N2 · Route return traffic out the interface it actually belongs to
This host is multi-homed: eth0 carries the default route via ISP-A, and eth1 is a secondary uplink through ISP-B at 198.51.100.20/24, gateway 198.51.100.1. Traffic sourced from 198.51.100.20 keeps leaving via the default route on eth0 instead of back out eth1, and ISP-B's edge firewall — correctly — drops it as spoofed.
Your task:
- Add a policy rule so any packet sourced from
198.51.100.20uses a separate routing table. - Populate that table with a default route via ISP-B's gateway, out
eth1. - Persist both the rule and the table so they survive a network service restart.
Done when: ip route get 8.8.8.8 from 198.51.100.20 reports it would go via 198.51.100.1 dev eth1; ip rule show lists the custom rule; and restarting systemd-networkd leaves both the rule and the table populated. (4 points)
Show the worked solution
ip rule add from 198.51.100.20 table 100 ip route add default via 198.51.100.1 dev eth1 table 100 ip route add 198.51.100.0/24 dev eth1 src 198.51.100.20 table 100 ip rule show ip route show table 100 ip route get 8.8.8.8 from 198.51.100.20 # confirm it now selects table 100 / eth1
# /etc/systemd/network/10-eth1.network (additions) [RoutingPolicyRule] From=198.51.100.20 Table=100 [Route] Gateway=198.51.100.1 Table=100
systemctl restart systemd-networkd ip rule show ip route show table 100
Why: a routing table can only hold one default route, so "reply out whichever interface a packet's source address belongs to" needs a second table entirely — ip rule decides which table a route lookup uses based on the packet's source address, before the ordinary destination-based lookup ever runs. Skipping this on a multi-homed host produces exactly this symptom: the packet leaves correctly addressed, on the wrong wire, and a remote firewall correctly treats a reply arriving from the "wrong" path as spoofed traffic and silently drops it.
N3 · Bridge a physical interface for two isolated lab guests
A pair of local KVM guests need a private lab network on their own bridge — not the default NAT'd libvirt network — with a dedicated physical uplink, eth3, enslaved to it. The bridge itself needs a usable management address.
Your task:
- Create bridge
br0and enslaveeth3to it. - Assign
10.50.0.1/24to the bridge itself, not toeth3. - Confirm the slave interface is actually forwarding, not just attached.
Done when: bridge link show lists eth3 with master br0 state forwarding; ip addr show br0 shows 10.50.0.1/24; and ip addr show eth3 shows no IPv4 address of its own. (4 points)
Show the worked solution
ip link add name br0 type bridge ip link set eth3 down ip link set eth3 master br0 ip link set eth3 up ip link set br0 up ip addr add 10.50.0.1/24 dev br0 bridge link show # eth3 ... master br0 state forwarding ip addr show br0 # 10.50.0.1/24 ip addr show eth3 # no IPv4 address — the bridge owns it now
# /etc/systemd/network/25-br0.netdev [NetDev] Name=br0 Kind=bridge # /etc/systemd/network/25-br0.network [Match] Name=br0 [Network] Address=10.50.0.1/24 # /etc/systemd/network/10-eth3.network [Match] Name=eth3 [Network] Bridge=br0
Why: bridging works at layer 2 — enslaving eth3 to br0 means the bridge, not the physical NIC, now owns any IP address for this segment, which is why the address moves to br0 instead of being duplicated on both. Bringing the slave down before enslaving it and back up afterward avoids a brief window where the interface still carries stale IP state from before it joined the bridge — a subtlety that almost never bites on a fresh lab box but reliably does on one that's been reconfigured a few times already.
N4 · Add a routable IPv6 address and prove connectivity, not just presence
The telemetry-archive host needs a routable IPv6 address on eth0 — 2001:db8:100::5/64 — with a default IPv6 route via 2001:db8:100::1, persisted, and actually reachable, not merely configured.
Your task:
- Assign the address and add the default IPv6 route.
- Persist both.
- Prove the address is a global one and that the route actually carries traffic.
Done when: ip -6 addr show eth0 lists 2001:db8:100::5/64 with scope global (not only the automatic fe80:: link-local address); ip -6 route get 2001:db8:100::1 resolves via eth0; and ping -6 -c2 2001:db8:100::1 receives replies. (4 points)
Show the worked solution
ip -6 addr add 2001:db8:100::5/64 dev eth0 ip -6 route add default via 2001:db8:100::1 dev eth0 ip -6 addr show eth0 # confirm scope global, not just link-local ip -6 route get 2001:db8:100::1 ping -6 -c2 2001:db8:100::1
# /etc/systemd/network/10-eth0.network (additions) [Network] Address=2001:db8:100::5/64 Gateway=2001:db8:100::1
Why: every Linux interface auto-generates a link-local fe80::/10 address on its own the moment it comes up — that address alone proves nothing about whether the global address you were actually asked to configure is present, which is exactly why the done-when check insists on scope global specifically. IPv6 has no ARP; neighbor discovery does that job over ICMPv6, so a failed ping -6 after an address and route that both look correct usually means neighbor-discovery or ICMPv6 traffic is being filtered somewhere on the path, not that the addressing syntax was wrong.
N5 · Load-balance two backends with a health check that actually removes a dead one
Two identical backend services run on 10.0.2.11:8080 and 10.0.2.12:8080. The public-facing host needs to round-robin across both on port 80, and a backend that stops responding needs to fall out of rotation automatically — not stay in it, quietly failing every request routed to it.
Your task:
- Configure HAProxy to front both backends on port 80 with round-robin balancing.
- Add an active health check against each backend.
- Prove the failover actually works by stopping one backend and confirming the frontend keeps serving.
Done when: haproxy -c -f /etc/haproxy/haproxy.cfg reports no errors; repeated requests against the frontend return 200 and are served by both backends; and after one backend is stopped, requests against the frontend still return 200, served entirely by the surviving backend. (4 points)
Show the worked solution
# /etc/haproxy/haproxy.cfg (additions)
frontend telemetry_front
bind *:80
default_backend telemetry_back
backend telemetry_back
balance roundrobin
option httpchk GET /healthz
server web1 10.0.2.11:8080 check
server web2 10.0.2.12:8080 checkhaproxy -c -f /etc/haproxy/haproxy.cfg # validate BEFORE reload — never reload blind
systemctl enable --now haproxy
systemctl reload haproxy
for i in 1 2 3 4; do curl -s localhost/ -o /dev/null -w "%{http_code}\n"; done# prove the health check actually removes a dead backend
systemctl stop web1-backend # simulate a failure on web1
for i in 1 2 3 4; do curl -s localhost/ -o /dev/null -w "%{http_code}\n"; done
# still all 200s — every request now served by web2 aloneWhy: option httpchk paired with check on each server line is what makes this a real load balancer instead of a static round-robin list — without an active health check, HAProxy keeps sending a share of every request to a dead backend, and each of those requests simply times out for the client. Validating with haproxy -c before reload matters for the same reason sshd -t mattered on the earlier papers in this set: a syntax error in a config file that's already running doesn't get applied by a reload, it gets silently ignored, and the failure this test is looking for never actually happens.
N6 · Route one internal domain's DNS lookups to a different resolver
Queries for anything under *.mission.internal need to go to an internal resolver, 10.0.0.53, while every other lookup keeps using the normal upstream DNS. A previous engineer's fix was a single /etc/hosts entry, which doesn't scale past one hostname and breaks the moment a second internal host needs resolving.
Your task:
- Point
eth0's DNS at10.0.0.53for themission.internaldomain specifically, not globally. - Persist the configuration.
- Confirm internal names resolve via the internal resolver while everything else still resolves normally.
Done when: resolvectl status eth0 shows DNS server 10.0.0.53 with domain ~mission.internal; resolvectl query relay.mission.internal reports it was answered via eth0's configured server; and resolvectl query example.com still resolves through the normal upstream. (4 points)
Show the worked solution
resolvectl dns eth0 10.0.0.53 resolvectl domain eth0 "~mission.internal" resolvectl status eth0 resolvectl query relay.mission.internal # answered via eth0's DNS server resolvectl query example.com # still answered via the default upstream
# /etc/systemd/network/10-eth0.network (additions) [Network] DNS=10.0.0.53 Domains=~mission.internal
systemctl restart systemd-networkd resolvectl status eth0
Why: a domain listed with a leading ~ is a routing-only domain in systemd-resolved's syntax — it tells the resolver "send queries under this suffix to this link's DNS server" without making this link the default resolver for everything, which is exactly the split-horizon behavior a single blanket /etc/hosts entry could never scale to more than one name. This is the real mechanism DNS-based service discovery relies on; a static /etc/hosts line is a one-off patch that has to be hand-maintained forever and covers exactly one hostname, never a subdomain or a host added later.
"I used to think of networking tasks as the annoying ones — fiddly syntax, easy to get subtly wrong, hard to tell why a curl just hung instead of erroring. Then I spent an afternoon debugging exactly N2's problem on a real multi-homed box: outbound packets left looking perfectly correct, and every single reply vanished, because nothing told the kernel to route the reply back out the interface the request had actually arrived on. Six tasks like this in one sitting isn't excessive — it's the minimum rehearsal for a category of bug that doesn't announce itself as a networking problem at all. It just looks like the internet stopped working for no reason."
Storage — S1 to S5 (20 points)
☺ Like you're 10: Five jobs about protecting and organizing what's already on a disk — a safety copy before a risky change, a mirror that survives one disk dying, a fair share of space per person, a disk borrowed from another machine, and figuring out why "full" doesn't always mean what you think.
Background: LFCS — the exam, and for the tool depth, LVM & Linux Storage Tools.
S1 · Snapshot a logical volume before a risky change, then roll back
A risky in-place migration is about to run against /dev/vg_data/lv_app, mounted at /srv/app. If it corrupts data, the fix needs to be a rollback, not a restore from a separate backup system that might not even exist yet.
Your task:
- Create a 2G snapshot of
lv_appbefore the migration. - Assume the migration went wrong. Roll
lv_appback to the snapshot. - Confirm the rollback actually took effect.
Done when: before rollback, lvs vg_data lists lv_app_snap with origin lv_app; after lvconvert --merge and reactivating the volume, lvs vg_data no longer lists the snapshot at all; and the filesystem mounted at /srv/app reflects the pre-migration state. (4 points)
Show the worked solution
lvcreate -L 2G -s -n lv_app_snap /dev/vg_data/lv_app lvs vg_data # lv_app_snap ... Origin lv_app # --- the migration runs here, and goes wrong --- umount /srv/app lvconvert --merge vg_data/lv_app_snap # schedules the merge lvchange -an vg_data/lv_app # deactivate... lvchange -ay vg_data/lv_app # ...and reactivate to complete a merge on an in-use LV mount /dev/vg_data/lv_app /srv/app lvs vg_data # lv_app_snap is gone — merged and consumed
Why: an LVM snapshot works by copy-on-write — it doesn't duplicate the origin's data up front, it only starts recording blocks as they change on the origin after the snapshot is taken, which is why it's cheap to create but also why leaving one running for a long time under heavy writes can fill its own allocated space and invalidate it. lvconvert --merge schedules the origin to be replaced by the snapshot's recorded changes on its next activation — for an actively mounted LV, that usually means deactivating and reactivating it (or a reboot) before the merge visibly completes, which is the detail that trips people up when the merge "doesn't seem to happen" right after the command returns.
S2 · Mirror two disks with software RAID1, then survive a simulated failure
A dataset on two spare disks, /dev/sdc and /dev/sdd, must survive a single disk failure without taking the mount offline. Build the mirror, mount it persistently, then prove it actually tolerates a failure.
Your task:
- Create a RAID1 array across the two disks, put a filesystem on it, and mount it persistently at
/srv/mirrored. - Simulate a failed member disk.
- Recover the array by removing the failed member and adding a replacement.
Done when: cat /proc/mdstat initially shows md0 fully in sync across both members; after mdadm --fail, it shows the array degraded but still active, with /srv/mirrored still mounted and readable; and after removing the failed disk and adding a fresh one, /proc/mdstat shows the array resyncing back to full redundancy. (4 points)
Show the worked solution
mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdc /dev/sdd cat /proc/mdstat # resync in progress, then active/in-sync mkfs.xfs /dev/md0 mkdir -p /srv/mirrored mount /dev/md0 /srv/mirrored mdadm --detail --scan >> /etc/mdadm.conf # Debian family: /etc/mdadm/mdadm.conf — check which yours reads echo "/dev/md0 /srv/mirrored xfs defaults,nofail 0 2" >> /etc/fstab mount -a
# simulate a failed disk mdadm /dev/md0 --fail /dev/sdc cat /proc/mdstat # sdc marked (F); array degraded but still active ls /srv/mirrored # still readable — the mount never had to move # recover: drop the failed member, add a replacement mdadm /dev/md0 --remove /dev/sdc mdadm /dev/md0 --add /dev/sde cat /proc/mdstat # resyncing back to full redundancy
Why: RAID1 mirrors every write to both members, so the array tolerates exactly one member failing without losing data or even taking the filesystem offline — the mount point never moves, which is the entire value proposition over restoring from a nightly backup after the fact. mdadm --detail --scan written into the array's own config file is what lets it reassemble automatically at boot by its recorded array UUID, rather than depending on device names like /dev/sdc staying attached to the same physical disk — the same instability a UUID-based fstab entry solves for a plain filesystem elsewhere on this trilogy.
S3 · Cap one user's disk usage without touching anyone else's
/home lives on its own ext4 filesystem, and one account, intern, keeps filling it with a runaway build cache. Set a block quota for intern specifically — 5G soft, 6G hard, with the standard grace period — without capping any other user.
Your task:
- Enable quota accounting on
/home. - Set
intern's soft and hard block limits. - Confirm the limits apply only to
intern.
Done when: repquota /home lists intern with a 5G soft / 6G hard block limit; quota -u intern reflects the same; and repquota /home shows no limit set for any other user on the filesystem. (4 points)
Show the worked solution
# /etc/fstab — /home's line needs the usrquota option /dev/vg_data/lv_home /home ext4 defaults,usrquota 0 2
mount -o remount /home quotacheck -cum /home # creates aquota.user, initial usage scan quotaon /home # limits are in 1K blocks: 5G = 5242880, 6G = 6291456 — setquota takes no unit suffixes setquota -u intern 5242880 6291456 0 0 /home repquota /home # intern ... 5242880 6291456 ... quota -u intern
Why: the soft limit is a warning threshold with a grace period attached, and the hard limit is the actual wall — an account can sit over its soft limit for the whole grace window while still doing real work, and only gets forcibly blocked once it crosses the hard limit or the grace period itself expires, which is deliberately more forgiving than one hard cap that refuses a write the instant usage ticks over a single number. Quotas are tracked per filesystem, not per directory, which is exactly why this only works cleanly because /home already has its own filesystem — the same setup on a shared root filesystem would need every other account's data walled off from intern's first.
S4 · Attach a remote block device over NBD and make it reattach on boot
A remote storage host exports a raw block device over the network at storage-01:10809, export name mission-blk. It needs to appear locally as a normal block device, hold a filesystem, mount at /mnt/remote-block, and come back automatically after a reboot — without anyone manually re-running nbd-client.
Your task:
- Attach the export locally as
/dev/nbd0, create a filesystem on it, and mount it. - Write a systemd unit that performs the attach automatically, ordered after the network is up.
- Wire the mount to depend on that unit so it doesn't try to mount a device that was never attached.
Done when: lsblk /dev/nbd0 shows it attached and mounted at /mnt/remote-block; systemctl is-enabled nbd-mission-blk.service reports enabled; and after systemctl restart nbd-mission-blk.service && mount -a, the mount comes back with no manual nbd-client command typed by hand. (4 points)
Show the worked solution
modprobe nbd max_part=8 nbd-client storage-01 10809 /dev/nbd0 -N mission-blk lsblk /dev/nbd0 mkfs.ext4 /dev/nbd0 mkdir -p /mnt/remote-block mount /dev/nbd0 /mnt/remote-block
# /etc/systemd/system/nbd-mission-blk.service [Unit] Description=Attach mission-blk NBD export After=network-online.target Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/sbin/modprobe nbd max_part=8 ExecStart=/usr/sbin/nbd-client storage-01 10809 /dev/nbd0 -N mission-blk ExecStop=/usr/sbin/nbd-client -d /dev/nbd0 [Install] WantedBy=multi-user.target
# /etc/fstab /dev/nbd0 /mnt/remote-block ext4 _netdev,x-systemd.requires=nbd-mission-blk.service 0 0
systemctl daemon-reload systemctl enable --now nbd-mission-blk.service mount -a
Why: unlike a local disk or even an NFS export, an NBD device has to be explicitly attached with nbd-client before it exists as a block device at all — /dev/nbd0 is a pre-created placeholder node, not something the kernel discovers on its own, so nothing in /etc/fstab by itself can make it appear. x-systemd.requires= is what lets a plain fstab line depend on that attach service finishing first — the same "wait for the thing that has to exist before the mount" problem _netdev alone solves for NFS, extended here to a device that has to be manufactured by a client command before it can be mounted.
S5 · Diagnose a "disk full" error on a filesystem that isn't full
A batch job writing into /var/spool/jobs — its own ext4 filesystem — keeps failing with No space left on device, but df -h reports the filesystem is only 40% full by capacity. Find the real cause and fix it without deleting anything the job still needs.
Your task:
- Explain the contradiction between the error and what
df -hreports, without guessing. - Identify which directory is actually responsible.
- Reclaim the resource that's actually exhausted, without touching unrelated live job files.
Done when: df -i /var/spool/jobs shows IUse% back under a safe threshold; the batch job's next write succeeds; and a file count of the directory before and after shows only stale files were removed. (4 points)
Show the worked solution
df -h /var/spool/jobs # 40% full by space — doesn't explain the error
df -i /var/spool/jobs # IUse% 100%, IFree 0 — every inode is used up
find /var/spool/jobs -xdev -type d -exec sh -c \
'echo "$(find "$1" -maxdepth 1 | wc -l) $1"' _ {} \; | sort -n | tail
# identifies /var/spool/jobs/tmp/ as the directory holding millions of tiny files# clean up genuinely stale files only — never a blind rm -rf find /var/spool/jobs/tmp -type f -empty -mtime +7 -delete df -i /var/spool/jobs # inodes freed, IUse% well under 100%
Why: df -h reports free bytes, but every filesystem also has a fixed number of inodes decided at mkfs time — one inode per file or directory regardless of that file's size — so a directory holding millions of near-empty files can exhaust every inode while barely touching the byte budget, and the kernel refuses new files with the exact same ENOSPC "No space left on device" error either way. df -i is the one command that tells the two failure modes apart; jumping straight to hunting for large files here would send you looking for a problem that doesn't exist, on a filesystem whose actual problem is file count, not file size.
Essential Commands — E1 to E3 (20 points)
☺ Like you're 10: Three jobs — finding exactly which change broke something without checking every change one by one, freeing up space a log file is quietly hoarding, and proving a website's certificate is trusted all the way up, not just today.
Background: LFCS — the exam, and for the TLS half, cert-manager automates in a cluster exactly the chain problem E3 makes you diagnose by hand.
E1 · Find the exact commit that broke a script with a binary search, not a guess
deploy.sh in the ops-scripts repository started silently exiting 1 sometime in the last dozen commits, and nobody knows which one. HEAD is broken; the tag v3 is known good.
Your task:
- Use a binary search over the commit history to find the exact first bad commit — not a manual walk through each one.
- Confirm which commit it is.
- Leave the repository in a clean, restored state when you're done.
Done when: the bisect process reports a single specific commit hash as the first bad commit; git status afterward shows a clean working tree with no detached-HEAD leftover; and that commit's diff plausibly touches deploy.sh. (7 points)
Show the worked solution
cd ops-scripts git bisect start git bisect bad HEAD git bisect good v3 # git checks out a midpoint commit automatically ./deploy.sh --dry-run; echo $? # test at each step git lands you on git bisect good # or: git bisect bad — depending on the result # ... repeat; git narrows the range by half each time # once git reports "<hash> is the first bad commit": git show <hash> # confirm the diff touches deploy.sh git bisect log > bisect-report.txt # optional record of the search git bisect reset # return to the original branch and HEAD
Why: bisect is a binary search over commit history — with a dozen suspect commits it needs roughly four tests, not twelve, to isolate the exact one, which is the entire reason it beats manually checking out and testing each commit in order. git bisect reset at the end matters as much as the search itself: bisect leaves the working tree in a detached-HEAD state at whatever commit it last checked out, and skipping the reset is how you accidentally keep working — or worse, commit — on a detached head with no branch pointing at it.
E2 · Rotate a runaway log without leaking the disk space back to nothing
beacon-api.log under /var/log/beacon/ has grown to 40G because no rotation was ever configured, and it's now the actual cause of repeated disk-full alerts on /var. Configure rotation, and make sure the running service actually releases the old file's space rather than continuing to write into it under a different name.
Your task:
- Configure daily rotation, keeping 14 days compressed.
- Make sure the writing service reopens its log file after each rotation.
- Prove a forced rotation right now actually frees space, not just renames the file.
Done when: logrotate -d /etc/logrotate.d/beacon-api reports no config errors; a forced rotation produces a rotated .1 file plus a small, fresh beacon-api.log; and checking the service's open file descriptors shows it holding the new file, not the rotated one. (7 points)
Show the worked solution
# /etc/logrotate.d/beacon-api
/var/log/beacon/beacon-api.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 beacon beacon
postrotate
systemctl kill -s HUP beacon-api.service
endscript
}logrotate -d /etc/logrotate.d/beacon-api # dry run — shows what WOULD happen, changes nothing logrotate -f /etc/logrotate.d/beacon-api # force one rotation right now to prove it works ls -la /var/log/beacon/ # beacon-api.log.1 present, new beacon-api.log small again ls -la /proc/$(pgrep beacon-api)/fd | grep beacon-api.log # holding the NEW file, not the rotated one df -h /var
Why: rotation alone — renaming the log file — doesn't help if the writing process keeps its original file descriptor open; it just keeps appending to what is now an unlinked, invisible file that still occupies disk space until the process exits, which recreates exactly the disk-full problem this task exists to solve. The postrotate block's HUP signal is what tells the running process to close and reopen its log file by name, picking up the fresh file logrotate just created — skipping it is the most common way this task "passes" the file-count check and still frees no actual space.
E3 · Diagnose and fix a broken TLS chain, not a broken certificate
An internal API at api.mission.internal serves a certificate signed by a private intermediate CA, but some clients report a trust failure even though the certificate itself hasn't expired. Determine whether the service is actually sending the full chain, and fix it if it isn't.
Your task:
- Determine how many certificates the live service actually sends during a handshake.
- Prove whether the chain verifies all the way to a trusted root.
- Fix the served chain if the intermediate is missing.
Done when: openssl s_client -showcerts against the live service returns two certificates — leaf then intermediate — not one; openssl verify -CAfile <root> -untrusted <intermediate> <leaf> reports OK; and a client that trusts only the root (not the intermediate directly) completes the handshake without a chain error. (6 points)
Show the worked solution
openssl s_client -connect api.mission.internal:443 -servername api.mission.internal \ -showcerts </dev/null 2>/dev/null | grep -c "BEGIN CERTIFICATE" # 1 — only the leaf is being sent. The intermediate is missing from the wire. openssl verify -CAfile /etc/pki/ca-trust/mission-root-ca.pem \ -untrusted intermediate-ca.pem server.pem # fails without the intermediate supplied separately
# fix: serve leaf + intermediate concatenated, in that order cat server.pem intermediate-ca.pem > /etc/nginx/tls/api.mission.internal-fullchain.pem # update nginx's ssl_certificate directive to point at the fullchain file, not the bare leaf nginx -t && systemctl reload nginx openssl s_client -connect api.mission.internal:443 -showcerts </dev/null 2>/dev/null \ | grep -c "BEGIN CERTIFICATE" # now 2 — leaf and intermediate both sent
Why: a CA hierarchy only works if every hop between the leaf and a certificate the client already trusts is actually present during the handshake — a server can hold a perfectly valid intermediate certificate on disk and still fail every client that doesn't already trust that intermediate directly, if it never sends it over the wire. openssl verify -untrusted mirrors exactly what a real client does: build a path from the leaf up through supplied intermediates to a trusted root, and refuse to trust a leaf it can't chain all the way up on its own.
Users & Groups — U1 to U2 (10 points)
☺ Like you're 10: The smallest slice of the exam, but two of its cheapest points — giving every account a shared set of tools automatically, and letting one host trust a shared directory of accounts without breaking local logins if that directory ever goes quiet.
Only 10% of the paper, but per the flag-and-move discipline above, never the two tasks worth skipping.
U1 · Set a shared environment for everyone, plus an extra for one account
Every account on this host needs MISSION_HOME=/opt/mission and /opt/mission/bin on its PATH by default. One service account, svc-nav, additionally needs /opt/mission/nav-tools on its own PATH — a directory no other account should see added.
Your task:
- Add the shared environment variable and
PATHentry for every account, system-wide. - Add
svc-nav's extraPATHentry without affecting any other account. - Confirm both layers apply correctly for the right accounts.
Done when: su - alice -c 'echo $PATH' (any other real account) includes /opt/mission/bin but not /opt/mission/nav-tools; su - svc-nav -c 'echo $PATH' includes both; and MISSION_HOME is set correctly under any login session. (4 points)
Show the worked solution
# /etc/profile.d/mission.sh — sourced automatically for every login shell export MISSION_HOME=/opt/mission export PATH="$PATH:/opt/mission/bin" chmod 644 /etc/profile.d/mission.sh
# ~svc-nav/.bash_profile [ -f /etc/profile ] && . /etc/profile export PATH="$PATH:/opt/mission/nav-tools" chown svc-nav:svc-nav ~svc-nav/.bash_profile
su - alice -c 'echo $MISSION_HOME; echo $PATH' # bin present, nav-tools NOT present su - svc-nav -c 'echo $PATH' # both present
Why: /etc/profile.d/*.sh is the supported place for system-wide environment additions — every file in it gets sourced by /etc/profile for every login shell, which is why it wins over hand-editing /etc/profile itself directly (a package update can legitimately replace that file; a drop-in survives it, the same pattern as a systemd unit override elsewhere in this trilogy). A per-account addition has to layer on top in that account's own shell startup file, and has to explicitly source the system profile first if it wants both layers — skip that line and svc-nav silently loses MISSION_HOME while still gaining its extra PATH entry.
U2 · Point one host at LDAP-backed accounts without breaking local logins
This host needs to authenticate one group of contractor accounts against an existing corporate LDAP directory at ldap://ldap.mission.internal, while every local account — including root — must keep working exactly as before if LDAP is briefly unreachable.
Your task:
- Configure SSSD to look up accounts against the LDAP directory.
- Set the NSS lookup order so local accounts are always checked first.
- Prove local accounts survive an LDAP outage, while LDAP-only accounts don't.
Done when: getent passwd <ldap-user> returns a full LDAP-backed entry; /etc/nsswitch.conf lists files before sss on the passwd and group lines; and after systemctl stop sssd, getent passwd root and a local-account login both still work, while the LDAP-only account stops resolving until sssd is back. (6 points)
Show the worked solution
# /etc/sssd/sssd.conf [sssd] services = nss, pam domains = mission.internal [domain/mission.internal] id_provider = ldap auth_provider = ldap ldap_uri = ldap://ldap.mission.internal ldap_search_base = dc=mission,dc=internal cache_credentials = true
chmod 600 /etc/sssd/sssd.conf authselect select sssd --force systemctl enable --now sssd grep -E "^(passwd|group):" /etc/nsswitch.conf # passwd: files sss # group: files sss getent passwd contractor.jane # resolves via LDAP through sssd id contractor.jane getent passwd root # resolves purely from /etc/passwd, no LDAP round trip
# prove the outage behavior systemctl stop sssd getent passwd root # still works — local, unaffected getent passwd contractor.jane # fails to resolve until sssd is back
Why: the order in nsswitch.conf is a search order, not a preference — files sss means every lookup checks /etc/passwd//etc/group first and only falls through to SSSD (and therefore LDAP) for names not found locally, which is precisely what keeps root and other local accounts working through an LDAP outage. This is "configure the system to use LDAP user and group accounts" in its actual exam shape: not hand-writing raw LDAP PAM modules, but standing up SSSD as the intermediary and getting the NSS search order right around it.
Score yourself
☺ Like you're 10: Add up the points, turn it into a percentage, and then do the more useful thing — look at which topic cost you the most and go fix that specifically.
Mark after a short break — grading your own work while still adrenalized produces generous nonsense. Award full points only when the done-when check actually passed for real, half when the resource exists and is broadly right but the check didn't pass cleanly, and zero for anything unattempted.
| Task | Domain | Points | Your score |
|---|---|---|---|
| OD1 · libvirt VM, import + autostart | Operations Deployment | 9 | |
| OD2 · Signed repo + versionlock | Operations Deployment | 8 | |
| OD3 · Recover from a bad fstab boot | Operations Deployment | 8 | |
| N1 · VLAN subinterface + persistence | Networking | 5 | |
| N2 · Policy routing across two uplinks | Networking | 4 | |
| N3 · Bridge device for lab guests | Networking | 4 | |
| N4 · IPv6 address + proven route | Networking | 4 | |
| N5 · HAProxy round-robin + health check | Networking | 4 | |
| N6 · Split-horizon DNS via resolved | Networking | 4 | |
| S1 · LVM snapshot + merge rollback | Storage | 4 | |
| S2 · mdadm RAID1 + simulated failure | Storage | 4 | |
| S3 · Per-user disk quota | Storage | 4 | |
| S4 · NBD attach + persistent unit | Storage | 4 | |
| S5 · Inode exhaustion diagnosis | Storage | 4 | |
E1 · git bisect to a root cause | Essential Commands | 7 | |
| E2 · logrotate + service reopen | Essential Commands | 7 | |
| E3 · TLS chain diagnosis + fix | Essential Commands | 6 | |
| U1 · System-wide + per-account profile | Users & Groups | 4 | |
| U2 · SSSD/LDAP + NSS order | Users & Groups | 6 | |
| Total | All five domains | 100 |
Computing your result. The points total 100, so your raw score is your percentage, against the published 67% pass mark. Then do the more useful arithmetic: total each domain separately, and this time also compare against your Set 1 and Set 2 domain scores if you sat them. A domain that improved across all three sittings is a real signal; a domain that's flat or worse despite this paper giving it more repetitions is the one that needs a different kind of practice, not just more of the same kind.
| Domain | Available | Yours | If you scored under two-thirds, go here |
|---|---|---|---|
| Operations Deployment | 25 | LFCS — the exam, then the practice task bank for repo management and service-recovery drills. | |
| Networking | 25 | LFCS — the exam and Networking, then re-drill the practice bank against real ip/nft/resolvectl commands. | |
| Storage | 20 | LVM & Linux Storage Tools, then the practice bank. | |
| Essential Commands | 20 | LFCS — the exam, then the practice bank for the Git and certificate-chain shapes specifically. | |
| Users & Groups | 10 | LFCS — the exam, then a fast re-read of man 5 nsswitch.conf on your own machine. |
What your score means
☺ Like you're 10: This is the last of the three papers, so the question now isn't "which set do I sit next" — it's "am I actually ready to book."
Treat the number as a diagnostic, never a prediction, same as the earlier two papers. But because this is the third and final sitting in this course, read the band below as a booking signal, not just a study signal.
| Score | Read it as | Next move |
|---|---|---|
| 85–100 | Comfortably ready, with margin for an unlucky task draw, and this held up even with more Networking/Storage repetitions than the other two papers gave you. | Stop grinding tasks. Skim the exam logistics one more time, then book it. |
| 67–84 | A pass on this paper, but check whether your weak domain is the same one that was weak on Set 1 or Set 2 — a repeat offender needs a different kind of practice, not another timed sitting. | Re-drill only your weakest domain from the table above, then reattempt a mixed set of tasks from the practice bank before booking. |
| 50–66 | Close, usually concentrated in one or two domains rather than spread evenly. | A focused week on your two weakest domains, then re-sit this exact paper cold in seven to ten days. |
| Under 50 | Genuine gaps that a third timed paper alone won't close. | Back to LFCS — the exam and the study plan for your lowest-scoring domains, then return to Set 1 with fresh eyes before attempting this one again. |
One check worth running across all three sittings, not just this one: did your Networking and Storage scores actually improve from Set 1 through Set 2 to this paper? If they did, the extra repetitions this paper deliberately front-loaded did their job. If they didn't, the gap probably isn't repetition at all — it's a specific concept (policy routing, snapshot merge semantics, a health-check flag) worth reading about directly rather than drilling blind again.
Pip: Six networking tasks and I still lost points on N2. Not the syntax — I forgot the second route inside the table, just added the rule and stopped.
Benny: Storage went cleaner for me this time. Twenty out of twenty. Five small jobs beat one big one — I never lost my place.
Gizmo: See, Pip? Networking's not worth the trouble. Just memorize one static route command and call it a day. 😈
Timmy: One static route command doesn't cover a bridge, a bond, a VLAN, IPv6 and a load balancer, Gizmo. Pip's fix isn't "give up," it's "add the second route to the same table before moving on."
Foxy: Wait — Pip, did you actually run ip route get ... from ... to prove it, or did ip rule show just look right?
Pip: ...I trusted ip rule show. That's the mistake, isn't it. A rule pointing at an empty table proves nothing.
Timmy: That's the whole trilogy in one sentence. Three papers, same lesson every time: prove the outcome, not the configuration. Sit the practice bank on N2's exact shape, then you're done with this course's mock exams.
1. Why does policy-based routing (an ip rule plus a second table) solve a problem a single default route cannot, and what's the tell-tale symptom when it's missing on a multi-homed host? 2. What's the actual difference between lvconvert --merge on a snapshot and simply deleting the snapshot, and why might the merge not finish immediately on a mounted origin? 3. df -h and df -i can disagree completely about a "disk full" complaint. What does each one actually measure? 4. In an HAProxy backend, what does the check keyword plus option httpchk actually give you that a plain list of servers doesn't? 5. Your nsswitch.conf reads passwd: files sss. Why does that order matter for surviving an LDAP outage? 6. A logrotate postrotate script renamed beacon-api.log, but disk usage didn't drop. What's actually still holding the space? 7. Name the one thing Set 3 changed relative to Set 1 and Set 2, and the one thing it deliberately kept identical.
Check your answers
- A normal routing table has exactly one default route; a packet that must exit through the same interface its source address belongs to needs a lookup keyed on source address, run before the ordinary destination-based lookup — that's what
ip rulesupplies. Missing it shows up as traffic leaving with a source address on one link's subnet but exiting through a different interface, which a remote firewall correctly treats as spoofed and silently drops. lvconvert --mergereplaces the origin's data with the snapshot's recorded copy-on-write changes — the opposite of simply deleting the snapshot, which discards those changes instead. Because the origin is normally still mounted, the merge is usually scheduled rather than completed on the spot, finishing only once the LV is deactivated and reactivated (or the host reboots).df -hmeasures free bytes;df -imeasures free inodes, a fixed count set atmkfstime independent of file size. A directory full of millions of tiny or empty files can exhaust inodes while barely touching byte usage, producing the exact sameENOSPCerror either way —df -iis what tells the two apart.- It makes HAProxy actively probe each backend and automatically pull a failed one out of rotation — without it, HAProxy keeps sending a share of every request to a dead server, and each of those requests simply times out for the client.
filesfirst means every lookup checks the local/etc/passwd//etc/groupbefore ever falling through to SSSD, so local accounts — including root — keep resolving with zero dependency on LDAP being reachable. Reversed, a slow or unreachable LDAP server could stall or fail logins for accounts that never needed LDAP at all.- The process that was writing to the original file still holds its old file descriptor open even after the rename — it keeps appending to a now-unlinked file, and the space isn't released until that descriptor closes. The
postrotatesignal tells the process to close and reopen its log by name, which is what actually lets the disk space free up. - Task count changed — Networking grew to six tasks and Storage to five, versus four apiece on Set 1 and Set 2, trading per-task point value for more independent repetitions in the two domains this paper targets. What stayed identical is the 100-point total and the official 25/25/20/20/10 domain weighting, so scores remain directly comparable, domain for domain, across all three sittings.
That's all three papers. If you've sat all of them, you now have three domain-by-domain scorecards instead of one — look at the trend, not any single number, and let a domain that's stayed weak across all three sittings decide what you study before you book anything. For the deeper cross-reference against a platform engineer's actual daily use of this material, see Platform Engineering's own LFCS page, and for booking mechanics, retake terms and the retired LFCE, see LFCS — the exam and the official LFCS certification page.