LFCS Mock Exam · Set 1
This is a full, sealed sitting: twenty performance-based tasks, one unbroken two-hour block, a hundred points split exactly 25 / 25 / 20 / 20 / 10 across Operations Deployment, Networking, Storage, Essential Commands and Users and Groups — the same five domains and weights the LFCS blueprint lays out in full. Unlike a multiple-choice paper, there is nothing to peek at until you've actually left a real machine in the right state: every task below carries a plain-language brief, a done when check you run yourself against your own terminal, and a full worked solution folded underneath that stays closed until the sitting is over — or you've flagged the task and moved on. The order is deliberately shuffled so no two neighbouring tasks share a domain, because the real exam gives you no such courtesy either. Build a throwaway box the night before, read the pre-flight and pacing sections below, then start one timer and don't stop it until the twentieth task is behind you.
Most of the tests on this course hand you four choices and ask you to pick one. This one hands you a screwdriver. Imagine twenty small jobs scattered around a real house — fix the wobbly shelf, find out why the tap won't turn off, set the thermostat to come on by itself at night — and someone standing behind you with a stopwatch who checks your work by actually testing it, not by asking you to describe it. You don't get credit for explaining how you'd fix the tap. You get credit when the tap is fixed. That's the entire difference between a performance exam and a quiz, and it's why practising on a real keyboard, against a real clock, matters here far more than memorising facts ever will.
How this paper is weighted
☺ Like you're 10: The twenty jobs aren't shared out evenly — the two biggest topics get five jobs each, and the smallest gets only two.
The LFCS's five official domain weights — 25, 25, 20, 20, 10 — sum to exactly 100%, and against twenty tasks worth five points apiece they divide with no rounding at all: five tasks each for Operations Deployment and Networking, four each for Storage and Essential Commands, two for Users and Groups. That tidiness is a coincidence of picking twenty tasks rather than some other paper size — it happens to sit inside the real exam's own published 17–20 task range — not a property of the exam itself. What it does mean is that your score on this paper, broken out by domain, is a faithful little mirror of where the real 100% actually goes.
| Domain | Official weight | Tasks here | Points |
|---|---|---|---|
| ⚙️ Operations Deployment | 25% | 5 | 25 |
| 📡 Networking | 25% | 5 | 25 |
| 💾 Storage | 20% | 4 | 20 |
| ⌨️ Essential Commands | 20% | 4 | 20 |
| 👥 Users and Groups | 10% | 2 | 10 |
Score your domains, not just your total. Because every task on this paper is worth an even five points, a 70 built from "solid everywhere except Networking" and a 70 built from "strong everywhere, weak on Users and Groups" look identical as a raw number — but Networking carries two and a half times the weight of Users and Groups on the real exam. Fix in weight order, not in the order you happened to notice the mistakes.
Pre-flight — build your practice box the night before
☺ Like you're 10: Get every tool installed and every light green the day before, so when the clock starts you're already holding the screwdriver, not still looking for it.
A single throwaway VM is enough — a cloud instance, a local multipass/vagrant box, or spare hardware — but two things are worth confirming before the timer starts. First, task nineteen needs SELinux, which means a RHEL-family box (AlmaLinux, Rocky, Fedora); a Debian- or Ubuntu-family host ships AppArmor instead, and the two are not interchangeable — check getenforce now, not mid-sitting. Second, install everything the paper touches so no task is lost to a missing package: lvm2, nftables, chrony, autofs and nfs-utils, podman, git, openssl, and — if your distribution splits them out — policycoreutils and setools for the SELinux task. A second unpartitioned disk (/dev/sdb in the tasks below) is required for the LVM task specifically; most cloud providers and hypervisors let you attach one for free on a throwaway box.
# --- the night before -----------------------------------------------
sudo dnf install -y lvm2 nftables chrony autofs nfs-utils podman git openssl policycoreutils setools-console
# (Debian/Ubuntu family, skip the last two — this host will not have SELinux)
# sudo apt install -y lvm2 nftables chrony autofs nfs-common podman git openssl
lsblk # confirm the spare disk for the LVM task is visible and empty
getenforce # Enforcing/Permissive = SELinux task is workable here; Disabled or
# command-not-found = attach an AlmaLinux/Rocky box for that one task
# --- ninety seconds before the clock starts --------------------------
whoami ; hostname ; ip -br addr
sudo -i whoami # confirm root escalation works BEFORE you need it under pressure
date # note the wall-clock start time somewhere you'll actually see itBefore you sit the whole paper, do a dry run of the protocol only. Set a 15-minute timer and work Q3 (the LVM task, the longest pole on this paper) and Q13 (nftables) back to back, with a forced two-minute detour into an unrelated shell session in between. Notice how long it actually takes you to re-find your footing — which volume group, which chain, which file you were mid-edit on. That re-orientation tax, paid twenty times over during the real sitting, is exactly what this paper is training out of you.
Sit it like the real thing
☺ Like you're 10: Root access, a real terminal, someone watching, and the only book allowed on the desk is the one built into the computer itself.
Per the real LFCS's published Important Instructions, you work as root via sudo -i, some tasks may direct you to SSH to a designated host, and terminal copy/paste is Ctrl+Shift+C / Ctrl+Shift+V rather than the usual shortcut — muscle-memory details that cost real seconds the first time you hit them cold. Allowed resources are narrow: man pages and whatever documentation ships with the distribution itself, accessed from inside the terminal. No browser, no notes, no this site, no AI assistant. Sit this paper the same way — close every tab except a local man page — and if a command's exact flags won't come, that friction is data about what to drill next, not a reason to open a search engine.
This is an independent, unofficial study resource, not affiliated with the CNCF or The Linux Foundation. The 17–20 task count, the 2-hour duration and the 67% pass mark used to calibrate this paper come from the Linux Foundation's own published Important Instructions for the LFCS as of writing — but price, retake terms, eligibility windows, proctoring rules and even the domain weights are all figures the Foundation revises without much notice. Confirm every one of them on the official LFCS page and the candidate handbook before you register or pay for anything — this page is a study aid, not a substitute for reading them.
Your time budget
☺ Like you're 10: Give every job a small slice of the two hours. When a job runs past its slice, leave a note on it and move to the next one — you can come back if there's time left over.
Twenty tasks in 120 minutes averages six minutes each — a little under that once you carve out reading and a final check — but the tasks are not equal and the budget shouldn't be either. Allocate 5 minutes to read every brief before you touch a keyboard, 109 minutes of task work, and 6 minutes at the end to re-run every done-when check you weren't sure of. That last block earns more points per minute than anything else on the paper — half-finished work you never verified is where points quietly die.
| Block | Tasks | Budget | Running total | Note |
|---|---|---|---|---|
| Read every brief | — | 5 min | 5 | Mark each task cheap / medium / expensive before you touch a terminal. |
| Block 1 | Q1–5 | 4+6+9+5+5 = 29 min | 34 | Q3 is the long pole on the whole paper. Give it its full nine, not more. |
| Block 2 | Q6–10 | 4+6+6+4+5 = 25 min | 59 | Q6 and Q9 are the cheapest pair here — bank them fast. |
| Block 3 | Q11–15 | 5+6+7+5+5 = 28 min | 87 | Q13's nftables rules are fiddly to type from memory — budget the full seven. |
| Block 4 | Q16–20 | 4+5+7+6+5 = 27 min | 114 | The tail holds 25 points. Candidates who never reach it are the ones who fail. |
| Verify & sweep flags | — | 6 min | 120 | Re-run every done-when you flagged. Finish the nearly-done before starting anything new. |
The flag-and-move rule. When a task passes its budgeted minutes without a passing done-when check, stop. Write one line about where you stalled, leave whatever you applied in place — partial credit is real on the actual exam's rubric — and go to the next task. The instinct to finish what you started is the most expensive habit in a timed performance exam: it trades a guaranteed five points elsewhere for a possible five points here. Benny's version: you are not paid to finish tasks, you are paid to bank points.
The paper — 20 tasks across four blocks
☺ Like you're 10: Twenty real jobs, in the order you should meet them. Read the brief, do the job on your own machine, check it against the done-when line, and only then open the folded solution.
Each task names its domain in parentheses so you can total your score by domain afterward — the real exam won't label them that way, so once you've sat this cold, consider a second pass with the domain names covered to see how many you can still place from the task shape alone. Work every task on your own practice box before opening its solution; opening one early converts a diagnostic into a reading exercise and teaches you nothing about your own pacing.
Block 1 — Q1–5
Q1 (Operations Deployment · 5 pts, ~4 min). Enable IPv4 forwarding on this host, and make the setting survive a reboot without you needing to reboot to prove it.
Done when: sysctl net.ipv4.ip_forward reports 1, and the value is written to a file under /etc/sysctl.d/ rather than set only in the running kernel.
Worked solution — Q1
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-forwarding.conf sudo sysctl --system # reloads every persistent file, no reboot needed sysctl net.ipv4.ip_forward # net.ipv4.ip_forward = 1
A bare sysctl -w sets the live value but is gone at the next boot — the file under /etc/sysctl.d/ is what makes it "persistent," which is the exact word the domain competency uses.
Q2 (Networking · 5 pts, ~6 min). On interface eth1, set a static IPv4 address 192.168.56.50/24, gateway 192.168.56.1, DNS 192.168.56.2 — persistent across reboots — then add a hostname resolution entry so beacon.internal resolves to 192.168.56.10.
Done when: ip -br addr show eth1 shows the new address after reapplying the connection (not just the running interface), and getent hosts beacon.internal returns 192.168.56.10.
Worked solution — Q2
nmcli con show # find the connection name bound to eth1 nmcli con mod eth1 ipv4.method manual nmcli con mod eth1 ipv4.addresses 192.168.56.50/24 nmcli con mod eth1 ipv4.gateway 192.168.56.1 nmcli con mod eth1 ipv4.dns 192.168.56.2 nmcli con up eth1 # reapplies from the saved profile — this is what persists it echo '192.168.56.10 beacon.internal' | sudo tee -a /etc/hosts getent hosts beacon.internal ip -br addr show eth1 ; ip route
On a netplan-based host the equivalent lives in a YAML file under /etc/netplan/ with dhcp4: false and an explicit addresses/routes block, applied with netplan apply — same result, different tool, which is exactly why the real exam is written against the outcome rather than one vendor's command.
Q3 (Storage · 5 pts, ~9 min — the long pole on this paper). Using the spare disk /dev/sdb, build an LVM-backed 2G filesystem mounted persistently at /data, then grow it online to 3G without unmounting it.
Done when: /data is mounted (survives mount -a from a fresh /etc/fstab read via UUID), and df -hT /data shows roughly 3G available after the extend.
Worked solution — Q3
sudo pvcreate /dev/sdb sudo vgcreate vg_data /dev/sdb sudo lvcreate -L 2G -n lv_data vg_data sudo mkfs.xfs /dev/vg_data/lv_data sudo mkdir -p /data UUID=$(blkid -s UUID -o value /dev/vg_data/lv_data) echo "UUID=$UUID /data xfs defaults 0 2" | sudo tee -a /etc/fstab sudo mount -a df -hT /data # ~2G # grow it ONLINE — no unmount, no downtime sudo lvextend -r -L +1G /dev/vg_data/lv_data # -r resizes the filesystem in the same step df -hT /data # ~3G
The -r flag on lvextend is the whole trick: it calls xfs_growfs (or resize2fs for ext4) for you in the same command, which is why nothing needed to be unmounted. Forgetting -r is the single most common way to lose points here — the logical volume grows, but the filesystem sitting on top of it doesn't, and df still reports the old size.
Q4 (Essential Commands · 5 pts, ~5 min). In a fresh Git repository, create a file, commit it, then create a branch that edits it one way while main edits it another — and resolve the resulting merge conflict cleanly.
Done when: git log --oneline --graph shows a merge commit, app.yaml contains no leftover <<<<<<< markers, and git status is clean.
Worked solution — Q4
git init hab-config && cd hab-config echo "replicas: 2" > app.yaml git add app.yaml && git commit -m "initial state" git switch -c bump-replicas sed -i 's/replicas: 2/replicas: 4/' app.yaml git commit -am "bump replicas to 4" git switch main sed -i 's/replicas: 2/replicas: 3/' app.yaml git commit -am "bump replicas to 3" git merge bump-replicas # CONFLICT: both sides touched the same line # edit app.yaml by hand — keep 'replicas: 4', delete the <<<<<<< ======= >>>>>>> markers git add app.yaml git commit # completes the merge with the default merge message git log --oneline --graph
"Basic Git operations" on this exam means exactly this shape — init, branch, conflicting commits, manual resolution, completed merge — not memorised trivia about Git internals.
Q5 (Users and Groups · 5 pts, ~5 min). Create a group crew and a user dot (member of crew, home directory, bash shell), set a hard limit of 4096 open files for dot, and set the environment variable EDITOR=vim system-wide for every user.
Done when: id dot shows group crew, a fresh login shell for dot reports ulimit -n as 4096, and echo $EDITOR in a fresh shell for any user prints vim.
Worked solution — Q5
sudo groupadd crew sudo useradd -m -G crew -s /bin/bash dot sudo passwd dot echo 'dot hard nofile 4096' | sudo tee /etc/security/limits.d/90-dot.conf echo 'dot soft nofile 4096' | sudo tee -a /etc/security/limits.d/90-dot.conf echo 'export EDITOR=vim' | sudo tee /etc/profile.d/editor.sh sudo chmod +x /etc/profile.d/editor.sh id dot ; groups dot su - dot -c 'ulimit -n ; echo $EDITOR'
Two different mechanisms for two different scopes: limits.d is per-user resource limits (PAM-enforced at login), and profile.d is a system-wide environment profile sourced by every login shell — mixing them up is a common way to lose half credit here even when both files exist.
Block 2 — Q6–10
Q6 (Networking · 5 pts, ~4 min). Point this host's time synchronisation at an internal NTP server 10.0.0.5, and confirm the system clock is actually synchronised — not just that the service is running.
Done when: timedatectl status reports System clock synchronized: yes, and chronyc sources shows 10.0.0.5 as a selected source.
Worked solution — Q6
sudo sed -i '/^pool\|^server/d' /etc/chrony.conf echo 'server 10.0.0.5 iburst' | sudo tee -a /etc/chrony.conf sudo systemctl enable --now chronyd # some Debian releases name the unit 'chrony' — check with 'systemctl list-unit-files | grep chron' chronyc sources -v timedatectl status # look specifically for 'System clock synchronized: yes'
A running chronyd with no reachable server still shows the service as active — that's why the done-when check is on timedatectl's synchronised state and chronyc sources, not on systemctl is-active alone. Clock skew silently breaks TLS handshakes and any cluster health check that compares timestamps.
Q7 (Operations Deployment · 5 pts, ~6 min). The unit docking-agent.service is failing to start. Diagnose why and fix it — the vendor unit file itself must stay untouched.
Done when: systemctl is-active docking-agent.service reports active, and it stays active on a second systemctl restart.
Worked solution — Q7
systemctl status docking-agent.service # Active: failed — read the last log lines here first journalctl -u docking-agent.service -b --no-pager | tail -20 systemctl cat docking-agent.service # the EFFECTIVE unit — check ExecStart's path # discovers: ExecStart=/usr/local/bin/docking-agent (no such file — the binary actually lives at # /opt/docking/bin/docking-agent) sudo systemctl edit docking-agent.service # opens an editor for a DROP-IN, vendor file untouched # [Service] # ExecStart= # ExecStart=/opt/docking/bin/docking-agent --config /etc/docking/config.yaml sudo systemctl daemon-reload sudo systemctl restart docking-agent.service systemctl is-active docking-agent.service # active journalctl -u docking-agent.service -f # watch for a moment — confirm no crash-loop restart
systemctl edit writes a drop-in snippet under /etc/systemd/system/<unit>.d/ rather than touching the packaged file — the correct way to override a unit, and the first thing graders check when "don't touch the vendor file" is implied by the task shape.
Q8 (Essential Commands · 5 pts, ~6 min). Write a brand-new unit beacon.service from scratch: it runs /usr/local/bin/beacon as user dot, restarts on failure after 5 seconds, and is capped at 256M memory and 25% CPU.
Done when: the unit is enabled and active, and systemctl show beacon.service -p MemoryMax reports 268435456 (256M in bytes).
Worked solution — Q8
# /etc/systemd/system/beacon.service [Unit] Description=Beacon telemetry agent After=network-online.target Wants=network-online.target [Service] Type=simple User=dot ExecStart=/usr/local/bin/beacon --config /etc/beacon/config.yaml Restart=on-failure RestartSec=5s MemoryMax=256M CPUQuota=25% [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable --now beacon.service systemctl show beacon.service -p MemoryMax -p CPUQuotaPerSecUSec systemd-cgtop -n 1 | grep beacon
MemoryMax and CPUQuota aren't systemd-only inventions — they're cgroup v2 controls with a friendlier spelling, the same controls a kubelet writes on your behalf whenever a Pod declares resources.limits. "Determine application and service specific constraints" is this competency, spelled out in a unit file instead of YAML.
Q9 (Storage · 5 pts, ~4 min). Add 1G of swap via a swapfile, persistent across reboots, and set vm.swappiness to 10, also persistent.
Done when: swapon --show lists the swapfile with roughly 1G, and cat /proc/sys/vm/swappiness reports 10.
Worked solution — Q9
sudo fallocate -l 1G /swapfile || sudo dd if=/dev/zero of=/swapfile bs=1M count=1024 sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-swappiness.conf sudo sysctl --system swapon --show ; free -h cat /proc/sys/vm/swappiness
fallocate is faster but some filesystems (older ext4, certain network-backed disks) reject a fallocated swapfile at mkswap time — the dd fallback in the command above is the exam-safe version that always works, just slower.
Q10 (Networking · 5 pts, ~5 min). Harden this host's OpenSSH server — key-only authentication, no root password login, listening on port 2222 — and add a client-side shortcut so ssh relay connects to 192.168.56.10 on that port as user dot.
Done when: sshd -t passes with no errors after the edit, the service is listening on 2222 (ss -ltnp), and ssh -G relay resolves the right host, port and user.
Worked solution — Q10
sudo sed -i \
-e 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' \
-e 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' \
-e 's/^#\?Port.*/Port 2222/' \
/etc/ssh/sshd_config
sudo sshd -t # syntax check FIRST — never restart sshd blind
sudo systemctl restart sshd
ss -ltnp | grep 2222
cat >> ~/.ssh/config <<'EOF'
Host relay
HostName 192.168.56.10
Port 2222
User dot
IdentityFile ~/.ssh/id_ed25519
EOF
ssh -G relay | grep -E '^(hostname|port|user) ' # resolves the config with no live connection neededsshd -t before restarting is not optional discipline here — it's the difference between a hardened server and a server you just locked yourself out of, and on a remote exam desktop there is no console to recover through.
"I got into a nice rhythm on the networking tasks and then Q7 dropped me straight into a broken systemd unit with zero warning. My first instinct was annoyance — I was mid-flow. Then I noticed that's just Tuesday: nobody at work schedules my incidents to arrive one domain at a time either. The paper isn't being unfair. My practice sessions, one topic at a time for an hour, were the unrealistic part."
Block 3 — Q11–15
Q11 (Operations Deployment · 5 pts, ~5 min). Schedule /usr/local/bin/backup.sh to run nightly at 02:00, two ways: as a user crontab entry, and as a systemd timer.
Done when: crontab -l shows the 0 2 * * * line, and systemctl list-timers backup.timer shows it scheduled and enabled.
Worked solution — Q11
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/backup.sh") | crontab - crontab -l sudo tee /etc/systemd/system/backup.service >/dev/null <<'EOF' [Unit] Description=Nightly backup [Service] Type=oneshot ExecStart=/usr/local/bin/backup.sh EOF sudo tee /etc/systemd/system/backup.timer >/dev/null <<'EOF' [Unit] Description=Run backup.service nightly at 02:00 [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=timers.target EOF sudo systemctl daemon-reload sudo systemctl enable --now backup.timer systemctl list-timers backup.timer
Persistent=true is worth knowing by heart: it makes a missed run (host was off at 02:00) fire as soon as the system is back, which cron cannot do on its own — a real reason to prefer timers over cron for anything that actually matters.
Q12 (Storage · 5 pts, ~6 min). Mount the NFS export docking-bay:/exports/logs on demand at /mnt/logs using autofs — not a static /etc/fstab entry.
Done when: the mount does not appear in mount output until something accesses /mnt/logs/data, at which point it appears automatically.
Worked solution — Q12
echo '/mnt/logs /etc/auto.logs' | sudo tee -a /etc/auto.master echo 'data -rw,soft docking-bay:/exports/logs' | sudo tee /etc/auto.logs sudo systemctl enable --now autofs mount | grep /mnt/logs # nothing yet — not mounted until touched ls /mnt/logs/data # this access TRIGGERS the automount mount | grep /mnt/logs # now it's there
The indirect map key (data) becomes the subdirectory under the mount point named in auto.master — so the export lands at /mnt/logs/data, not /mnt/logs itself. That off-by-one-directory detail is the most common way this task loses points even when the config is otherwise correct.
Q13 (Networking · 5 pts, ~7 min). Using nftables, default-deny inbound traffic on this host except SSH (2222) and HTTPS (443); separately, forward external port 8443 to an internal service at 10.0.0.20:443.
Done when: nft list ruleset shows an input chain with policy drop and the two accept rules, plus a prerouting DNAT rule for 8443.
Worked solution — Q13
sudo nft add table inet filter
sudo nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
sudo nft add rule inet filter input ct state established,related accept
sudo nft add rule inet filter input iif lo accept
sudo nft add rule inet filter input tcp dport { 2222, 443 } accept
sudo nft add table ip nat
sudo nft add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
sudo nft add rule ip nat prerouting tcp dport 8443 dnat to 10.0.0.20:443
sudo nft list ruleset | sudo tee /etc/nftables.conf
sudo systemctl enable --now nftablesOrder matters twice here: the established,related accept rule has to come before the port allowlist or your own return traffic gets dropped by the default policy, and the lo accept has to exist at all or you can lock yourself out of loopback-only tooling. Both are classic self-inflicted zeros on this exact task shape.
Q14 (Essential Commands · 5 pts, ~5 min). /var reports 100% full, but du on /var doesn't add up to anywhere near that. Find what's actually consuming the space and reclaim it.
Done when: df -hT /var shows meaningfully reclaimed space, and you can name the process and file that caused it.
Worked solution — Q14
df -hT /var du -xhd1 /var | sort -h | tail # doesn't explain the gap — the classic symptom sudo lsof +L1 | grep -i deleted | head # open files with 0 links: deleted, but still held open # e.g.: java 1234 app 40w REG 253,1 8.9G (deleted) /var/log/app/heap.log sudo systemctl restart app.service # releasing the file descriptor is what actually frees the space df -hT /var # space returns immediately, without ever touching /var by hand
Deleting a file only unlinks its directory entry — a process still holding it open keeps the disk blocks allocated until that file descriptor closes, which is exactly why rm-then-df can look identical before and after. lsof +L1 (or lsof | grep deleted) is the tool that finds it; a reboot would also "fix" it, which is why a reboot is never actually the fix on an exam that's grading your diagnosis, not just your outcome.
Q15 (Operations Deployment · 5 pts, ~5 min). Add a new package repository, install a package from it with GPG verification enabled, and pin it so it will not be upgraded by a routine system update.
Done when: the package is installed, gpgcheck/signature verification was enabled for the install (not disabled to work around a failure), and the package shows as held/locked against upgrade.
Worked solution — Q15
# RHEL-family sudo rpm --import https://repo.example.com/RPM-GPG-KEY sudo tee /etc/yum.repos.d/platform.repo >/dev/null <<'EOF' [platform] name=Platform tools baseurl=https://repo.example.com/rhel/$releasever/$basearch gpgcheck=1 gpgkey=https://repo.example.com/RPM-GPG-KEY EOF sudo dnf install -y missioncli sudo dnf versionlock add missioncli # pin it # Debian-family curl -fsSL https://repo.example.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/platform.gpg echo 'deb [signed-by=/usr/share/keyrings/platform.gpg] https://repo.example.com/deb stable main' \ | sudo tee /etc/apt/sources.list.d/platform.list sudo apt update && sudo apt install -y missioncli sudo apt-mark hold missioncli # pin it
Since the May 2023 revision the exam no longer names a distribution, so both tool families are fair game — the graded outcome is "a signed package, from a repo you added, that won't silently drift on the next update," regardless of which package manager gets you there.
Block 4 — Q16–20
Q16 (Users and Groups · 5 pts, ~4 min). /srv/telemetry is owned root:crew at mode 750. Grant user dot read and execute access without adding them to the crew group, and make sure any new file created inside inherits that same access for dot.
Done when: getfacl /srv/telemetry shows a named-user entry and a default ACL for dot, and dot can list the directory while groups dot still shows no crew membership.
Worked solution — Q16
sudo setfacl -m u:dot:rx /srv/telemetry sudo setfacl -d -m u:dot:rx /srv/telemetry # DEFAULT ACL — inherited by anything created inside getfacl /srv/telemetry su - dot -c 'ls /srv/telemetry' # works, with no group membership at all groups dot # confirms dot is NOT in crew
The -d flag is the part this task is actually testing — an ACL without it only covers files that already exist at the moment you ran the command; anything created afterward falls back to the parent directory's default permissions, quietly excluding dot again.
Q17 (Essential Commands · 5 pts, ~5 min). Generate a self-signed TLS certificate and key for host app.internal, install it into an existing nginx site config, and verify the certificate's subject and expiry from the command line.
Done when: nginx -t passes after the edit, and openssl x509 -noout -subject -enddate against the installed cert shows CN=app.internal and a date roughly 365 days out.
Worked solution — Q17
openssl req -x509 -nodes -newkey rsa:2048 \ -keyout /etc/ssl/private/app.internal.key \ -out /etc/ssl/certs/app.internal.crt \ -days 365 -subj "/CN=app.internal" sudo chmod 600 /etc/ssl/private/app.internal.key sudo sed -i \ -e 's#ssl_certificate .*#ssl_certificate /etc/ssl/certs/app.internal.crt;#' \ -e 's#ssl_certificate_key .*#ssl_certificate_key /etc/ssl/private/app.internal.key;#' \ /etc/nginx/sites-available/app.internal.conf sudo nginx -t && sudo systemctl reload nginx openssl x509 -in /etc/ssl/certs/app.internal.crt -noout -subject -enddate
"Work with SSL certificates" on this exam means exactly this — generate, install, verify from the shell — not a theory question about the TLS handshake. nginx -t before reload catches a malformed path or a permissions problem before it takes the site down.
Q18 (Networking · 5 pts, ~7 min). Add a persistent static route so traffic to 10.20.0.0/16 goes via gateway 192.168.56.254 through eth1; separately, bond interfaces eth2 and eth3 into bond0 in active-backup mode.
Done when: ip route show 10.20.0.0/16 shows the route via the correct gateway, and cat /proc/net/bonding/bond0 shows both slave interfaces with bonding mode active-backup.
Worked solution — Q18
sudo nmcli con mod eth1 +ipv4.routes "10.20.0.0/16 192.168.56.254" sudo nmcli con up eth1 ip route show 10.20.0.0/16 sudo nmcli con add type bond ifname bond0 bond.options "mode=active-backup,miimon=100" sudo nmcli con add type ethernet ifname eth2 master bond0 sudo nmcli con add type ethernet ifname eth3 master bond0 sudo nmcli con up bond0 cat /proc/net/bonding/bond0 | grep -E 'Bonding Mode|Slave Interface'
The +ipv4.routes syntax (note the leading +) appends to the connection's route list rather than replacing it — dropping the + silently wipes out the default route along with everything else, which is a fast way to lose remote access to the box you're working on.
Q19 (Operations Deployment · 5 pts, ~6 min — requires SELinux). Run a rootless podman container that bind-mounts /srv/telemetry read-only, with a 256M memory limit, under a correct SELinux label — then prove the container cannot write to that mount.
Done when: getenforce shows Enforcing, the container starts and stays up, and an attempted write inside the container fails.
Worked solution — Q19
sudo semanage fcontext -a -t container_file_t "/srv/telemetry(/.*)?" sudo restorecon -Rv /srv/telemetry podman run -d --name relay \ --memory=256m \ -v /srv/telemetry:/data:ro,Z \ nginx:alpine podman exec relay sh -c 'touch /data/test' ; echo $? # non-zero — read-only mount blocks it outright getenforce # Enforcing sudo ausearch -m avc -ts recent | tail # confirm whether policy itself also denied anything
The :Z suffix on the volume mount relabels the host path with a private, container-specific SELinux context — without it, a correctly Enforcing host can still refuse the mount entirely with a permission denial that has nothing to do with the read-only flag. Both the mount option and the label matter, and the exam rewards being able to tell which one caused a given denial.
Q20 (Storage · 5 pts, ~5 min). After an unclean shutdown, the /data filesystem has remounted itself read-only. Diagnose why, repair it, and confirm ongoing storage I/O is healthy afterward.
Done when: mount | grep /data shows rw, and a short iostat sample shows no sustained high %util or await.
Worked solution — Q20
dmesg | grep -iE 'ext4|xfs|error' | tail -20 # look for the kernel's own remount-ro event and why mount | grep /data # confirms 'ro' is currently in the mount options sudo umount /data sudo fsck -y /dev/vg_data/lv_data # -y auto-answers "repair it" to every prompt sudo mount /data mount | grep /data # now 'rw' iostat -xz 2 5 # watch %util and await across a few intervals — should be calm
A filesystem remounting itself read-only is usually the kernel protecting you from further corruption after it noticed something wrong — treating that as a permissions bug and trying to chmod or mount -o remount,rw your way past it without running fsck first is how a small problem becomes data loss.
Score yourself
☺ Like you're 10: Add up your points, turn it into a percentage against the real 67% bar, then look at which topic cost you the most — that second part is the part that actually helps.
Mark after a break, not immediately — grading your own work while still adrenalised produces generous nonsense. Award full points only when the done-when check actually passed on your machine, half when the resource exists and is broadly correct but the check didn't pass, and zero for anything unattempted. There is no credit for "I knew how to do that one" — especially not here, since every task's real test is whether the machine ended up in the right state, not whether you can describe the right state.
| # | Task | Domain | Points | Your score |
|---|---|---|---|---|
| 1 | Persistent kernel parameter | Ops Deployment | 5 | |
| 2 | Static IP + hostname resolution | Networking | 5 | |
| 3 | LVM build + online extend | Storage | 5 | |
| 4 | Git branch + merge conflict | Essential Commands | 5 | |
| 5 | User/group + resource limits + profile | Users & Groups | 5 | |
| 6 | chrony time sync | Networking | 5 | |
| 7 | systemd crash-loop fix | Ops Deployment | 5 | |
| 8 | Custom unit with cgroup limits | Essential Commands | 5 | |
| 9 | Swapfile + swappiness | Storage | 5 | |
| 10 | SSH hardening + client shortcut | Networking | 5 | |
| 11 | Cron + systemd timer | Ops Deployment | 5 | |
| 12 | autofs NFS automount | Storage | 5 | |
| 13 | nftables filter + DNAT | Networking | 5 | |
| 14 | Disk-space forensics | Essential Commands | 5 | |
| 15 | Repo + GPG + version lock | Ops Deployment | 5 | |
| 16 | ACLs, named user + default | Users & Groups | 5 | |
| 17 | Self-signed TLS certificate | Essential Commands | 5 | |
| 18 | Static route + bonding | Networking | 5 | |
| 19 | podman + SELinux MAC | Ops Deployment | 5 | |
| 20 | Filesystem repair + iostat | Storage | 5 | |
| Total | All five domains | 100 |
Computing your result. The points total exactly 100, so your raw score is your percentage, and the bar is 67%. That leaves 33 points spendable — you can drop six of the twenty tasks outright and still clear the bar with two to spare, which is worth internalising, because the candidates who fail rarely failed on knowledge. Then do the more useful arithmetic and total each domain separately.
| Domain | Available | Yours | If under two-thirds, go here |
|---|---|---|---|
| ⚙️ Operations Deployment | 25 | LFCS — the exam, then the practice bank | |
| 📡 Networking | 25 | Networking on the PE trio, then the practice bank | |
| 💾 Storage | 20 | Storage & State, then LVM & Linux Storage Tools | |
| ⌨️ Essential Commands | 20 | systemd & journald, then the practice bank | |
| 👥 Users and Groups | 10 | Linux Fundamentals for Platform Engineers |
Sort your misses into three piles, because each one needs a different cure. A task you didn't recognise the shape of at all is a genuine knowledge gap — reread the linked domain page and redo the exact task cold in a couple of days. A task where you knew what to do but burned the clock hunting for a flag or a file path is a speed problem, and the cure is more timed reps with man open, not more reading. A task that broke in a way you couldn't diagnose — like Q14's phantom disk usage — is a triage gap: the evidence-gathering order (check the obvious thing, then the less obvious thing, in a fixed sequence) is what needs drilling, not the specific command. And count how many tasks you left completely untouched — more than three, and the real problem is pacing, not any of the above.
Where each domain is taught
☺ Like you're 10: Every task above traces back to one of a handful of pages. Go back to the page, not to a search engine.
Nothing in this paper tests anything that isn't covered somewhere on this course. Where a domain is genuinely LFCS-specific, the deepest read lives right here on Golden Astronaut; where it's ground the sibling trio also walks — general Linux and networking fundamentals a platform engineer needs regardless of which exam is next — the deeper cross-reference lives on Platform Engineering's own LFCS page, which covers the same five domains from the other side of the trio.
Operations Deployment
Kernel parameters, process and service troubleshooting, job scheduling, package management, container engines and SELinux.
📡 · 25%Networking
Addressing and hostname resolution, time sync, OpenSSH, packet filtering and NAT, static routing, bonding.
💾 · 20%Storage
LVM top to bottom, filesystem creation and repair, swap, automounters and storage performance monitoring.
⌨️ · 20%Essential Commands
Git basics, writing and troubleshooting services, disk-space forensics, and working with SSL certificates.
👥 · 10%Users and Groups
Accounts, environment profiles, resource limits, ACLs, and where LDAP integration fits.
Beyond the domain pages, the wider revision kit still applies: flashcards for the vocabulary, the self-check quiz for mixed recall, and the LFCS study plan for how to sequence all of this against your remaining prep time. And because LFCS shares its performance-based format with CKA and with the CNPE, sitting this paper under real timing conditions rehearses a format you'll meet again on both.
Foxy: Sixty-eight out of a hundred! I passed! Barely, but I passed!
Benny: Which domain hid the other thirty-two, Foxy? Sixty-eight sounds fine until one topic swallowed most of it on its own.
Foxy: ...Networking. I got the static IP, but I completely blanked on the nftables DNAT rule and the bonding task.
Sol: Networking is worth... twenty-five points... same as Operations Deployment. Losing two tasks there... is losing a fifth of the whole exam.
Gizmo: Easy fix — just skip the read-the-whole-paper-first step next time. Dive straight into task one, save five whole minutes! 😈
Timmy: That's exactly backwards, Gizmo. Those five minutes are how Foxy would have spotted the nftables task early and given it its full seven minutes instead of stumbling into it cold with two minutes left.
Benny: And notice what didn't fail — the LVM task, the long pole on the whole paper. Foxy gave it the full nine minutes it was budgeted, and it passed clean.
Foxy: Fine, fine — nftables and bonding get a proper re-drill before Set 2. Slowly, apparently.
That's the full sitting. Reread whichever domain cost you the most, let it settle for a day or two, then move on to Set 2 — assembled the same way, weighted the same way, with an entirely different twenty tasks.
1. Name the five LFCS domains and their weights, in weight order. 2. Why does lvextend need the -r flag to actually solve a "disk full" complaint, and what happens if you forget it? 3. A file was deleted but disk space didn't return — what tool finds the process still holding it open, and why does deleting the file alone not free the space? 4. What's the difference in scope between an ACL set with plain setfacl -m and one set with setfacl -d -m? 5. Why must sshd -t run before restarting a freshly edited SSH config, especially on a remote exam desktop? 6. Name one thing this paper's numbers are calibrated against that the Linux Foundation actually publishes, and one thing you should still verify officially before booking.
Check your answers
- Operations Deployment 25%, Networking 25%, Storage 20%, Essential Commands 20%, Users and Groups 10%.
lvextend -rresizes the filesystem sitting on top of the logical volume in the same step (callingxfs_growfsorresize2fsfor you). Without-r, the logical volume itself grows but the filesystem doesn't, sodfstill reports the old, smaller size even thoughlvsshows the larger volume.lsof +L1(orlsof | grep deleted) lists open files with zero remaining links — files a process still has open even though they've been unlinked from any directory. Deleting a file only removes its directory entry; the disk blocks stay allocated until every process holding it open closes that file descriptor.- A plain
setfacl -monly affects files that already exist at the moment you run it. A-d(default) ACL is inherited by anything created inside that directory afterward — without it, new files silently fall back to the parent directory's normal permissions and exclude whoever the ACL was meant to cover. - To catch a syntax error or a bad path in the config before it takes effect — on a remote, proctored desktop there is no physical console to fall back to if a bad restart locks out the only way in.
- Published and used to calibrate this paper: the 17–20 task count, the 2-hour duration, and the 67% pass mark, all from the Linux Foundation's own Important Instructions. Still verify officially: price, retake terms, eligibility window, and the exact domain weights, all of which the Foundation can revise without much notice — check the official LFCS page before you book.