Linux+ XK0-006 command cheat sheet
The commands the five XK0-006 domains actually need, in weight order, with output from the same captures the guides use. One Debian-family host and one RPM-family host, because the exam may hand you either.
- Debian familyUbuntu 26.04 LTS - kernel 7.0.0-30-generic, systemd 259
- RPM familyAlmaLinux 10.2 - kernel 6.12.0-211.7.3.el10_2, systemd 257
- Shellzsh 5.9 login shell on both - bash 5.3.9 (Ubuntu) / 5.2.26 (Alma)
- Mandatory access controlAppArmor on Ubuntu, SELinux enforcing on Alma
- Firewallufw / nftables 1.1.6 - firewalld / nftables 1.1.5
- Commands33
Domain 1 - System Management (23%)
-
lsblk -o NAME,SIZE,TYPE,MOUNTPOINTSThe first storage command on any unfamiliar machine: disks, partitions, LVM and mount points as a tree, changing nothing.
bash Example session lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS | head -7; echo "--- one 50 GB disk, and every byte of it already in use"NAME SIZE TYPE MOUNTPOINTSfd0 4K disksda 50G disk├─sda1 1M part├─sda2 2G part /boot└─sda3 48G part └─ubuntu--vg-ubuntu--lv 48G lvm /--- one 50 GB disk, and every byte of it already in use -
losetup -fP --show <file>Attach a file as a real block device. `-f` picks the first free one and prints it; `-P` asks the kernel to scan it for partitions.
bash Example session sudo losetup -fP --show /var/tmp/xk0.img/dev/loop0 -
mkfs.ext4 -L <label> <device> && blkid <device>Make a filesystem and read back the UUID it generated. The UUID belongs to the filesystem, so reformatting changes it.
bash Example session sudo mkfs.ext4 -q -L XK0DATA /dev/loop0p1 && sudo blkid /dev/loop0p1/dev/loop0p1: LABEL="XK0DATA" UUID="0ad80a25-dfff-4792-97d7-063e30b88aa2" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="primary" PARTUUID="c4fa73b3-41ac-4c0a-bd27-6567cffca396" -
findmnt --verify --verboseCheck /etc/fstab BEFORE a reboot. It also warns when systemd is still using the old copy and needs `systemctl daemon-reload`.
bash Example session sudo findmnt --verify --verbose 2>&1 | tail -4 [ ] source /dev/loop0p1 exists [ ] FS type is ext4 [W] your fstab has been modified, but systemd still uses the old version; use 'systemctl daemon-reload' to reload -
systemd-analyze critical-chainWhat actually delayed the boot. `blame` sorts units by their own duration and lists things nothing waited for - this shows the chain.
bash Example session systemd-analyze critical-chain 2>/dev/null | head -8The time when unit became active or started is printed after the "@" character.The time the unit took to start is printed after the "+" character. graphical.target @4.987s└─multi-user.target @4.987s └─snapd.seeded.service @2.588s +2.398s └─basic.target @2.558s └─sockets.target @2.558s -
dpkg -S $(command -v <prog>) / rpm -qf $(command -v <prog>)Which package owns a running program. The single most useful packaging query, and it is spelled differently on each family.
bash Example session dpkg -S $(command -v systemctl) | cut -d: -f1; dpkg -l systemd | tail -1 | awk '{print $2, $3}'; apt-get -s install systemd 2>&1 | tail -1systemdsystemd 259.5-0ubuntu3.40 upgraded, 0 newly installed, 0 to remove and 54 not upgraded.
Domain 2 - Services and User Management (20%)
-
systemd-analyze verify <unit-file>Parse a unit before enabling it. Warnings about OTHER units on the machine are normal - read the exit status, not the volume.
bash Example session sudo systemd-analyze verify /etc/systemd/system/xk0svc.service; echo "verify exit $?"/usr/lib/systemd/system/xfs_scrub_all.service:26: Support for option CPUAccounting= has been removed and it is ignored/usr/lib/systemd/system/system-xfs_scrub.slice:15: Support for option CPUAccounting= has been removed and it is ignoredverify exit 0 -
systemctl show <unit> -p User -p Group -p ExecMainStatusThe effective values after every drop-in, in machine-readable form. `ExecMainStatus` is the exit code the process last returned.
bash Example session systemctl show xk0svc.service -p User -p Group -p WorkingDirectory -p ExecMainStatusExecMainStatus=0WorkingDirectory=/var/lib/xk0svcUser=xk0svcGroup=xk0svc -
journalctl -u <unit> -n 20 --no-pager -o catThe second command in every service failure. `systemctl status` says it failed; this says why, because `-o cat` prints what the process itself wrote.
bash Example session sudo journalctl -u xk0svc.service -n 6 --no-pager -o catStarting xk0svc.service - XK0-006 demonstration service.../bin/sh: 1: cannot create /var/lib/xk0svc/locked/whoami.txt: Permission denied/bin/sh: 1: cannot create /var/lib/xk0svc/locked/whoami.txt: Permission deniedxk0svc.service: Main process exited, code=exited, status=2/INVALIDARGUMENTxk0svc.service: Failed with result 'exit-code'.Failed to start xk0svc.service - XK0-006 demonstration service. -
useradd -D / grep UID_MIN /etc/login.defsWhat the next account will look like before you create it. Note `SHELL=/bin/sh` is the default - pass `-s /bin/bash` for a human.
bash Example session useradd -D; echo "--- the defaults, from /etc/default/useradd"GROUP=100GROUPS=HOME=/homeINACTIVE=-1EXPIRE=SHELL=/bin/shSKEL=/etc/skelUSRSKEL=/usr/etc/skelCREATE_MAIL_SPOOL=noLOG_INIT=yes--- the defaults, from /etc/default/useradd -
sudo -l -U <user>Audit what a user may run, as sudo will actually apply it, without becoming them. Includes anything inherited from a group.
bash Example session sudo -l -U xk0user 2>&1 | tail -3User xk0user may run the following commands on lplus-a01: (root) NOPASSWD: /usr/bin/systemctl status chrony.service -
visudo -c -f /etc/sudoers.d/<file>Validate a sudo rule before trusting it. It names the file, line and column - and a broken file makes every sudo on the machine noisy.
bash Example session printf 'xk0user ALL=(root) NOPASSWD /usr/bin/id\n' | sudo tee /etc/sudoers.d/xk0broken > /dev/null && sudo visudo -c -f /etc/sudoers.d/xk0broken; echo "--- exit $? : the colon is missing and visudo says so"/etc/sudoers.d/xk0broken:1:29: expecting ':' but found '/'xk0user ALL=(root) NOPASSWD /usr/bin/id ^/etc/sudoers.d/xk0broken:1:29: syntax error: expecting ':' but found '/'xk0user ALL=(root) NOPASSWD /usr/bin/id ^visudo: invalid sudoers file--- exit 1 : the colon is missing and visudo says so
Domain 3 - Security (18%)
-
sestatus / getenforceSELinux state, policy and BOTH modes: the running one and the one in the config file, which can differ until a reboot.
bash Example session sestatus | head -6SELinux status: enabledSELinuxfs mount: /sys/fs/selinuxSELinux root directory: /etc/selinuxLoaded policy name: targetedCurrent mode: enforcingMode from config file: enforcing -
ls -Z <file>The SELinux label, which is what policy is written about. The type - `passwd_file_t`, `httpd_sys_content_t` - is the part that decides access.
bash Example session ls -Z /etc/passwd /etc/shadow /var/log/messages; echo "--- every file carries a label, and the policy is written about labels"system_u:object_r:passwd_file_t:s0 /etc/passwd system_u:object_r:shadow_t:s0 /etc/shadow system_u:object_r:var_log_t:s0 /var/log/messages--- every file carries a label, and the policy is written about labels -
restorecon -v <file> (or -Rv <dir>)Set the label the policy says a path SHOULD have. Use this rather than `chcon`, which is undone by the next relabel.
bash Example session sudo restorecon -v /var/www/html/moved.html; ls -Z /var/www/html/moved.html; curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost/moved.htmlRelabeled /var/www/html/moved.html from unconfined_u:object_r:user_tmp_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/moved.htmlHTTP 200 -
aa-statusAppArmor profiles loaded and how many are enforcing. Note the tools that CHANGE a profile ship separately, in `apparmor-utils`.
bash Example session sudo aa-status | head -5apparmor module is loaded.182 profiles are loaded.106 profiles are in enforce mode. /usr/bin/man /usr/lib/snapd/snap-confine -
ssh-keygen -lf <key>The fingerprint - identical for both halves of a pair, because it is a hash of the public key. That is how you match a key to an authorized_keys line.
bash Example session ssh-keygen -lf ~/xk0keys/demo.pub; ssh-keygen -lf ~/xk0keys/demo; echo "--- both halves report the same fingerprint, because the fingerprint is of the public key"256 SHA256:yBC/YcTFW9HO/XAYMGQsRxYzT/Xd7fN3mThv5NfLBnk xk0-006 demo (ED25519)256 SHA256:yBC/YcTFW9HO/XAYMGQsRxYzT/Xd7fN3mThv5NfLBnk xk0-006 demo (ED25519)--- both halves report the same fingerprint, because the fingerprint is of the public key -
sshd -T | grep -E '^(permitrootlogin|passwordauthentication)'The EFFECTIVE ssh policy, with every include and drop-in resolved - which is not what the main config file appears to say.
bash Example session sudo sshd -T 2>/dev/null | grep -E "^(permitrootlogin|passwordauthentication|pubkeyauthentication|permitemptypasswords|maxauthtries|x11forwarding)" | sortmaxauthtries 6passwordauthentication yespermitemptypasswords nopermitrootlogin prohibit-passwordpubkeyauthentication yesx11forwarding yes -
openssl x509 -in <crt> -noout -subject -issuer -dates -ext subjectAltNameRead a certificate. Subject equal to issuer means self-signed; modern clients check subjectAltName and ignore the Common Name entirely.
bash Example session cd ~/xk0keys && openssl x509 -in site.crt -noout -subject -issuer -dates -ext subjectAltNamesubject=CN=lplus-a01.labissuer=CN=lplus-a01.labnotBefore=Aug 30 14:13:50 2026 GMTnotAfter=Aug 30 14:13:50 2027 GMTX509v3 Subject Alternative Name: DNS:lplus-a01.lab
Domain 4 - Automation, Orchestration and Scripting (17%)
-
set -euo pipefailThe three settings that stop a script reporting success after a failed command. Without them the exit status is the LAST command's.
bash Example session cd ~/xk0auto && chmod +x strict.sh && ./strict.sh; echo "exit $? - and it stopped at the line that failed"cp: cannot stat '/etc/definitely-not-here': No such file or directoryexit 1 - and it stopped at the line that failed -
set -o pipefailMeasured on and off: `false | true` returns 1 with it and 0 without. Essential in anything shaped like `command | grep | awk`.
bash Example session cd ~/xk0auto && printf '#!/bin/bash\nset -o pipefail\nfalse | true\necho "with pipefail: $?"\nset +o pipefail\nfalse | true\necho "without it: $?"\n' > pipe.sh && chmod +x pipe.sh && ./pipe.shwith pipefail: 1without it: 0 -
git status --short / git diff --stat`M` in the second column is modified-in-worktree, `??` is untracked. `git diff` shows what is NOT staged; `--staged` shows what is.
bash Example session cd ~/xk0auto && git status --short; echo "--- modified, not staged: git knows the difference between the file and the commit" M check.sh--- modified, not staged: git knows the difference between the file and the commit -
podman unshare cat /proc/self/uid_mapThe rootless mapping: container uid 0 is your own uid on the host, and the rest come from /etc/subuid. Also how you chown a volume a container wrote.
bash Example session podman unshare cat /proc/self/uid_map; echo "--- the mapping itself: this user's 1000 is the container's 0" 0 1000 1 1 100000 65536--- the mapping itself: this user's 1000 is the container's 0 -
podman build -t <name>:<tag> .Build from a Containerfile. A locally built image is named under `localhost/`, which is how podman keeps it apart from a registry image.
bash Example session cd ~/xk0img && podman build -t xk0check:1.0 . 2>&1 | tail -4COMMIT xk0check:1.0--> 91b54afd8c34Successfully tagged localhost/xk0check:1.091b54afd8c34d2492b3636196ad3939918cdc47e159c651846f5781c6acbada6 -
podman history --format "{{.CreatedBy}} {{.Size}}" <image>What every instruction cost. RUN adds bytes; LABEL, USER, ENTRYPOINT and CMD are 0 B - which is the whole argument for ordering a Containerfile.
bash Example session podman history --format "{{.CreatedBy}} {{.Size}}" xk0check:1.0 2>/dev/null | head -8; echo "--- one layer per instruction that changes the filesystem"/bin/sh -c #(nop) CMD ["80"] 0B/bin/sh -c #(nop) ENTRYPOINT ["/usr/local/... 0B/bin/sh -c #(nop) USER 1000:1000 0B/bin/sh -c chmod +x /usr/local/bin/check.sh 4.1kB/bin/sh -c #(nop) COPY file:41eb43131f6665... 3.58kB/bin/sh -c apk add --no-cache coreutils > ... 1.65MB/bin/sh -c #(nop) LABEL org.opencontainers... 0BCMD ["/bin/sh"] 0B--- one layer per instruction that changes the filesystem -
podman generate systemd --new --name <container>Generate a unit rather than writing one. `--new` creates the container at each start, and the generator gets TimeoutStopSec right.
bash Example session podman create --name xk0once xk0check:1.0 > /dev/null && podman generate systemd --new --name xk0once 2>/dev/null | grep -vE "^#|^$" | head -12; podman rm -f xk0once > /dev/null; echo "--- generated, not installed"[Unit]Description=Podman container-xk0once.serviceDocumentation=man:podman-generate-systemd(1)Wants=network-online.targetAfter=network-online.targetRequiresMountsFor=%t/containers[Service]Environment=PODMAN_SYSTEMD_UNIT=%nRestart=on-failureTimeoutStopSec=70ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \--- generated, not installed
Domain 5 - Troubleshooting (22%)
-
cat /proc/pressure/{cpu,memory,io}How much time was LOST waiting, which an average cannot tell you. `some` is any task stalled; `full` is everything stalled at once.
bash Example session cat /proc/pressure/cpu /proc/pressure/memory /proc/pressure/io 2>/dev/null | head -6; echo "--- pressure stall information: how much time was LOST waiting, which an average cannot tell you"some avg10=0.03 avg60=0.05 avg300=0.01 total=28181694full avg10=0.00 avg60=0.00 avg300=0.00 total=0some avg10=0.00 avg60=0.00 avg300=0.00 total=1full avg10=0.00 avg60=0.00 avg300=0.00 total=1some avg10=0.00 avg60=0.00 avg300=0.00 total=42842379full avg10=0.00 avg60=0.00 avg300=0.00 total=22418916--- pressure stall information: how much time was LOST waiting, which an average cannot tell you -
ps -eo pid,pcpu,comm --sort=-pcpu | head / top -bn1`ps` is scriptable and reports a lifetime average; `top -bn1` reports the last interval. They disagree on a spike, and both are right.
bash Example session top -bn1 | head -8 | tail -4MiB Swap: 3910.0 total, 3910.0 free, 0.0 used. 2883.4 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 28856 sysadmin 25 5 2888 1940 1828 R 100.0 0.1 0:03.41 burn.sh -
lsof +L1 <mountpoint>The fault people reboot for: `df` says full, `du` disagrees, and a deleted file is still held open. `+L1` lists exactly those.
bash Example session sudo sh -c 'exec 9< /mnt/full/big.bin; rm -f /mnt/full/big.bin; echo "--- after rm, with the file still open:"; df -h /mnt/full | tail -1; du -sh /mnt/full; lsof -n +L1 /mnt/full 2>/dev/null | tail -2'--- after rm, with the file still open:/dev/loop0 56M 41M 12M 78% /mnt/full20K /mnt/fullsh 29165 root 9r REG 7,0 41943040 0 13 /mnt/full/big.bin (deleted)tail 29172 root 9r REG 7,0 41943040 0 13 /mnt/full/big.bin (deleted) -
ss -ltnp | grep <port>Read the bind address before blaming the firewall. `127.0.0.1:8099` can never be reached remotely; `0.0.0.0:8099` can.
bash Example session ss -ltnp 2>/dev/null | grep 8099; echo "--- the bind address is the whole answer: 127.0.0.1 is not 0.0.0.0"LISTEN 0 5 127.0.0.1:8099 0.0.0.0:* users:(("python3",pid=29280,fd=3))--- the bind address is the whole answer: 127.0.0.1 is not 0.0.0.0 -
namei -l /full/path/to/fileWalks every component of a path and names the one that refused. A 644 file can be unreadable because a directory two levels up is 700.
bash Example session namei -l /srv/xk0secret/data.txtf: /srv/xk0secret/data.txtdrwxr-xr-x root root /drwxr-xr-x root root srvdrwx------ root root xk0secret data.txt - Permission denied[exit 1] -
journalctl -p err -b / systemctl --failedThe two triage commands for a machine you have just been handed. Priorities nest, so `-p err` includes critical and above.
bash Example session sudo journalctl -p err -b --no-pager | tail -4; echo "--- errors this boot, which is the triage command"Aug 29 08:26:31 lplus-a01 kernel: piix4_smbus 0000:00:07.3: SMBus base address uninitialized - upgrade BIOS or use force_addr=0xaddrAug 30 12:57:46 lplus-a01 su[20466]: pam_unix(su-l:auth): auth could not identify password for [xk0svc]Aug 30 12:57:50 lplus-a01 systemd[1]: Failed to start xk0svc.service - XK0-006 demonstration service.Aug 30 12:58:26 lplus-a01 systemd[1]: Failed to start xk0svc.service - XK0-006 demonstration service.--- errors this boot, which is the triage command -
systemd-run --scope -p MemoryMax=64M -p MemorySwapMax=0 <cmd>Bound a process for real. `MemoryMax` alone lets it swap instead of dying - both properties are needed, and the kill is logged as `oom-kill`.
bash Example session sudo systemd-run --scope -p MemoryMax=64M -p MemorySwapMax=0 --unit=xk0oom sh -c 'python3 -c "b=bytearray(); [b.extend(bytes(1024*1024)) for _ in range(200)]; print(\"allocated 200 MB\")"' 2>&1 | tail -3; echo "--- exit ${PIPESTATUS[0]:-$?}"Running as unit: xk0oom.scope; invocation ID: 1eaf50047e3341beadc4c15e71649d4dKilled--- exit 0
No command matches that search.