Skip to content

Must-Know Facts

Every topic's Must-Know Facts table in one place, for a fast revision pass. Tables are pulled in from the topic files, so this page never drifts from them.


Foundations

What Is Linux

Fact Value Verify with
Linux is A kernel; a full system adds a userland and becomes a distribution uname -s
Author and year Linus Torvalds, first release 1991 cat /proc/version
Kernel license GPL version 2 only, plus the Linux syscall exception, which keeps user programs outside the GPL dnf repoquery --qf '%{license}' kernel-core
Userland license (bash, coreutils) GPL version 3 or later rpm -q --qf '%{LICENSE}\n' bash
Ancestor design Unix (Bell Labs, 1969); Linux reimplements it, sharing no code man 7 standards
Standards POSIX and the Single UNIX Specification (Linux is not certified) getconf _POSIX_VERSION
Release cadence A new mainline kernel roughly every 9 to 10 weeks uname -r

Kernel vs OS vs Distro

Fact Value Verify with
Kernel Process scheduling, memory, filesystems, networking, drivers uname -r
Userland glibc, bash, coreutils, systemd, package manager ldd --version
Shell A user program that starts other programs; not part of the kernel echo $0
Distribution Kernel + userland + package manager + defaults + support lifecycle cat /etc/os-release
GNU/Linux Name that credits the GNU userland; Alpine and Android use Linux without GNU ls --version
Kernel and distro versions Independent numbers uname -r; grep VERSION_ID /etc/os-release
Containers Bring their own userland and share the host kernel uname -r inside a container
Kernel package kernel / kernel-core (RHEL), linux-image-* (Ubuntu) rpm -q kernel

Distributions

Fact Value Verify with
Red Hat family RHEL, Rocky, AlmaLinux, CentOS Stream, Fedora, Amazon Linux grep ID_LIKE /etc/os-release
Debian family Debian, Ubuntu, Linux Mint grep ID_LIKE /etc/os-release
Package formats .rpm with dnf; .deb with apt rpm --version, dpkg --version
CentOS Stream Upstream of RHEL since 2021, not a rebuild cat /etc/redhat-release
RHEL rebuilds Rocky Linux and AlmaLinux grep ^ID= /etc/os-release
Ubuntu LTS Every two years in April (YY.04), 5 years standard support lsb_release -a
RHEL support 10 years per major release grep SUPPORT_END /etc/os-release
Container minimal images Alpine (musl, BusyBox), distroless, UBI cat /etc/os-release in the image
Architecture names x86_64 = amd64; aarch64 = arm64 uname -m, dpkg --print-architecture

Linux vs Windows

Fact Value Verify with
Privilege model Root (UID 0) versus unprivileged users; daily work runs unprivileged id
Password hashes /etc/shadow, readable by root only ls -l /etc/shadow
Package trust Repositories and packages are GPG-signed and checked on install rpm -q gpg-pubkey
Mandatory access control SELinux (RHEL) or AppArmor (Ubuntu) on top of file permissions cat /sys/kernel/security/lsm
Memory randomization ASLR on by default (2 = full) sysctl kernel.randomize_va_space
Configuration Plain-text files under /etc, no central registry ls /etc
Remote administration SSH and a shell; no GUI required systemctl status sshd
License cost No per-core or per-instance fee for the OS itself Vendor pricing

Architecture

Fact Value Verify with
Layers Hardware, kernel, system calls, C library, shell and applications ldd /usr/bin/ls
Kernel space Runs in CPU privileged mode (ring 0 on x86) with full hardware access grep -c . /proc/kallsyms
User space Runs in ring 3; reaches hardware only through system calls strace -c ls
System call The only entry point from a program into the kernel strace -e trace=openat cat /etc/hostname
C library glibc on RHEL and Ubuntu, musl on Alpine; wraps system calls ldd /usr/bin/ls
Dynamic loader /lib64/ld-linux-x86-64.so.2 loads shared libraries at exec file /usr/bin/ls
vDSO Kernel page mapped into every process for fast calls such as clock_gettime grep vdso /proc/self/maps
Kernel design Monolithic, with loadable modules lsmod
PID 1 systemd, the first user-space process ps -o pid,comm -p 1
PID 2 kthreadd, parent of all kernel threads ps --ppid 2
Error reporting System calls return -1 and set errno (ENOENT, EACCES) strace ls /nonexistent

System Information

Fact Value Verify with
Distribution and version /etc/os-release (standard on every systemd distribution) cat /etc/os-release
Kernel release Separate from the distribution version uname -r
CPU architecture x86_64 or aarch64 uname -m
Logical CPUs Count of schedulable CPUs nproc
Memory that can be used now available column, not free free -h
Block devices and mounts Tree of disks, partitions and mount points lsblk
Uptime and load Time since boot, ⅕/15-minute load averages uptime
Virtualization Hypervisor type, or none on bare metal systemd-detect-virt, hostnamectl
Firmware tables dmidecode reads SMBIOS; absent on some microVMs sudo dmidecode -t system
Page size 4096 bytes on x86_64 getconf PAGE_SIZE

Shell and CLI

Shell Basics

Fact Value Verify with
Terminal Device that carries input and output (/dev/pts/N for SSH, /dev/ttyN for consoles) tty
Shell Program that parses and runs commands (bash, dash, zsh) echo $0
Login shell of a user Field 7 of /etc/passwd getent passwd $USER
Allowed login shells /etc/shells cat /etc/shells
/bin/sh bash on RHEL, dash on Ubuntu readlink -f /bin/sh
Virtual consoles getty@ttyN services; Ctrl+Alt+F2 switches on physical machines systemctl list-units 'getty@*'
Interactive shell $- contains i echo $-
Login shell Started by login, sshd or bash -l; reads profile files shopt login_shell
History file ~/.bash_history, written when the shell exits echo $HISTFILE
Trace a command line set -x prints each command after expansion bash -x script.sh

Getting Help

Fact Value Verify with
Man sections 1 commands, 5 file formats, 8 admin commands, 2 system calls, 3 library calls, 7 overviews man -f intro
Page in a section man 5 passwd for the file, man passwd for the command man -f passwd
Search descriptions man -k or apropos; needs an index built by mandb apropos -s 8 'user account'
Builtin help help <builtin>; builtins have no man page of their own help cd
Quick usage <command> --help ls --help
Package docs /usr/share/doc/<package> rpm -qd sudo
Container images Often ship without docs (tsflags=nodocs, dpkg excludes) grep tsflags /etc/dnf/dnf.conf

Command Resolution

Fact Value Verify with
Lookup order Alias, keyword, function, builtin, hash table, PATH type -a <name>
type vs which type is the shell's own answer; which searches PATH only type echo; which echo
Portable lookup in scripts command -v <name> command -v echo
Bypass an alias \name or command name \ls
Skip aliases and functions command name (builtins still win); a full path always runs the file command ls
Hash table Remembers the path of each external command already run hash -t <name>
Clear the hash hash -r; assigning PATH also clears it hash
Not found Exit code 127 nosuchcmd; echo $?
Found but not executable Exit code 126 echo $?
Current directory Not in PATH; run local files as ./name echo $PATH

Variables and Environment

Fact Value Verify with
Shell variable Visible only in the current shell set
Environment variable Exported; copied to child processes at exec env, printenv
Export export VAR=value or declare -x VAR declare -p VAR
One-command variable VAR=value command sets it for that command only LOG_LEVEL=debug env
Children cannot change parents A child's export never reaches the parent shell bash -c 'export X=1'; echo $X
Process environment Fixed at exec; later export does not change a running process tr '\0' '\n' < /proc/<pid>/environ
PATH Colon-separated directories searched in order; . is not included echo $PATH
Login shell files /etc/profile, /etc/profile.d/*.sh, then the first of ~/.bash_profile, ~/.bash_login, ~/.profile bash -l
Non-login interactive files ~/.bashrc (which sources /etc/bashrc on RHEL) bash -i
Scripts, cron, systemd Read no startup files bash -c env
System-wide static variables /etc/environment, read by pam_env at login cat /etc/environment
sudo Resets the environment and sets PATH from secure_path sudo printenv PATH
Read-only variable readonly VAR=value; cannot be changed or unset readonly -p

Locale and Encoding

Fact Value Verify with
Precedence LC_ALL overrides every LC_*, which override LANG locale
C / POSIX locale Byte order, ASCII, English messages; same result on every machine LC_ALL=C sort
C.UTF-8 Byte-order sorting with UTF-8 characters locale -a
System default /etc/locale.conf (RHEL), /etc/default/locale (Ubuntu) localectl status
Installed locales glibc-langpack-* (RHEL), locale-gen (Ubuntu) locale -a
Encoding of a file Detected, not stored file -i <file>
Convert encoding iconv -f <from> -t <to> iconv -l

Quoting and Expansion

Fact Value Verify with
Single quotes Everything literal; no expansion at all echo '$HOME'
Double quotes Expand $var, $(cmd), $((expr)); prevent word splitting and globbing echo "$HOME"
Backslash Escapes one character echo \$HOME
Expansion order Brace, tilde, parameter/command/arithmetic, word splitting, pathname (glob), quote removal set -x
Globs Expanded by the shell, not by the command echo *
Unmatched glob Left as literal text unless nullglob is set echo *.none
Word splitting Unquoted expansions split on IFS (space, tab, newline) set -x
Always quote "$var", "$(cmd)", "$@" bash -x script.sh
End of options -- stops option parsing, for names starting with - rm -- -rf
Command substitution $(cmd) nests cleanly; backticks are the legacy form echo "$(date)"

Streams and Redirection

Fact Value Verify with
Standard streams 0 stdin, 1 stdout, 2 stderr ls -l /proc/self/fd
> / >> Truncate / append stdout to a file echo x >> f
2> Redirect stderr ls /nope 2> err.txt
> file 2>&1 Both streams to the file; order matters cat file
2>&1 > file Only stdout to the file; stderr stays on the terminal cat file
&> file Bash shorthand for > file 2>&1 cat file
Pipe (vertical bar) Connects stdout of one command to stdin of the next; stderr is not piped ls -l /proc/self/fd
Piping both streams 2>&1 before the bar, or the bash bar-ampersand form set -x
Pipeline status Status of the last command, unless pipefail is set echo ${PIPESTATUS[*]}
/dev/null Discards writes, returns end of file on read ls -l /dev/null
Writing root-owned files Pipe into sudo tee file; sudo cmd > file redirects as the caller sudo cat file
Here-document <<EOF expands variables; <<'EOF' does not cat <<'EOF'
Process substitution <(cmd) presents output as a file path diff <(a) <(b)

Exit Codes and Chaining

Fact Value Verify with
Last status $?, overwritten by the next command echo $?
Success 0 true; echo $?
General failure 1 false; echo $?
Misuse or serious error 2 (ls, grep on a missing file) ls /nope; echo $?
Found but cannot execute 126 /usr/lib/os-release; echo $?
Command not found 127 nosuchcmd; echo $?
Killed by signal N 128 + N: 130 SIGINT, 137 SIGKILL, 141 SIGPIPE, 143 SIGTERM kill -l 9
timeout expired 124 timeout 1 sleep 5; echo $?
Values above 255 Reduced modulo 256 (exit 300 gives 44) bash -c 'exit 300'; echo $?
a && b b runs only if a succeeded mkdir d && cd d
OR list (double bar) The second command runs only if the first failed echo $?
a ; b b always runs echo $?
grep statuses 0 match, 1 no match, 2 error grep -q x f; echo $?

Text Editors

Fact Value Verify with
vi on RHEL vim-minimal; full vim is vim-enhanced rpm -qf /usr/bin/vi
vi on Ubuntu Alternatives link to vim.basic or vim.tiny readlink -f /usr/bin/vi
Default editor on Ubuntu nano, through the editor alternative update-alternatives --display editor
Editor used by tools $VISUAL, then $EDITOR, then a built-in default echo $EDITOR
Edit a root file safely sudoedit <file> (sudo -e); runs the editor as the user sudoedit /etc/hosts
Learn vim vimtutor (30 minutes) vimtutor

Scripting Essentials

Fact Value Verify with
Shebang First line, #!/bin/bash; chooses the interpreter head -1 script.sh
Run a script chmod +x script.sh; ./script.sh, or bash script.sh ls -l script.sh
Arguments $0 name, $1..$9, ${10}, $# count, "$@" all, kept separate ./args.sh a "b c"
Test commands [ ] (POSIX), [[ ]] (bash: patterns, no word splitting) help [[
Numeric comparison -eq -ne -lt -le -gt -ge, or (( a < b )) [ 5 -gt 3 ]
String comparison =, !=, -z (empty), -n (not empty) [ -z "$x" ]
File tests -e exists, -f file, -d directory, -r -w -x, -s not empty [ -d /etc ]
Read a file line by line while IFS= read -r line; do ...; done < file help read
Function variables local keeps them inside the function help local
Syntax check and lint bash -n script.sh; shellcheck script.sh (EPEL, Ubuntu universe) echo $?

Files and Filesystem

Filesystem Hierarchy

Fact Value Verify with
One tree Disks, partitions and network shares are mounted into /; no drive letters findmnt
/etc Host-specific configuration, plain text ls /etc
/var Data that changes: logs, caches, spools, databases ls /var
/var/log Log files ls /var/log
/var/lib Persistent application state (package databases, Docker, databases) ls /var/lib
/usr Installed software, read-only in normal operation du -sh /usr
usrmerge /bin, /sbin, /lib, /lib64 are symlinks into /usr ls -l /bin
/usr/local, /opt Software installed outside the package manager ls /usr/local
/tmp vs /var/tmp Both world-writable with the sticky bit; /var/tmp survives reboots and is cleaned less often, or never ls -ld /tmp /var/tmp
/run tmpfs for runtime data (PID files, sockets); emptied at boot findmnt /run
/proc, /sys Virtual filesystems exposing kernel and device state findmnt /proc
/dev Device files, created by the kernel and udev ls -l /dev/null
/boot Kernel, initramfs, bootloader files ls /boot
/root vs /home Root's home (mode 700) vs regular users' homes ls -ld /root /home
References man 7 hier, man 7 file-hierarchy man -w 7 hier

File Types

Fact Value Verify with
- Regular file ls -l
d Directory ls -ld /etc
l Symbolic link ls -l /bin
c Character device (byte stream: terminals, /dev/null) ls -l /dev/null
b Block device (disks, partitions) ls -l /dev/vda
p Named pipe (FIFO) mkfifo p; ls -l p
s Unix domain socket ls -l /run/systemd/journal/stdout
Device numbers Major (driver) and minor (instance) replace the size column ls -l /dev/vda
Type from content file reads magic bytes; extensions mean nothing to the kernel file <path>
Type as text stat -c %F stat -c %F /dev/null
Search by type find -type f,d,l,c,b,p,s find /dev -type b
Fact Value Verify with
Absolute path Starts at / cd /etc/ssh
Relative path Starts at the current directory; . current, .. parent cd ../log
Home cd, cd ~; another user's home is ~user echo ~root
Previous directory cd - (prints it); $OLDPWD holds it echo $OLDPWD
Logical vs physical path pwd keeps symlink names; pwd -P resolves them pwd -P
Hidden files Names starting with .; shown by ls -a ls -a ~
Newest last ls -ltr ls -ltr /var/log
Largest first ls -lS ls -lhS
Directory itself, not contents ls -ld <dir> ls -ld /tmp

File Operations

Fact Value Verify with
Create parents mkdir -p (no error if the directory exists) mkdir -pv a/b
touch Creates an empty file or updates its timestamps stat -c %y f
cp Copies content; new file gets the copier's owner and the current time ls -l
cp -a Archive: recursive, keeps mode, owner, timestamps, links, xattrs ls -l
cp -r Recursive only; does not keep ownership or times ls -l
mv on one filesystem Rename: same inode, instant ls -i
mv across filesystems Copy, then delete the source ls -i
rm Removes a name (unlinks); no recycle bin ls -l
rmdir Removes empty directories only rmdir d
Sparse file Size is larger than the blocks it uses du -h --apparent-size
install Copy plus mode and ownership in one step install -m 755 src dst
Download that fails on HTTP errors curl -fsSL -o file URL echo $?
Fact Value Verify with
Inode holds Type, mode, UID, GID, size, timestamps, link count, block pointers stat <file>
Inode does not hold The file name ls -i
Directory A table of name to inode number ls -li
Hard link Another name for the same inode; same filesystem only; not for directories ln a b
Symbolic link A small file containing a path; can cross filesystems and point to directories ln -s a b
Link count Number of names for an inode; a new directory starts at 2 stat -c %h <file>
Deletion rm removes a name; data is freed at link count 0 and no open descriptors lsof +L1
Inode exhaustion "No space left on device" with free blocks df -i
mtime Content changed stat -c %y
ctime Inode changed (content, mode, owner, links); cannot be set by touch stat -c %z
atime Last read; relatime updates it at most once a day findmnt -o OPTIONS /
Root inode 2 on ext4 (XFS uses a different number) stat -c %i /

File Descriptors

Fact Value Verify with
Descriptor Index into the process's descriptor table ls -l /proc/<pid>/fd
Allocation Lowest free number; 0, 1, 2 are normally taken strace -e trace=openat
Three layers Descriptor table (per process), open file description (offset, flags), inode (the file) cat /proc/<pid>/fdinfo/<n>
After fork() Child gets copies of the descriptors pointing to the same open file descriptions, so offsets are shared read in a subshell
Separate open() New open file description with its own offset /proc/<pid>/fdinfo
dup2(old, new) Makes new refer to the same description; this is 2>&1 strace -e trace=dup2
O_APPEND Every write goes to the end, atomically flags in fdinfo
O_CLOEXEC Descriptor closes on execve; flag 02000000 flags in fdinfo
Per-process limit ulimit -n / RLIMIT_NOFILE, often soft 1024 grep 'open files' /proc/<pid>/limits
System-wide counters /proc/sys/fs/file-nr, file-max, nr_open cat /proc/sys/fs/file-nr
Limit reached EMFILE: "Too many open files" ls /proc/<pid>/fd
Sockets and pipes Also descriptors; count against the same limit lsof -p <pid>

Finding Files

Fact Value Verify with
Syntax find <paths> <tests> <actions>; default action -print find /etc -name '*.conf'
Quote patterns Otherwise the shell expands them first find . -name '*.log'
Size units -size +100M, -size -10k; + more than, - less than find / -xdev -size +1G
Time tests -mtime +30 older than 30 days; -mmin -60 newer than an hour find /var/log -mtime +30
Permission tests -perm 644 exact, -perm -4000 all bits set, -perm /022 any bit set find / -perm -4000
Owner tests -user, -group, -nouser, -nogroup find / -nouser
Run a command -exec cmd {} \; once per file; -exec cmd {} + batched find . -exec ls {} +
Safe with xargs -print0 and xargs -0 handle spaces and newlines find . -print0
Delete -delete; put it last find /tmp -name '*.tmp' -delete
Stay on one filesystem -xdev find / -xdev
Depth -maxdepth N, -mindepth N find . -maxdepth 1
Name index locate searches the database built by updatedb locate sshd_config
Command location which searches PATH; whereis adds man pages whereis sshd

Archiving and Compression

Fact Value Verify with
Create, list, extract tar -c, -t, -x tar -tf <archive>
-f The next argument is the archive file; without it, tar uses stdin or stdout tar -tzf a.tgz
-C <dir> Change to <dir> before extracting or adding files tar -xf a.tgz -C /opt
Compression flags -z gzip, -j bzip2, -J xz, --zstd zstd file <archive>
Extracting GNU tar detects the compression; the flag is optional tar -xf a.tar.xz
Absolute paths Leading / is stripped on create tar -tf
Permissions Stored always; restored for root by default, -p for others tar -tvf
Compressors Ratio on typical text: xz > zstd > bzip2 > gzip; speed: zstd and gzip fastest, xz slowest ls -l, time
Read compressed files zcat, zless, zgrep; xzcat, bzcat, zstdcat zcat f.gz
Useful extras --exclude='*.log', --strip-components=1, one member by path tar -tf <archive>
zip Archive and compression in one; common with Windows users unzip -l f.zip

Text Processing

Viewing and Comparing

Fact Value Verify with
Page through a file less (/ search, G end, F follow, q quit) less /var/log/messages
First or last lines head -n N, tail -n N tail -n 50 file
Follow a log tail -f follows the open file; tail -F follows the name across rotation tail -F /var/log/app.log
Count wc -l lines, -w words, -c bytes wc -l < file
Hidden characters cat -A shows ^M (CR), ^I (tab), $ (line end) cat -A file
Byte view od -c, xxd, hexdump -C od -c file
Compare files diff (exit 0 same, 1 different, 2 error); diff -u for patches diff -u a b
Binary compare cmp reports the first differing byte cmp a b
Compressed logs zcat, zless, zgrep read .gz without extracting zgrep ERROR app.log.1.gz
Checksums sha256sum; -c verifies a list sha256sum -c SHA256SUMS
Base64 Encoding, not encryption base64 -d
Line ranges sed -n '5,7p' file sed -n '5,7p'

grep and Regex

Fact Value Verify with
Common flags -i ignore case, -v invert, -n line numbers, -c count, -w whole word, -x whole line grep -in error app.log
Output only the match -o grep -o '[0-9]*' f
Context -A N after, -B N before, -C N both grep -C2 ERROR log
Recursive -r (-R follows symlinks); --include, --exclude, --exclude-dir grep -r --include='*.conf' x /etc
File names only -l matching, -L not matching grep -rl TODO .
Quiet test -q; status 0 match, 1 no match, 2 error grep -q x f; echo $?
Regex flavors Basic (default), -E extended, -P Perl, -F fixed string grep -E 'a+'
BRE vs ERE In BRE, +, ?, the bar, parentheses and braces need a backslash to be special grep 'a\+'
Anchors ^ line start, $ line end, \b word boundary grep '^#'
Classes [0-9], [^a-z], [[:space:]], [[:digit:]]; \s, \w in GNU grep grep '[[:digit:]]'
Repetition * 0+, + 1+, ? 0 or 1, {n,m} (ERE) grep -E 'a{2,}'
Dot Any character; \. is a literal dot grep -c '\.'
Binary files Reports "binary file matches"; -a treats them as text grep -a

sed

Fact Value Verify with
Substitute s/old/new/ first match per line; g all; N the Nth; I ignore case (GNU) sed 's/a/b/g'
Default output Every line, edited or not; -n plus p prints only selected lines sed -n '2p'
In-place edit -i rewrites the file; -i.bak keeps a backup sed -i.bak ...
BSD and macOS sed -i '' needs an explicit empty suffix man sed
Addresses Line number, $ last line, /regex/, ranges a,b sed -n '/start/,/end/p'
Delete d sed '/^#/d'
Insert and append i text before, a text after, c text replace the line sed '/x/a y'
Delimiter Any character after s: s#a#b# avoids escaping slashes sed 's#/usr#/opt#'
Whole match & in the replacement sed 's/[0-9]\+/<&>/'
Groups \(...\) in BRE, (...) with -E; \1 in the replacement sed -E 's/(a)(b)/\2\1/'
Several commands -e cmd -e cmd or cmd; cmd sed -e ... -e ...
Symlinks -i replaces a symlink with a regular file unless --follow-symlinks ls -l

awk

Fact Value Verify with
Program shape pattern { action }; a missing pattern matches every line, a missing action prints the line awk '/x/'
Fields $1...$NF; $0 is the whole line awk '{print $1}'
Built-in variables NR line number, NF field count, FS/OFS input and output separators, FNR line number per file awk '{print NR, NF}'
Field separator Whitespace by default; -F: or -F' = ' to change it awk -F: '{print $1}' /etc/passwd
Special blocks BEGIN runs before input, END after awk 'END {print NR}'
Comparisons Numeric when both sides look numeric: $9 >= 500 awk '$9 >= 500'
Regex match $7 ~ /re/, $7 !~ /re/ awk '$9 !~ /^2/'
Accumulate sum += $10 in the body, print in END awk '{s+=$10} END {print s}'
Associative arrays count[$1]++, then for (k in count) awk '{c[$1]++} END {for (k in c) print c[k], k}'
Pass shell values -v name=value awk -v t=1.5 '$NF > t'
Implementations gawk on RHEL; mawk by default on Ubuntu until gawk is installed readlink -f "$(command -v awk)"
CSV with quoted commas gawk --csv (gawk 5.3 and later) awk --csv

Cut, Sort, Uniq and Tr

Fact Value Verify with
cut -d X -f N Field N split on exactly one character; repeated delimiters give empty fields cut -d: -f1 /etc/passwd
cut -c Character positions cut -c1-10
sort default Text order by locale; 10 sorts before 9 sort nums.txt
Numeric sorts -n numbers, -h human sizes (2G), -V versions (v1.10) sort -h
Sort by field -t separator, -k2,2 one field; -k2 means field 2 to end of line sort -t: -k3,3n
Key options An option on a key (-k2n) replaces all global ordering options for that key sort -k2,2nr
uniq Removes adjacent duplicates only, so sort first uniq -c after sort
Counting uniq -c; -d only repeated lines; -u only unique lines uniq -c
sort -u Sort and deduplicate in one step sort -u f
tr Translate, squeeze (-s) or delete (-d) characters; reads stdin only tr 'a-z' 'A-Z'
paste, join, comm Merge lines side by side, join on a key, compare sorted lists comm -12 a b
Decimal math bc or awk; $(( )) is integer only awk 'BEGIN {print 10/3}'

xargs and tee

Fact Value Verify with
xargs default Splits input on whitespace and runs the command with as many arguments as fit xargs echo
One item per command -n 1 xargs -n1
Placeholder -I{} runs once per line and replaces {} xargs -I{} echo {}
Safe names -0 with NUL-separated input (find -print0, grep -Z) xargs -0
Parallel -P N runs up to N commands at once xargs -P4
Empty input GNU xargs runs the command once anyway unless -r xargs -r
Show commands -t prints each command before running it xargs -t
Stop on failure A command exiting 255 aborts xargs, which then exits 124 echo $?
Argument limit ARG_MAX bytes; xargs splits long lists automatically getconf ARG_MAX
tee file Writes to the file and to stdout cat f
tee -a Appends tee -a log
Root-owned files sudo tee file, because the shell performs > as the caller ls -l file

JSON and YAML on the CLI

Fact Value Verify with
jq . Pretty-prints and validates JSON jq . file.json
Raw strings -r drops the quotes, for use in shell variables jq -r .name
Iterate arrays .items[] emits each element jq '.items[]'
Filter select(.state == "running") jq 'select(...)'
Build output {id: .Id}, [...], @tsv, @csv jq -c '{id: .Id}'
Shell values --arg name value (string), --argjson (JSON) jq --arg s x
Missing keys Return null, not an error jq '.nope'
Fail on null or false -e sets exit status 1 jq -e .key
yq (mikefarah) jq-like syntax for YAML, JSON, XML; -i edits in place yq --version
Two different yq tools Go mikefarah/yq (EPEL, GitHub releases) and Python kislyuk/yq (Ubuntu apt install yq) apt-cache show yq
Convert formats yq -o=json, yq -p json -o yaml yq -o=json file.yaml

Users and Access

Users

Fact Value Verify with
Superuser UID 0 (any account with UID 0 is root) awk -F: '$3 == 0' /etc/passwd
Account database /etc/passwd, mode 644 (world-readable) ls -l /etc/passwd
Password hashes /etc/shadow, mode 000 (root only) ls -l /etc/shadow
/etc/passwd fields 7: name, x, UID, GID, GECOS, home, shell getent passwd root
Regular UID range 1000 to 60000 grep '^UID_M' /etc/login.defs
System UID range RHEL 10: 201 to 999; Ubuntu: 100 to 999 grep SYS_UID /etc/login.defs
Unprivileged overflow user nobody, UID 65534 id nobody
Account defaults /etc/login.defs and useradd -D useradd -D
Home directory template /etc/skel ls -la /etc/skel
Shell that refuses logins /usr/sbin/nologin su - <service-user>
Lookup through NSS getent passwd <name or UID> getent passwd 0
Consistency check pwck -r (read-only) pwck -r

Groups

Fact Value Verify with
Primary group GID in field 4 of /etc/passwd id -gn <user>
Supplementary groups Member list in field 4 of /etc/group id -Gn <user>
/etc/group fields 4: name, x, GID, member list getent group wheel
Group passwords and admins /etc/gshadow, root only sudo grep <group> /etc/gshadow
User private groups Each new user gets a group of the same name (USERGROUPS_ENAB yes) grep USERGROUPS_ENAB /etc/login.defs
RHEL admin group wheel, GID 10 getent group wheel
Ubuntu admin group sudo, GID 27 getent group sudo
New files get The creator's primary (effective) group touch f; ls -l f
Membership takes effect At the next login or newgrp, not in running sessions grep Groups /proc/$$/status
Append a group usermod -aG <group> <user> (-G alone replaces the list) id <user>

Passwords and Aging

Fact Value Verify with
/etc/shadow fields 9: name, hash, last change, min, max, warn, inactive, expire, reserved sudo getent shadow <user>
Date fields Days since 1970-01-01 sudo chage -l <user>
Hash prefix $y$ yescrypt (default on RHEL 10 and Ubuntu 24.04) sudo grep <user> /etc/shadow
Hash prefix $6$ SHA-512 crypt man 5 crypt
Leading ! on the hash Password locked; the hash is kept sudo passwd -S <user>
passwd -S status P usable, L locked, NP no password sudo passwd -S <user>
Force change at next login chage -d 0 <user> sudo chage -l <user>
Account expiry chage -E YYYY-MM-DD (whole account) sudo chage -l <user>
Password expiry chage -M <days> (password only) sudo chage -l <user>
Defaults for new accounts PASS_MAX_DAYS, PASS_MIN_DAYS, PASS_WARN_AGE in /etc/login.defs grep ^PASS_ /etc/login.defs
Portable scripted password chpasswd (passwd --stdin is RHEL only) sudo chpasswd < <file>

Sudo and Su

Fact Value Verify with
Policy file /etc/sudoers, mode 0440, edited only with visudo ls -l /etc/sudoers
Drop-in directory /etc/sudoers.d/ (root-owned, mode 0440; names containing . or ending in ~ are skipped) ls -l /etc/sudoers.d
Rule syntax who where=(as_whom) what sudo grep -E '^%' /etc/sudoers
Admin group %wheel on RHEL, %sudo on Ubuntu id -Gn
Syntax check visudo -c (all files) or visudo -cf <file> sudo visudo -c
What a user may run sudo -l (self) or sudo -l -U <user> sudo -l -U <user>
Password asked for The caller's own password, not root's sudo -k whoami
Credential cache 5 minutes by default, per terminal ls /run/sudo/ts/
Environment Reset (env_reset) and PATH replaced by secure_path sudo env
Mechanism sudo, su and passwd are SUID-root binaries ls -l /usr/bin/sudo
Audit trail journald (tag sudo); /var/log/secure (RHEL) or /var/log/auth.log (Ubuntu) when rsyslog runs journalctl -t sudo

PAM

Fact Value Verify with
Per-service stacks /etc/pam.d/<service> ls /etc/pam.d
Module types auth, account, password, session cat /etc/pam.d/su
Control flags required, requisite, sufficient, optional, include, substack man 5 pam.conf
RHEL stack manager authselect (do not edit system-auth by hand) authselect current
Ubuntu stack manager pam-auth-update (profiles in /usr/share/pam-configs) ls /usr/share/pam-configs
Failed-login lockout pam_faillock, default deny=3 sudo faillock --user <user>
Password quality pam_pwquality, /etc/security/pwquality.conf grep pwquality /etc/pam.d/*

Login Sessions

Fact Value Verify with
Current sessions /run/utmp (read by who, w) who
Login history and reboots /var/log/wtmp (read by last) last -n 5
Failed logins /var/log/btmp, mode 660, root and group utmp only (read by lastb) sudo lastb -n 5
Last login per user /var/log/lastlog, a sparse file indexed by UID lastlog -u <user>
Load and activity w prints uptime, load and each user's current command w
systemd view loginctl list-sessions loginctl

Centralized Identity

Fact Value Verify with
Lookup order /etc/nsswitch.conf (passwd:, group:, shadow: lines) grep ^passwd: /etc/nsswitch.conf
Query through NSS getent passwd <user>; getent -s files limits the source getent -s files passwd root
Directory client SSSD (sssd service, /etc/sssd/sssd.conf, mode 600) systemctl status sssd
Join a domain realm join <domain> (realmd) realm list
Kerberos ticket kinit <user>, klist klist
Create homes on first login pam_mkhomedir (oddjob-mkhomedir on RHEL) grep mkhomedir /etc/pam.d/*

Permissions

Basic Permissions

Fact Value Verify with
Permission string Type, then rwx for user, group, other ls -l
Octal values r 4, w 2, x 1; 640 = rw-r----- stat -c '%a %A' f
File r, w, x Read content, change content, run as a program cat, echo >>, ./f
Directory r List names ls dir
Directory w Create, delete and rename entries (with x) touch dir/f
Directory x Enter and reach entries by name cd dir
Deleting a file Needs w and x on the directory, not on the file rm dir/f
Check order Owner match, else group match, else other; only the first match applies ls -l
Root Bypasses read and write checks; needs at least one x bit to execute sudo cat f
chown Only root changes the owner sudo chown user:group f
chgrp Owner may change to a group they belong to chgrp devs f
Capital X Execute only for directories and already-executable files chmod -R u=rwX,go=rX dir
Every parent matters Each directory on the path needs x namei -l /path/to/file

umask

Fact Value Verify with
Starting modes Programs usually request 666 for files and 777 for directories strace -e trace=openat touch f
Rule Final mode = requested mode with the umask bits removed (bitwise, not subtraction) umask 0123; touch f
Execute on files Never added by the umask; files from touch get no x ls -l
Scope Per process, inherited by children grep Umask /proc/$$/status
Show umask (octal), umask -S (symbolic) umask -S
Rocky 10.2 default 0022 for root and regular users su - user -c umask
Ubuntu 24.04 default 0022 for root, 0002 for regular users (pam_umask with user private groups) su - user -c umask
System setting UMASK in /etc/login.defs, applied by pam_umask at login grep UMASK /etc/login.defs
Per user umask line in ~/.bashrc or ~/.profile umask
Services UMask= in the unit; PID 1 runs with 0000 systemctl show <unit> -p UMask

Special Permissions

Fact Value Verify with
SUID (4000) Executable runs with the file owner's effective UID ls -l /usr/bin/passwd
SGID on a file (2000) Executable runs with the file group's effective GID ls -l /usr/bin/write
SGID on a directory New files and subdirectories inherit the directory's group; subdirectories also get SGID ls -ld /srv/team
Sticky bit (1000) In a writable directory, only the file owner, directory owner or root may delete or rename a file ls -ld /tmp
Display s in the user or group execute position, t in the other position ls -l
Capital S or T The special bit is set but the execute bit under it is not chmod 4644 f
Octal Fourth digit in front: 4755, 2770, 1777 stat -c %a f
Real vs effective UID Real is who started the process; effective is used for permission checks /proc/<pid>/status
Scripts Linux ignores SUID and SGID on interpreted scripts ls -l script
chown Clears SUID and SGID on the file ls -l
nosuid mount option Disables SUID and SGID for the whole filesystem findmnt -no OPTIONS <mount>
Audit find / -perm -4000 and -perm -2000 sudo find / -xdev -perm /6000

ACL

Fact Value Verify with
Show getfacl <path> getfacl f
Add or change setfacl -m u:<user>:<perms> or g:<group>:<perms> setfacl -m u:amor:r f
Remove setfacl -x u:<user> (one entry), setfacl -b (all) setfacl -x u:amor f
Marker in ls -l A + after the mode ls -l
Mask Upper limit for named users, named groups and the owning group getfacl f
chmod on group bits Changes the mask, not the owning group entry, once an ACL exists chmod 600 f; getfacl f
Default ACL setfacl -d -m ... on a directory; inherited by new files and subdirectories getfacl dir
Recursive setfacl -R; capital X adds execute to directories only setfacl -R -m g:ops:rX dir
Backup and restore getfacl -R dir > file; setfacl --restore=file getfacl -R
Copies cp drops ACLs; cp -a, rsync -A, tar --acls keep them ls -l

File Attributes

Fact Value Verify with
Show lsattr <file>; lsattr -d <dir> for the directory itself lsattr f
Set and clear chattr +i, chattr -i (root or CAP_LINUX_IMMUTABLE only) sudo chattr +i f
i immutable No write, delete, rename, link or chmod, even for root lsattr f
a append-only Opens for append only; no truncate, overwrite or delete lsattr log
i on a directory No entries created, removed or renamed; existing files stay writable lsattr -d dir
e Extents in use (ext4); informational lsattr
Error text Operation not permitted (EPERM), not Permission denied strace
Package e2fsprogs; works on ext4, XFS and btrfs, not on /proc rpm -qf /usr/bin/chattr
Not shown by ls -l Mode bits look normal ls -l

Package Management

Packaging Concepts

Fact Value Verify with
RPM file name name-version-release.arch.rpm (NEVRA with an optional epoch) rpm -qp --qf '%{NEVRA}' f.rpm
DEB file name name_version-revision_arch.deb dpkg -I f.deb
Epoch Integer that overrides version comparison (2:1.26.3) rpm -q --qf '%{EPOCH}'
Release or revision Packaging build number; el10_2 and ubuntu13.18 mark the distribution rpm -q bash
Architectures x86_64/amd64, aarch64/arm64, noarch/all uname -m
Low-level vs high-level rpm, dpkg handle files; dnf, apt add repositories and dependency resolution dnf history
Dependencies Declared as package names, files or library sonames (libc.so.6()(64bit)) rpm -q --requires
Scriptlets Pre and post install and remove scripts run as root rpm -q --scripts
Signatures Packages or repository metadata are signed with GPG keys; managers refuse unsigned or unknown-key content rpm -K f.rpm
Trusted keys RPM: gpg-pubkey pseudo-packages; APT: keyring files referenced by Signed-By rpm -q gpg-pubkey
Configuration files Tracked specially; upgrades keep local changes (.rpmnew, dpkg prompt) rpm -qc, dpkg -s
Source packages .src.rpm and .dsc build binary packages rpm -qi (Source RPM)

rpm and dnf

Fact Value Verify with
Is it installed rpm -q <pkg> rpm -q bash
Package details rpm -qi rpm -qi openssh-server
Files, configs, docs rpm -ql, -qc, -qd rpm -qc openssh-server
Which package owns a file rpm -qf <path> rpm -qf /usr/bin/ls
Which package would provide a file dnf provides '*/<name>' dnf provides /usr/bin/dig
Verify installed files rpm -V (size, mode, digest, owner, mtime flags) sudo rpm -V openssh-server
Check a downloaded package rpm -K (signatures), rpm -qpl (files) rpm -K pkg.rpm
Install, remove, update dnf install, dnf remove, dnf upgrade dnf history
Undo a transaction dnf history undo <id> or last dnf history list
Updates available dnf check-update exits 100 echo $?
Security errata dnf updateinfo, dnf upgrade --security dnf updateinfo summary
Pin a version dnf versionlock add <pkg> (plugin) or exclude= in dnf.conf dnf versionlock list
Parallel kernels installonly_limit=3 keeps three grep installonly /etc/dnf/dnf.conf
Scriptlets and other queries rpm -q --scripts, --whatrequires; rpm -qp... on a package file rpm -q --scripts <pkg>
Backported fixes Changelog lists CVEs fixed in an old upstream version rpm -q --changelog <pkg>

dpkg and apt

Fact Value Verify with
Installed packages dpkg -l (ii = installed) dpkg -l bash
Status and dependencies dpkg -s <pkg> dpkg -s openssh-server
Files of a package dpkg -L <pkg> dpkg -L openssh-server
Owner of a file dpkg -S <path> dpkg -S /usr/sbin/sshd
Package for a file not installed apt-file search (package apt-file) apt-file search -x '/usr/bin/dig$'
Refresh indexes apt update; install and upgrade never refresh by themselves apt update
Remove vs purge remove keeps configuration files (rc); purge deletes them dpkg -l <pkg>
Unused dependencies apt autoremove apt autoremove --dry-run
Candidate version and origin apt-cache policy <pkg> apt-cache policy nginx
Hold a version apt-mark hold <pkg> apt-mark showhold
Verify files dpkg --verify, debsums -c (-e for config files) sudo debsums -ce openssh-client
History /var/log/apt/history.log, /var/log/dpkg.log tail /var/log/apt/history.log
Scripts Use apt-get; apt warns that its CLI is not stable and waits on locks apt-get -y install

Repositories

Fact Value Verify with
RHEL definitions /etc/yum.repos.d/*.repo (baseurl or mirrorlist, gpgcheck, gpgkey); a local repository is a createrepo_c directory with baseurl=file:///<dir> dnf repolist --all
Ubuntu definitions /etc/apt/sources.list.d/*.sources (deb822: Types, URIs, Suites, Components, Signed-By; default on 24.04) or *.list cat /etc/apt/sources.list.d/ubuntu.sources
Keys RPM imports into its database; APT uses keyring files in /etc/apt/keyrings/ via Signed-By rpm -q gpg-pubkey
Extra RHEL repositories CRB (build dependencies) and EPEL (community packages), enabled with dnf config-manager --set-enabled <id>; Red Hat repositories need subscription-manager register (not on Rocky) dnf repolist
Unreachable repository dnf stops with Failed to download metadata; apt update warns and continues dnf makecache

Flatpak and Snap

Fact Value Verify with
Flatpak remotes Sources such as Flathub; system-wide or per user flatpak remotes
Install, update, remove flatpak remote-add, install, update, uninstall; apps pull shared runtimes flatpak list
Scope --system (default for root, /var/lib/flatpak) or --user (~/.local/share/flatpak) flatpak list --columns=installation
Clean up flatpak uninstall --unused removes orphaned runtimes du -sh /var/lib/flatpak
Snap Squashfs images mounted under /snap, managed by snapd snap list
Snap updates Automatic by default; snap refresh --hold pauses them snap refresh --list

Shared Libraries

Fact Value Verify with
Loader /lib64/ld-linux-x86-64.so.2, named in the ELF header file <binary>
Needed libraries NEEDED entries in the dynamic section readelf -d <binary>
Resolved paths ldd shows where each library is found ldd <binary>
Search order LD_LIBRARY_PATH, the binary's RUNPATH, /etc/ld.so.cache, then the default directories; a legacy RPATH is searched before LD_LIBRARY_PATH LD_DEBUG=libs <binary>
Cache ldconfig builds /etc/ld.so.cache from /etc/ld.so.conf.d/*.conf ldconfig -p
soname Name a library promises to stay compatible under (libgreet.so.1) readelf -d lib.so
Symlinks libx.so.1.0 real file; libx.so.1 for programs (soname); libx.so for the compiler (-dev or -devel packages) ls -l
RUNPATH Directories stored in the binary; $ORIGIN means the binary's own directory patchelf --set-rpath
LD_PRELOAD Loads a library first, overriding symbols; ignored for SUID programs LD_PRELOAD=... cmd
Symbol versions GLIBC_2.34 in a binary sets the minimum glibc objdump -T <binary>
Static binaries Contain their libraries; ldd prints not a dynamic executable ldd <binary>

Other Install Methods

Fact Value Verify with
Release binaries Single files in /usr/local/bin (install -m 0755 after a checksum check); bundles in /opt/<app> command -v kubectl
Source builds ./configure && make && sudo make install, default prefix /usr/local ./configure --help
Untracked files Nothing in /usr/local belongs to a package dpkg -S, rpm -qf
Alternatives One command name, several implementations: update-alternatives (Debian), alternatives (RHEL); --display, --config, --set update-alternatives --query editor
Python on Ubuntu 24.04 System pip install refused (PEP 668, EXTERNALLY-MANAGED); use a venv or pipx ls /usr/lib/python3*/EXTERNALLY-MANAGED
Python on Rocky 10.2 No EXTERNALLY-MANAGED marker; pip install --user works, root pip warns pip3 --version
Unpack an RPM without installing rpm2cpio output piped into cpio -idm; dpkg-deb -x for a .deb find . -type f

Processes

Process Fundamentals

Fact Value Verify with
Program vs process A program is a file on disk; a process is a running instance with its own PID and memory ps -C sleep
PID Unique number for a running process; reused after the process is reaped echo $$
PPID PID of the parent that created the process ps -o pid,ppid -p $$
PID 1 systemd on RHEL and Ubuntu; adopts orphans and cannot be killed by a stray signal ps -p 1 -o comm
PID 2 kthreadd, parent of every kernel thread (shown in [brackets]) ps -o ppid,comm --ppid 2
PID limit kernel.pid_max, 4194304 on 64-bit systems with systemd cat /proc/sys/kernel/pid_max
Thread A task that shares memory and file descriptors with its process; has its own TID ps -T -p <pid>
Thread count NLWP column, Threads: in /proc/<pid>/status ps -o nlwp -p <pid>
$$ vs $BASHPID $$ stays the script's PID in subshells; $BASHPID is the current process ( echo $$ $BASHPID )
/proc/<pid>/ Kernel view of one process: status, cmdline, exe, cwd, fd/, environ, maps ls /proc/$$
Other users' processes cwd, exe, fd/ and environ are readable only by the owner or root ls -l /proc/1/cwd
Process tree pstree -p, ps -ef --forest, systemd-cgls pstree -p 1
Process credentials Real, effective, saved and filesystem UID and GID, stored per process grep Uid /proc/$$/status

Process Lifecycle

Fact Value Verify with
fork() Creates a copy of the calling process; returns the child PID to the parent and 0 to the child strace -f -e trace=clone bash -c 'ls; true'
execve() Replaces the program in the current process; the PID stays the same bash -c 'echo $$; exec bash -c "echo \$\$"'
Copy-on-write Parent and child share memory pages until one writes; fork() copies page tables only /proc/<pid>/smaps_rollup
exit() Frees memory and files; the process becomes a zombie holding its exit status ps -o stat shows Z
wait() Parent reads the exit status and the kernel removes the zombie (reaping) strace -e trace=wait4
SIGCHLD Sent to the parent when a child exits or stops strace shows --- SIGCHLD
Zombie Exited, not yet reaped; uses no memory, only a process table entry ps -o pid,ppid,stat
Orphan Parent exited first; adopted by PID 1 or the nearest subreaper ps -o ppid shows 1
Exit status 0 to 255; 128+N means killed by signal N echo $?
Process group Jobs of one pipeline; signals from the terminal go to the foreground group ps -o pgid
Session Process groups under one login; the leader owns the controlling terminal ps -o sid,tty
Daemon Background process with no controlling terminal (TTY ?), usually managed by systemd ps -o tty -p <pid>
setsid Starts a program in a new session with no terminal setsid sleep 60

Viewing Processes

Fact Value Verify with
ps aux BSD syntax: all processes, user-oriented columns, %CPU and %MEM ps aux
ps -ef UNIX syntax: all processes with PPID and full command ps -ef
Custom columns -o pid,ppid,user,stat,%cpu,rss,etime,cmd; = after a name removes the header ps -o pid= -p 1
Sorting --sort=-%cpu, --sort=-rss ps -eo pid,rss,comm --sort=-rss
Select by name -C nginx matches the command name exactly ps -C nginx -o pid,args
Tree view ps -ef --forest, pstree -p pstree -p <pid>
%CPU in ps CPU time divided by lifetime, not current usage top for current usage
RSS vs VSZ Resident memory in KiB vs virtual address space size ps -o rss,vsz
pgrep / pkill Match the name (15 chars); -f matches the full command line; exit 1 when nothing matches pgrep -a nginx
pidof PIDs of an exact program name, space-separated pidof nginx
top batch mode top -b -n 1 prints one screen for scripts and tickets top -b -n 1
top keys P CPU, M memory, 1 per-CPU, H threads, c full command, k kill inside top
Load average Runnable plus uninterruptible (D) tasks, averaged over 1, 5 and 15 minutes uptime
watch Reruns a command every 2 seconds (-n to change, -d to highlight changes) watch -n 1 'ps -C nginx'

Process States

Fact Value Verify with
R Running or runnable (waiting in the CPU run queue) ps -o stat -p <pid>
S Interruptible sleep: waiting for an event, wakes on a signal ps -o stat,wchan
D Uninterruptible sleep: waiting inside the kernel, usually for I/O; signals wait ps -eo stat,pid,wchan
T Stopped by SIGSTOP or SIGTSTP (Ctrl-Z) kill -STOP <pid>
t Stopped by a debugger or tracer gdb -p <pid>
Z Zombie: exited, waiting for the parent to reap it ps -o stat,ppid
I Idle kernel thread (not counted in load average) ps -eo stat,comm
X Dead; never seen in practice man ps
Flags s session leader, l multithreaded, + foreground group, < high priority, N low priority ps aux
Load average Counts R and D tasks cat /proc/loadavg
wchan Kernel function where a sleeping process waits ps -o wchan:30 -p <pid>
Kernel stack Full blocked call path (root only) sudo cat /proc/<pid>/stack
SIGKILL and D The signal stays pending until the kernel call returns grep ShdPnd /proc/<pid>/status

Signals

Fact Value Verify with
SIGHUP (1) Terminal hung up; daemons use it as "reload configuration" kill -HUP <pid>
SIGINT (2) Ctrl-C kill -INT <pid>
SIGQUIT (3) Ctrl-\; terminate with a core dump kill -QUIT <pid>
SIGKILL (9) Terminate immediately; cannot be caught, blocked or ignored kill -9 <pid>
SIGTERM (15) Polite termination; the default of kill, pkill and systemctl stop kill <pid>
SIGSTOP (19) / SIGCONT (18) Stop (uncatchable) and resume kill -STOP <pid>
SIGTSTP (20) Ctrl-Z; a catchable stop kill -TSTP <pid>
SIGCHLD (17) A child exited or stopped strace -e signal=SIGCHLD
SIGSEGV (11) / SIGPIPE (13) Invalid memory access / write to a pipe with no reader dmesg, exit code 141
SIGUSR1 (10), SIGUSR2 (12) Application-defined (log reopen, debug toggle) man 7 signal
Exit code 128 + signal number: 130 INT, 137 KILL, 141 PIPE, 143 TERM kill -l 137
kill -0 Sends nothing; tests existence and permission kill -0 <pid>; echo $?
Permission A user can signal only processes with the same real or effective UID; root can signal any kill 1 as a user
Masks SigBlk, SigIgn, SigCgt, ShdPnd in /proc/<pid>/status (bit N-1 = signal N) grep Sig /proc/<pid>/status
trap Shell handler: trap 'cmd' TERM; trap '' INT ignores trap -p

Job Control

Fact Value Verify with
cmd & Runs in the background; the shell prints [job] PID sleep 60 &
jobs -l Lists the shell's jobs with PIDs; + is current, - is previous jobs -l
Ctrl-Z Sends SIGTSTP; the job stops jobs shows Stopped
bg %n / fg %n Continue a job in the background / bring it to the foreground bg %1
Ctrl-C Sends SIGINT to the foreground process group; exit status 130 echo $?
Ctrl-D End of input (EOF), not a signal; closes the shell at an empty prompt cat then Ctrl-D
Job specs %1, %+, %-, %sleep (name prefix) kill %1
Terminal hangup The session leader gets SIGHUP; bash sends SIGHUP to all its jobs close the terminal
nohup Ignores SIGHUP; writes output to nohup.out if stdout is a terminal nohup cmd &
disown Removes a job from the shell's table, so bash does not send it SIGHUP disown %1
setsid Starts a command in a new session with no terminal setsid cmd
huponexit If set, bash sends SIGHUP to jobs on a normal exit too (off by default) shopt huponexit
tmux / screen Keep a whole terminal session alive on the server across disconnects tmux attach

Priority and Nice

Fact Value Verify with
Nice range -20 (highest priority) to 19 (lowest); default 0 nice
Start with a nice value nice -n 10 cmd; the default increment is 10 nice -n 10 nice
Change a running process renice -n 5 -p <pid>; also -u user, -g pgrp ps -o ni -p <pid>
Unprivileged users May only raise the nice value (lower priority) of their own processes renice -n 2 after 5 fails
Negative nice Needs root, CAP_SYS_NICE, or a nice limit in limits.conf ulimit -e
Weight Each nice step changes CPU share by about 10% (weight ratio 1.25) two CPU hogs on one core
PR in top 20 + nice for normal tasks; rt or negative for real-time top
I/O class ionice -c 1 realtime, -c 2 -n 0..7 best-effort, -c 3 idle ionice -p <pid>
I/O priority effect Honored by the BFQ scheduler; mq-deadline and none mostly ignore it cat /sys/block/<dev>/queue/scheduler
Real-time policies SCHED_FIFO and SCHED_RR, priority 1 to 99, root only chrt -p <pid>
Scheduler CFS up to Linux 6.5; EEVDF from 6.6 (RHEL 10 ships 6.12) uname -r
Autogroup When enabled, the scheduler groups tasks by session, so nice works only within a session cat /proc/sys/kernel/sched_autogroup_enabled
systemd Nice=, CPUWeight=, IOSchedulingClass=, IOWeight= in a unit systemctl show -p Nice <unit>

System Calls and Tracing

Fact Value Verify with
System call Controlled entry from user mode into the kernel (the syscall instruction on x86_64) strace true
Return value -1 plus errno on failure; strace prints the symbolic name strace cat /nonexistent
Common errno ENOENT 2, EACCES 13, EPERM 1, EAGAIN 11, EMFILE 24, ENOSPC 28, ECONNREFUSED 111 python3 -c 'import os; print(os.strerror(13))'
EACCES vs EPERM Permission bits or ACL denied vs operation not allowed for this identity (ownership, capability) strace output
vDSO Kernel code mapped into every process so time calls avoid a real system call grep vdso /proc/self/maps
strace -f Follow children and threads strace -f bash -c 'ls; true'
strace -e trace= Filter by name or class: %file, %network, %process, %memory strace -e trace=%file
strace -p <pid> Attach to a running process; needs the same user or root timeout 5 strace -p <pid>
strace -c Count calls, errors and time per call strace -c ls
Useful flags -o file, -tt timestamps, -T time in call, -y file names for fds, -s 200 longer strings, -P path man strace
Overhead strace stops the target on every traced call; it can slow a busy service many times over strace -c on a hot path
ltrace Traces calls into shared libraries (malloc, getenv) ltrace -c ls
Stack of a hung process /proc/<pid>/wchan, /proc/<pid>/stack (root), gdb -p or gstack for user space sudo cat /proc/<pid>/stack
Ptrace restrictions Yama kernel.yama.ptrace_scope=1 on Ubuntu allows tracing only of children without root sysctl kernel.yama.ptrace_scope

Systemd and Services

Init and Targets

Fact Value Verify with
Init on RHEL and Ubuntu systemd; /sbin/init is a symlink to it ls -l /sbin/init
SysV init Ran /etc/rc.d/rcN.d scripts one after another; replaced in RHEL 7 and Ubuntu 15.04 man systemd-sysv-generator
Target A unit that groups other units into a system state systemctl list-units --type=target
Default target /etc/systemd/system/default.target symlink systemctl get-default
Server default multi-user.target (runlevel 3); desktops use graphical.target (runlevel 5) systemctl get-default
Change the default systemctl set-default multi-user.target (takes effect at next boot) ls -l /etc/systemd/system/default.target
Switch now systemctl isolate multi-user.target; only units with AllowIsolate=yes systemctl show -p AllowIsolate <target>
Rescue / emergency rescue.target (local filesystems, root shell) / emergency.target (root filesystem only, no services) systemctl cat rescue.target
Boot into a target once Kernel parameter systemd.unit=rescue.target cat /proc/cmdline
Current runlevel runlevel, who -r (compatibility) runlevel
Power commands poweroff, reboot, halt, shutdown are symlinks to systemctl ls -l /usr/sbin/reboot
Scheduled shutdown shutdown -r +30 "msg"; shutdown -c cancels; --show lists it shutdown --show
System health systemctl is-system-running: running, degraded (a unit failed), starting systemctl --failed
Boot time systemd-analyze systemd-analyze blame

systemctl

Fact Value Verify with
start / stop / restart Change the running state now systemctl is-active <unit>
reload Ask the service to reread its configuration (ExecReload=); the main PID stays systemctl show -p MainPID
enable / disable Create or remove [Install] symlinks; affects the next boot only systemctl is-enabled <unit>
enable --now Enable and start in one command systemctl status <unit>
mask / unmask Link the unit to /dev/null so nothing can start it systemctl is-enabled shows masked
is-active exit codes 0 active, 3 inactive or failed systemctl is-active <unit>; echo $?
status exit codes 0 active, 3 not running, 4 no such unit systemctl status <unit>; echo $?
Enablement states enabled, disabled, static (no [Install]), masked, alias, indirect systemctl list-unit-files
Preset Vendor default for enable: RHEL disables most new services, Ubuntu enables and starts them on install systemctl list-unit-files <unit>
Failed units systemctl --failed; clear with systemctl reset-failed systemctl is-failed <unit>
Reading config systemctl cat <unit> prints the unit and its drop-ins systemctl cat sshd
Properties systemctl show <unit> -p MainPID -p Restart -p NRestarts systemctl show <unit>
After editing units systemctl daemon-reload Warning "changed on disk"
Without root Read commands work; changes need sudo ("Interactive authentication required") systemctl start <unit> as a user
User units systemctl --user manages the per-user manager systemctl --user status

Unit Files

Fact Value Verify with
Unit types .service, .socket, .timer, .target, .mount, .automount, .path, .slice, .scope, .device, .swap systemctl -t help
Sections [Unit] (description, dependencies), [Service] / [Socket] / ... (type-specific), [Install] (enable) systemctl cat <unit>
Search order /etc/systemd/system overrides /run/systemd/system, which overrides /usr/lib/systemd/system systemd-analyze unit-paths
Vendor units /usr/lib/systemd/system (packages); never edit them systemctl show -p FragmentPath
Drop-ins /etc/systemd/system/<unit>.d/*.conf change single settings systemctl show -p DropInPaths
systemctl edit <unit> Creates override.conf and reloads; --full copies the whole unit systemctl cat <unit>
List-valued settings ExecStart= with an empty value resets the list before a new value systemctl cat <unit>
daemon-reload Required after any file change; otherwise systemd warns "changed on disk" systemctl status <unit>
Type= simple (default), exec, forking, oneshot, notify, dbus, idle systemctl show -p Type <unit>
Restart= no (default), on-failure, always, on-abnormal; RestartSec= delay systemctl show -p NRestarts <unit>
Start limit StartLimitBurst=5 in StartLimitIntervalSec=10s by default, then "Start request repeated too quickly" systemctl reset-failed <unit>
Wants= / Requires= Pull in another unit; Requires= also fails or stops with it systemctl list-dependencies <unit>
After= / Before= Ordering only; without them, units start in parallel systemctl show -p After <unit>
network-online.target Needs Wants= and After= to wait for configured networking systemctl show -p WantedBy network-online.target
Check a file systemd-analyze verify <file> systemd-analyze verify <file>
Overrides report systemd-delta lists overridden and extended units systemd-delta --type=extended

Writing a Service

Fact Value Verify with
Location /etc/systemd/system/<name>.service systemctl cat <name>
Minimum [Service] with ExecStart= and an absolute path; [Install] to enable it systemd-analyze verify <file>
Foreground The program must not daemonize under Type=exec or simple systemctl status
Logging stdout and stderr go to the journal journalctl -u <name>
Identity User=, Group=; DynamicUser=yes creates a transient user ps -o user -p <pid>
Configuration Environment=KEY=value, EnvironmentFile=/etc/<name>/<name>.env (- prefix makes it optional) systemctl show -p Environment
Writable state StateDirectory=, RuntimeDirectory=, LogsDirectory=, created and owned for the service user ls -l /var/lib/<name>
Sandboxing ProtectSystem=strict, ProtectHome=yes, PrivateTmp=yes, NoNewPrivileges=yes systemd-analyze security <name>
Resource limits MemoryMax=, CPUQuota=, TasksMax=, LimitNOFILE= systemctl show -p MemoryMax
Exposure score 0 (sandboxed) to 10 (unrestricted) systemd-analyze security
Exit code 203 EXEC: the binary could not be executed systemctl status
Test a setting systemd-run -P -p <Setting>=<value> <cmd> runs a command as a transient unit systemd-run -P -q true

Systemd Toolbox

Fact Value Verify with
Boot time systemd-analyze, blame, critical-chain <unit> systemd-analyze blame
Calendar syntax check systemd-analyze calendar "<expr>" systemd-analyze calendar daily
Sessions and users loginctl list-sessions, show-user, terminate-session loginctl list-users
Lingering loginctl enable-linger <user> keeps user services running without a login ls /var/lib/systemd/linger
Transient units systemd-run runs a command as a service, scope or timer systemd-run --unit=<name> <cmd>
cgroup tree systemd-cgls; live usage with systemd-cgtop systemd-cgls -u <unit>
Temporary files tmpfiles.d rules create and clean paths; systemd-tmpfiles --create / --clean cat /usr/lib/tmpfiles.d/tmp.conf
Escaping names systemd-escape --path turns a path into a unit name systemd-escape --path /mnt/backup
Logging from scripts systemd-cat -t <tag> sends output to the journal journalctl -t <tag>
Other tools hostnamectl, timedatectl, localectl, resolvectl, networkctl hostnamectl

Logging

Log Locations

Fact Value Verify with
Two log paths journald receives almost everything; rsyslog copies it into text files systemctl is-active systemd-journald rsyslog
General log RHEL /var/log/messages, Ubuntu /var/log/syslog ls /var/log
Authentication log RHEL /var/log/secure, Ubuntu /var/log/auth.log sudo tail /var/log/secure
Cron log RHEL /var/log/cron; Ubuntu writes cron lines to syslog journalctl -u crond or -u cron
Kernel log dmesg, journalctl -k; Ubuntu also /var/log/kern.log journalctl -k -n 5
Package logs RHEL /var/log/dnf.log, dnf.rpm.log; Ubuntu /var/log/dpkg.log, /var/log/apt/history.log ls /var/log/apt
Login records (binary) wtmp (last), btmp (lastb), lastlog last -n 3
Application logs Own directory, for example /var/log/nginx/ ls /var/log/nginx
Journal storage /var/log/journal (persistent) or /run/log/journal (lost at reboot) journalctl --header (the File path line)
Who can read RHEL files are root only (0600); Ubuntu files are group adm (0640) ls -l /var/log/messages
Journal readers Members of adm, systemd-journal and (RHEL) wheel see all entries id
Syslog socket /dev/log is a symlink to journald's socket ls -l /dev/log

journalctl

Fact Value Verify with
By unit -u nginx (repeatable) journalctl -u nginx -n 5
Follow -f, like tail -f journalctl -fu nginx
By boot -b current, -b -1 previous; list with --list-boots journalctl --list-boots
By priority -p err means err and worse; ranges with -p warning..err journalctl -p err -b
Kernel only -k (implies the current boot unless -b is given) journalctl -k -n 5
By time --since "1 hour ago", --until "2026-09-17 05:48", -S/-U journalctl --since today
Text search -g <regex> (--grep) journalctl -g "bad command"
By field _PID=, _COMM=, _UID=, SYSLOG_IDENTIFIER= (-t) journalctl -F _SYSTEMD_UNIT
Output modes short-iso, short-precise, cat, verbose, json, json-pretty journalctl -o json -n 1
Storage Storage=auto: persistent only if /var/log/journal exists journalctl --header (the File path line)
Size limit SystemMaxUse= (default 10% of the filesystem, capped at 4G) journalctl --disk-usage
Cleanup --vacuum-size=, --vacuum-time=, --vacuum-files= journalctl --vacuum-time=2d
Config /etc/systemd/journald.conf.d/*.conf; restart systemd-journald systemd-analyze cat-config systemd/journald.conf
Rate limit Per service, RateLimitBurst= in RateLimitIntervalSec=; drops are logged as "Suppressed N messages" journalctl -u systemd-journald
Write to it logger, systemd-cat, or stdout of any service logger -t test hello

rsyslog

Fact Value Verify with
Config /etc/rsyslog.conf, then /etc/rsyslog.d/*.conf grep include /etc/rsyslog.conf
Selector facility.priority means that priority and higher; .= exactly; .none excludes grep authpriv /etc/rsyslog.conf
Facilities kern, user, mail, daemon, auth, authpriv, cron, local0 to local7 man 3 syslog
Stop processing & stop after a rule, or stop inside if cat /etc/rsyslog.d/*.conf
Validate rsyslogd -N1 (exit 1 on errors) rsyslogd -N1
Forward over UDP / TCP *.* @host:514 / *.* @@host:514 Rule in /etc/rsyslog.d/
Receive module(load="imtcp") and input(type="imtcp" port="514") ss -tlnp "sport = :514"
Property filters :msg, contains, "segfault" /var/log/crashes.log matches text instead of facility logger -t test segfault
Test messages logger -p local3.err -t app "text"; remote: logger -n host -P 514 -T tail /var/log/messages
Leading - on a file Ubuntu syntax for "do not sync after each line" /etc/rsyslog.d/50-default.conf

logrotate

Fact Value Verify with
Config /etc/logrotate.conf (globals), /etc/logrotate.d/<app> (per log) cat /etc/logrotate.conf
Schedule logrotate.timer, daily on both families (cron.daily on older systems) systemctl list-timers logrotate.timer
State file Last rotation per file: RHEL /var/lib/logrotate/logrotate.status, Ubuntu /var/lib/logrotate/status cat the file
Dry run logrotate -d <conf> changes nothing logrotate -d /etc/logrotate.conf
Force logrotate -f <conf> rotates even if not due logrotate -v -f /etc/logrotate.d/app
Frequency daily, weekly, monthly, yearly, or size 100M; maxsize combines both man logrotate.conf
Keep rotate 7 keeps seven old files ls /var/log/app
Naming Numbered (app.log.1) or dated with dateext (RHEL default) ls /var/log
create Rename the file, create a new empty one; the program must reopen postrotate block
copytruncate Copy then truncate in place; the program keeps its descriptor; lines written between copy and truncate are lost ls -l /proc/<pid>/fd
compress / delaycompress gzip old files; skip the newest one ls *.gz
postrotate ... endscript Command after rotation, usually a reload or kill -USR1 cat /etc/logrotate.d/nginx
sharedscripts Run postrotate once for all files that matched cat /etc/logrotate.d/nginx
su user group Rotate as that user; required when the directory is group- or world-writable logrotate -d warning

Log Parsing Recipes

Fact Value Verify with
Count by field awk '{print $N}', then sort, uniq -c, sort -rn, head Top client IPs
nginx combined format fields $1 client, $4 time, $6 method (with a quote), $7 path, $9 status, $10 bytes head -1 access.log
Split on quotes awk -F'"' '{print $6}' gives the user agent Top user agents
Numeric filter awk '$9 >= 500' 5xx lines
Time window awk '$4 >= "[17/Sep/2026:05:49" && $4 < "[17/Sep/2026:05:50"' (same day only) Requests in one minute
Per minute awk '{print substr($4, 2, 17)}', then uniq -c Traffic shape
Sum and average awk '{s += $10} END {print s/NR}' Bytes per request
Only the match grep -o / grep -oP 'user \K\S+' Invalid usernames
Live filtering tail -f into grep --line-buffered Watching 5xx
Compressed rotations zgrep, or zcat file.gz into the same pipeline Older days
Journal as input journalctl -u ssh -o cat, or -o json into jq -r .MESSAGE Same pipelines, no files
Sort order sort -rn numeric descending; sort -k4 -rn by column 4 Error-rate table

Scheduling

cron and at

Fact Value Verify with
Field order minute, hour, day of month, month, day of week, command cat /etc/crontab
Ranges and steps 1-5, 1,15, */10; day of week 0 and 7 are Sunday man 5 crontab
Day fields If both day of month and day of week are set, either match runs the job man 5 crontab
Shortcuts @reboot, @hourly, @daily, @weekly, @monthly, @yearly man 5 crontab
User crontabs crontab -e, -l, -r, -u user; stored in /var/spool/cron/ (RHEL) or /var/spool/cron/crontabs/ (Ubuntu) sudo ls /var/spool/cron
System crontabs /etc/crontab and /etc/cron.d/* have a user field after the schedule cat /etc/cron.d/0hourly
Periodic directories /etc/cron.hourly, daily, weekly, monthly, run by run-parts ls /etc/cron.daily
anacron Runs daily, weekly and monthly jobs missed while the host was off cat /etc/anacrontab
Environment SHELL=/bin/sh, minimal PATH, HOME of the user, no profile files * * * * * env > /tmp/cron-env.txt
% Means newline in a crontab command; write \% date +\%F
Output Mailed to MAILTO (default the owner); without an MTA it is logged or discarded journalctl -u crond
Access cron.allow wins if present, otherwise cron.deny; same model for at.allow and at.deny ls /etc/cron.*
Daemon crond (cronie) on RHEL, cron on Ubuntu; reloads crontabs automatically systemctl status crond
Logs RHEL /var/log/cron; Ubuntu syslog, both in the journal journalctl -u cron
at / batch One-off job at a time / when load is low; atq, atrm, at -c atq

Systemd Timers

Fact Value Verify with
Pairing name.timer starts name.service unless Unit= says otherwise systemctl cat name.timer
Calendar OnCalendar=Mon..Fri *-*-* 09:00:00; shortcuts hourly, daily, weekly systemd-analyze calendar "..."
Monotonic OnBootSec=, OnUnitActiveSec=, OnActiveSec= (relative to boot, last run, timer start) man systemd.timer
Missed runs Persistent=true runs a missed calendar job at the next boot ls /var/lib/systemd/timers
Spread load RandomizedDelaySec= adds a random delay systemctl list-timers
Precision AccuracySec= (default 1 minute) lets systemd batch wake-ups systemctl show -p AccuracyUSec x.timer
Enable Enable and start the timer, not the service systemctl enable --now name.timer
Service type Usually Type=oneshot; no [Install] needed in the service systemctl status name.service
Run now systemctl start name.service journalctl -u name
List systemctl list-timers --all shows next and last run systemctl list-timers
One-off systemd-run --on-active=30s or --on-calendar= creates a transient timer systemd-run --on-active=1m /bin/true
User timers ~/.config/systemd/user/, systemctl --user, lingering for no-login runs loginctl show-user $USER -p Linger

Kernel and Hardware

proc and sys

Fact Value Verify with
/proc procfs: one directory per PID plus system-wide files mount -t proc
/proc/sys Writable kernel parameters, managed by sysctl ls /proc/sys
/sys sysfs: the device and driver model, one value per file ls /sys/class/net
/dev devtmpfs: device nodes, populated by the kernel and udev mount -t devtmpfs
/run, /dev/shm tmpfs: memory-backed, cleared at reboot df -h -t tmpfs
File sizes Most files show size 0 (procfs) or 4096 (sysfs); content is generated on read ls -l /proc/meminfo
Memory /proc/meminfo (free reads it) grep MemAvailable /proc/meminfo
CPU /proc/cpuinfo, /proc/loadavg, /proc/stat nproc
Kernel /proc/version, /proc/cmdline, /proc/modules, /proc/config.gz if built in uname -r
Storage /proc/partitions, /proc/mounts, /sys/block/<dev>/ lsblk
Network /proc/net/tcp, /sys/class/net/<if>/ ss, ip link
Writing Needs root for most files; sudo echo x > file fails because the shell opens the file sudo tee file with the value on standard input
Persistence Writes last until reboot; persistent settings go in sysctl.d or udev rules sysctl --system
Per-process files /proc/<pid>/status, cmdline, environ, fd/, limits, maps, cgroup cat /proc/self/status

sysctl

Fact Value Verify with
Name mapping net.ipv4.ip_forward is /proc/sys/net/ipv4/ip_forward cat /proc/sys/net/ipv4/ip_forward
Read sysctl key, sysctl -a, sysctl -a --pattern <regex> sysctl vm.swappiness
Write now sysctl -w key=value (root; lost at reboot) sysctl key
Persistent A file in /etc/sysctl.d/ named NN-name.conf, then sysctl --system sysctl --system
Load one file sysctl -p <file>; sysctl -p alone reads /etc/sysctl.conf sysctl -p /etc/sysctl.d/90-app.conf
Directories /etc/sysctl.d, /run/sysctl.d, /usr/lib/sysctl.d; a file in /etc overrides one with the same name systemd-analyze cat-config sysctl.d/99-sysctl.conf
Order All files sorted by name; for the same key, the last file read wins sysctl --system
/etc/sysctl.conf Symlinked as /etc/sysctl.d/99-sysctl.conf on both families ls -l /etc/sysctl.d/99-sysctl.conf
Boot systemd-sysctl.service applies the files systemctl status systemd-sysctl
Module keys Keys appear only after their module loads (net.sctp.*, net.bridge.*) sysctl net.sctp.rto_min
Namespaces Most net.* keys are per network namespace; containers can have their own ip netns exec x sysctl net.ipv4.ip_forward
Routing net.ipv4.ip_forward = 1 for routers, NAT, Kubernetes nodes sysctl net.ipv4.ip_forward
Memory vm.swappiness, vm.overcommit_memory, vm.max_map_count, vm.dirty_ratio sysctl -a --pattern ^vm
Files and processes fs.file-max, fs.inotify.max_user_watches, kernel.pid_max sysctl fs.file-nr

Kernel Modules

Fact Value Verify with
Location /lib/modules/$(uname -r)/kernel/**.ko (often .ko.xz or .ko.zst) find /lib/modules/$(uname -r) -name "*.ko*"
List loaded lsmod (reads /proc/modules): name, size, use count, users lsmod
Details modinfo <name>: file, license, dependencies, signature, parameters modinfo -p nbd
Load modprobe <name> resolves dependencies from modules.dep; insmod needs a path and resolves nothing modprobe -v <name>
Unload modprobe -r (with unused dependencies) or rmmod; fails while in use lsmod use count
Built-in Code compiled into the kernel image; cannot be unloaded; listed in modules.builtin modinfo loop shows (builtin)
Parameters modprobe nbd nbds_max=4, persistently options nbd nbds_max=4 in /etc/modprobe.d/*.conf cat /sys/module/nbd/parameters/nbds_max
Blacklist blacklist <name> stops automatic loading only modprobe -c
Block completely install <name> /bin/false makes every load fail modprobe <name>
Load at boot One name per line in /etc/modules-load.d/*.conf, read by systemd-modules-load.service systemctl status systemd-modules-load
Dependency index depmod -a rebuilds modules.dep after adding a module ls /lib/modules/$(uname -r)/modules.dep
Taint Out-of-tree or unsigned modules set /proc/sys/kernel/tainted cat /proc/sys/kernel/tainted
Early boot Storage drivers needed to mount / must be in the initramfs lsinitrd or lsinitramfs

Devices and udev

Fact Value Verify with
Type, major, minor b block or c character; major selects the driver, minor the instance ls -l /dev/vda /dev/null, cat /proc/devices
/dev devtmpfs created by the kernel; udev adds permissions and symlinks mount -t devtmpfs
Stable names /dev/disk/by-uuid, by-id, by-path, by-label ls -l /dev/disk/by-uuid
Device database udevadm info --name=<dev> (properties), --attribute-walk (rule keys) udevadm info --name=/dev/vda
Rules /usr/lib/udev/rules.d/ (vendor), /etc/udev/rules.d/ (local, wins on same name) ls /etc/udev/rules.d
Apply rules udevadm control --reload, then udevadm trigger for existing devices ls -l /dev/<name>
Hardware and events lspci -k, lsusb, udevadm monitor --udev; sensors and ipmitool need physical hardware (No sensors found! in this VM) lspci -k
Create a node by hand mknod <path> c <major> <minor> sudo mknod /tmp/mynull c 1 3

dmesg and Kernel Messages

Fact Value Verify with
Ring buffer Fixed size; old messages are overwritten; the journal keeps a copy dmesg
Readable times dmesg -T (can drift after suspend); journalctl -k has real timestamps dmesg -T
Filter by level dmesg -l err,warn; journalctl -k -p warning dmesg -l err
Show level and facility dmesg -x dmesg -x
Follow dmesg -w, journalctl -kf New messages appear
Previous boot Only the journal: journalctl -k -b -1 journalctl --list-boots
Access kernel.dmesg_restrict = 1 limits dmesg to root (0 on both playgrounds) sysctl kernel.dmesg_restrict
Console level kernel.printk: messages below the first number reach the console sysctl kernel.printk
OOM kill Out of memory: Killed process or Memory cgroup out of memory journalctl -k -g "Killed process"
Segfault <prog>[pid]: segfault at <addr> ip ... error N in <binary> journalctl -k -g segfault
I/O error Buffer I/O error on dev ..., I/O error, dev sdb, sector ... dmesg -l err
Hung task INFO: task <name>:<pid> blocked for more than N seconds sysctl kernel.hung_task_timeout_secs
Core dumps systemd-coredump stores them; coredumpctl list, info, debug sysctl kernel.core_pattern

Storage

Disks and Devices

Fact Value Verify with
List block devices lsblk; lsblk -f adds filesystem, label, UUID and usage lsblk -o NAME,SIZE,TYPE,MOUNTPOINTS
Filesystem signatures blkid (as root for uncached devices) sudo blkid
SATA, SAS, USB disks /dev/sda, /dev/sdb; partitions sda1 lsblk
Virtio disks (KVM) /dev/vda; partitions vda1 lsblk -o NAME,TRAN
NVMe /dev/nvme0n1 (controller 0, namespace 1); partitions nvme0n1p1 nvme list
Xen disks (older EC2) /dev/xvda lsblk
Other block devices loopN (file), dm-N (LVM, LUKS), mdN (software RAID), sr0 (optical) lsblk -o NAME,TYPE
Stable names /dev/disk/by-uuid, by-id, by-path, by-label, by-partuuid ls -l /dev/disk/by-uuid
Rotational flag 1 spinning disk (and most virtual disks), 0 SSD cat /sys/block/<dev>/queue/rotational
Size in sectors /sys/block/<dev>/size, always in 512-byte units cat /sys/block/vda/size
MBR 4 primary partitions (or 3 plus an extended one), 2 TiB limit fdisk -l
GPT 128 partitions by default, backup table at the end of the disk, needed above 2 TiB and for UEFI gdisk -l
Disk health smartctl -H (SATA, SAS), nvme smart-log (NVMe); physical or passthrough disks only sudo smartctl -i /dev/sda
Loop device Presents a file as a disk: losetup -fP --show <file> losetup -l

Partitioning

Fact Value Verify with
fdisk Interactive, MBR and GPT; changes stay in memory until w sudo fdisk -l /dev/sdb
gdisk, sgdisk GPT only; type codes such as 8300, 8E00, EF00, 8200 sudo gdisk -l /dev/sdb
parted MBR and GPT, scriptable with -s; writes each command at once sudo parted /dev/sdb print free
sfdisk Dumps and restores a table as text sudo sfdisk -d /dev/sdb > sdb.dump
MBR type IDs 83 Linux, 82 swap, 8e LVM, fd RAID, ef EFI fdisk command l
MBR layout 4 primary, or 3 primary plus 1 extended holding logical partitions (5 and up) sudo fdisk -l
Alignment Partitions start at 1 MiB (sector 2048) sudo parted /dev/sdb align-check optimal 1
Re-read the table partprobe <disk>, or partx -a (add) and partx -u (update) lsblk <disk>
Remove signatures wipefs -a <device> (without -a it only lists them) sudo wipefs /dev/sdb
ESP GPT type EF00 (ESP flag in parted), FAT32, mounted at /boot/efi lsblk -o NAME,PARTTYPENAME
BIOS boot GPT disk booting in BIOS mode needs a 1 MiB EF02 partition for GRUB sudo gdisk -l

Filesystems

Fact Value Verify with
Defaults RHEL: XFS; Ubuntu and Debian: ext4 findmnt -no FSTYPE /
Create mkfs.ext4, mkfs.xfs, mkfs.vfat -F 32, mkfs.btrfs; -L sets a label lsblk -f
Existing signature mkfs.xfs and mkfs.btrfs refuse without -f; mke2fs asks on a terminal wipefs <dev>
Resize ext4: resize2fs, grows online, shrinks offline; XFS: xfs_growfs <mountpoint> grows only (apart from an experimental trim of the last allocation group) df -h
Check and repair ext4: e2fsck (unmounted); XFS: xfs_repair (unmounted; -n checks only) exit status
Inspect and label tune2fs -l, dumpe2fs -h, xfs_info; tune2fs -L, xfs_admin -L (unmounted), fatlabel blkid
Reserved blocks ext4 keeps 5% for root; tune2fs -m 1 lowers it tune2fs -l
Inodes ext4 fixes the count at creation (mkfs.ext4 -i, -N); XFS allocates them dynamically df -i
Backup superblocks ext4 keeps copies; mke2fs -n lists them, e2fsck -b <block> uses one dumpe2fs <dev>
Btrfs Copy-on-write, subvolumes, snapshots, checksums; Fedora and SUSE default; removed from RHEL 8 btrfs filesystem show

Mounting and fstab

Fact Value Verify with
Mount and unmount mount <dev> <dir>, umount <dir> (not "unmount") findmnt <dir>
Show mounts findmnt (tree), findmnt -t xfs, mount, /proc/self/mountinfo findmnt -no OPTIONS /
fstab fields device, mount point, type, options, dump, fsck pass man 5 fstab
Device field UUID=, LABEL=, PARTUUID= or /dev/...; UUID survives renames blkid
fsck pass 1 root, 2 other local filesystems, 0 skip (always 0 for XFS) cat /etc/fstab
Test fstab findmnt --verify, then mount -a exit status
systemd systemd-fstab-generator turns each line into a .mount unit; daemon-reload after edits systemctl list-units -t mount
nofail Boot continues if the device is missing systemctl show -p WantedBy <unit>
_netdev Wait for the network (NFS, iSCSI); implied for network filesystem types man systemd.mount
x-systemd.device-timeout= How long boot waits for the device (default 90 s) systemctl cat <unit>
Common options ro, noatime, noexec, nosuid, nodev, defaults (rw,suid,dev,exec,auto,nouser,async) findmnt -no OPTIONS <dir>
Busy target fuser -vm <dir>, lsof +f -- <dir>; umount -l detaches lazily umount <dir>
vfat and NTFS ownership Set at mount time with uid=, gid=, umask= ls -l

Swap

Fact Value Verify with
Show swap swapon --show, free -h, /proc/swaps swapon --show
Prepare mkswap [-L label] <dev or file> blkid shows TYPE="swap"
Enable, disable swapon <dev>, swapoff <dev>; -a for every fstab entry swapon --show
fstab line UUID=... none swap defaults 0 0 (pri=N sets the priority) systemctl list-units -t swap
Swap file Created with dd or fallocate (no holes), mode 0600 ls -l /swapfile
Priority Higher is used first; equal priorities are used in round robin swapon --show
vm.swappiness 0 to 200, kernel default 60; lower values keep more anonymous memory in RAM sysctl vm.swappiness
Per process VmSwap in /proc/<pid>/status grep VmSwap /proc/*/status
Per cgroup MemorySwapMax=, memory.swap.max; MemorySwapCurrent systemctl show -p MemorySwapCurrent <unit>
Sizing Up to the RAM size on small hosts, a few GiB on large ones; RAM plus more for hibernation free -h
Kubernetes The kubelet refuses to start with swap on unless failSwapOn: false is set swapon --show
zram Compressed swap in RAM; the default on Fedora zramctl

LVM

Fact Value Verify with
Layers Physical volume (PV) → volume group (VG) → logical volume (LV) → filesystem lsblk
Extents A VG is split into physical extents (PE, 4 MiB by default); an LV is a list of them vgdisplay
Create pvcreate, vgcreate <vg> <pvs>, lvcreate -n <lv> -L <size> <vg> (-l 100%FREE for extents) pvs, vgs, lvs
Device paths /dev/<vg>/<lv> and /dev/mapper/<vg>-<lv>, both links to /dev/dm-N ls -l /dev/mapper
Grow vgextend adds a PV; lvextend -r grows the LV and the filesystem together df -h
Shrink lvreduce -r (ext4 only; unmounts it); XFS cannot shrink lvs
Snapshot lvcreate -s -L <size> -n <snap> <vg>/<lv>; invalid when full; lvconvert --merge rolls back lvs (Data%)
Move data pvmove <pv> empties a PV online; then vgreduce and pvremove pvs
Thin pools --type thin-pool, lvcreate -V <size> -T <vg>/<pool>; allows overprovisioning lvs (Data%, Meta%)
Devices file RHEL 9 and later only use PVs listed in /etc/lvm/devices/system.devices; Ubuntu 24.04 does not use one lvmdevices
Metadata backup /etc/lvm/backup/<vg>, restored with vgcfgrestore vgcfgbackup

Resizing and Cloud Disks

Fact Value Verify with
Layers to grow Disk → partition → PV (if LVM) → LV → filesystem lsblk
See the new disk size NVMe and Virtio: automatic; SCSI: echo 1 > /sys/class/block/sdX/device/rescan lsblk
Grow a partition growpart <disk> <number> (package cloud-utils-growpart on RHEL, cloud-guest-utils on Ubuntu) lsblk
Grow a PV pvresize <partition> pvs
Grow an LV and its filesystem lvextend -r -l +100%FREE <vg>/<lv> df -h
Grow a filesystem ext4: resize2fs <device>; XFS: xfs_growfs <mountpoint> df -h
Online Every step above works on mounted filesystems, including / findmnt /
Shrinking Cloud volumes cannot shrink; create a smaller volume and copy the data provider console
Boot-time growth cloud-init growpart and resize_rootfs grow the root partition and filesystem on first boot cloud-init status --long
Partition position Only the last partition, or one followed by free space, can grow in place sudo parted <disk> print free
MBR limit A partition on an MBR disk cannot use space beyond 2 TiB sudo fdisk -l
AWS aws ec2 modify-volume --size; repeated modifications of one volume are rate-limited aws ec2 describe-volumes-modifications

Disk Usage

Fact Value Verify with
Filesystem usage df -h, df -hT (type), df -x tmpfs (exclude a type) df -h /
Inode usage df -i df -i /
Directory totals du -sh <dir>, du -xh --max-depth=1 / (-x stays on one filesystem) du -sh /var/log
Sort sizes sort -h orders human-readable sizes such as 1.2G and 51M sort --help
Large files find / -xdev -type f -size +100M ls -lh
Interactive ncdu -x / (EPEL on RHEL, apt install ncdu) ncdu --version
Apparent vs allocated ls -l and du --apparent-size show the length; du shows the blocks used du -h <sparse file>
Deleted but open Space is freed when the last process closes the file lsof -a +L1 <mountpoint>
Free it without a restart : > /proc/<pid>/fd/<fd> truncates the open file df -h
Reserved blocks ext4 keeps 5% for root, so users hit No space left at Avail 0 before Size is used tune2fs -l <dev>
Inode exhaustion No space left on device with free blocks; ext4 inode count is fixed df -i
Hidden files Files written to a directory before a filesystem was mounted over it count in df, not in du mount --bind / /mnt/rootfs
Usual growth /var/log, the journal, package caches, container images, /tmp, core dumps, backups du -xh --max-depth=2 /var
Journal size journalctl --disk-usage, --vacuum-size=, SystemMaxUse= journalctl --disk-usage

Quotas

Fact Value Verify with
Limits Soft (may be exceeded for the grace period, 7 days by default) and hard (never exceeded) quota -s <user>
XFS Mount options uquota, gquota, pquota at mount time; managed with xfs_quota -x findmnt -no OPTIONS <dir>
ext4 tune2fs -O quota -Q usrquota,grpquota (unmounted), mount with usrquota,grpquota quotaon -p <dir>
Set limits xfs_quota -x -c 'limit bsoft=50m bhard=60m <user>'; setquota -u <user> <bsoft> <bhard> <isoft> <ihard> <dir>; edquota -u <user> opens an editor quota -s <user>
Reports and grace xfs_quota -x -c 'report -h', repquota -s <dir>; grace with xfs_quota -x -c 'timer ...' or setquota -t as root

Backup and Restore

Fact Value Verify with
Full Copies everything; slowest to take, one step to restore backup size
Incremental Copies changes since the last backup of any kind; restore needs the full plus every incremental in order tar --listed-incremental
Differential Copies changes since the last full; restore needs the full plus the latest differential backup size
3-2-1 rule 3 copies, on 2 kinds of media, 1 off site backup inventory
RPO and RTO How much data may be lost; how long a restore may take runbook
rsync -a Recursive, keeps links, modes, times, owner and group; add -H (hard links), -A (ACLs), -X (xattrs, SELinux labels) rsync -aHAX
Trailing slash src/ copies the contents; src copies the directory itself ls dest
Dry run rsync -n -i shows what would change; --delete removes files gone from the source rsync -ani --delete
Snapshot-style copies rsync --link-dest=<previous> hard-links unchanged files ls -li
Consistent copy LVM, cloud or filesystem snapshot, or the application's own dump (pg_dump, mysqldump) lvs
Block image dd if=<dev> of=<file> bs=4M; ddrescue for failing disks sha256sum
Verify tar -d (compare), rsync -anc (checksums), a real test restore exit status

RAID and Encryption

Fact Value Verify with
Levels 0 striping, no redundancy; 1 mirror; 5 and 6 striping with one or two parity blocks mdadm --detail /dev/md0
RAID 10 Striped mirrors; common for databases mdadm --detail
Status /proc/mdstat: [UU] healthy, [U_] degraded cat /proc/mdstat
Replace a disk --fail, --remove, then --add the new one; rebuild runs online /proc/mdstat
Persist the array mdadm --detail --scan into /etc/mdadm.conf (Ubuntu: /etc/mdadm/mdadm.conf), then dracut -f (Ubuntu: update-initramfs -u) cat /proc/mdstat after a reboot
LUKS2 cryptsetup luksFormat, open, close; up to 32 keyslots (passphrases or key files) cryptsetup luksDump
Open at boot /etc/crypttab: name, device, key file or none, options; fstab then mounts /dev/mapper/<name> systemctl status systemd-cryptsetup@<name>
Stratis and VDO Removed from the RHCSA objectives; VDO now lives in LVM (lvcreate --type vdo) man lvmvdo

Networking

Interfaces and Addresses

Fact Value Verify with
Tool ip (iproute2) replaced ifconfig, route and arp (net-tools); net-tools may be absent ip -br addr
Link vs address ip link is layer 2 (state, MAC, MTU); ip addr is layer 3 ip -br link
State flags UP = administratively up; LOWER_UP = carrier present ip link show eth0
Predictable names enp0s3 (PCI path), ens3 (slot), eno1 (onboard), enx<MAC>; net.ifnames=0 keeps eth0 udevadm test-builtin net_id /sys/class/net/eth0
Loopback lo, 127.0.0.1/8 and ::1; traffic never leaves the host ip addr show lo
Link-local IPv6 fe80::/64, created on every IPv6-enabled link; derived from the MAC unless privacy addresses are on ip -6 addr
Runtime only ip addr add and ip link set are lost at reboot; persistence lives in NetworkManager or netplan ip -br addr after a reboot
Connected route Adding 10.0.0.1/24 to an up link adds the route 10.0.0.0/24 ip route show dev <if>
ARP / neighbor IPv4 to MAC cache; states REACHABLE, STALE, DELAY, INCOMPLETE, FAILED ip neigh
Counters Errors and drops per interface ip -s link show eth0
Driver and speed ethtool eth0 (speed, duplex, link), ethtool -i (driver), ethtool -S (NIC counters) sudo ethtool eth0
MTU Default 1500; jumbo frames 9000; tunnels and cloud overlays often lower ip link show eth0
sysfs /sys/class/net/<if>/ holds address, mtu, carrier, statistics/ cat /sys/class/net/eth0/mtu

Network Configuration

Fact Value Verify with
RHEL stack NetworkManager; profiles are keyfiles in /etc/NetworkManager/system-connections/ nmcli connection show
RHEL 10 No ifcfg-rh plugin, so /etc/sysconfig/network-scripts files are ignored; RHEL 9 still reads them but writes keyfiles ls /usr/lib64/NetworkManager/*/
Connection vs device A connection (profile) is applied to a device (interface) nmcli device status
Change a profile nmcli con mod edits the file; nmcli con up or nmcli dev reapply applies it ip -br addr
Add to a list +ipv4.addresses, -ipv4.addresses; without the sign the list is replaced nmcli -g ipv4.addresses con show <name>
Auto profiles NetworkManager creates Wired connection 1 (DHCP) for an unconfigured NIC; no-auto-default=* stops it nmcli connection show
Ubuntu stack netplan YAML in /etc/netplan/*.yaml generates systemd-networkd (server) or NetworkManager (desktop) files netplan get
netplan files Mode 0600; lists use YAML sequences; indentation is significant sudo netplan generate
Apply safely netplan try reverts after the timeout unless confirmed; netplan apply does not sudo netplan try
cloud-init Writes /etc/netplan/50-cloud-init.yaml on cloud images ls /etc/netplan
Hostname hostnamectl set-hostname writes /etc/hostname; hostname -s is the short name hostnamectl
Name for the host Add the hostname to /etc/hosts when no DNS record exists getent hosts $(hostname)

Routing

Fact Value Verify with
Lookup rule Longest prefix wins; among equal prefixes, the lowest metric wins ip route get <ip>
Default route default = 0.0.0.0/0, used when nothing more specific matches ip route show default
Connected route Created with each address on an up link (proto kernel scope link) ip route
via Next-hop router; it must be reachable on a connected subnet ip route get <ip>
Forwarding A Linux host routes between interfaces only with net.ipv4.ip_forward = 1 sysctl net.ipv4.ip_forward
Return path Replies use the destination host's own routing table; a missing return route drops the reply tcpdump -e on the destination
TTL Each router decrements TTL by one; ttl=63 from a Linux host means one hop ping
Reverse path filter rp_filter=1 (strict) drops packets arriving on an interface that would not be used to reply sysctl net.ipv4.conf.all.rp_filter
Policy routing ip rule chooses a table (by source, mark, interface) before the route lookup ip rule show
Tables local (own addresses), main (normal), default; custom tables by number or /etc/iproute2/rt_tables ip route show table all
Special routes blackhole, unreachable, prohibit drop traffic on purpose ip route
Persistence NetworkManager ipv4.routes, netplan routes:, sysctl.d for forwarding nmcli -g ipv4.routes con show <name>
Legacy view route -n (net-tools); flags U up, G gateway, H host route -n

DNS Resolution

Fact Value Verify with
Lookup order hosts: line in /etc/nsswitch.conf; files = /etc/hosts, dns = resolvers in /etc/resolv.conf grep ^hosts /etc/nsswitch.conf
RHEL hosts: files dns myhostname; /etc/resolv.conf written by NetworkManager head -1 /etc/resolv.conf
Ubuntu systemd-resolved stub at 127.0.0.53; /etc/resolv.conf is a symlink to stub-resolv.conf ls -l /etc/resolv.conf, resolvectl status
Application view getent hosts <name> follows nsswitch like applications getent ahostsv4 <name>
DNS view dig, host, nslookup query DNS only and ignore /etc/hosts (the resolved stub serves hosts entries itself) dig @<server> <name>
resolv.conf limits Up to 3 nameserver lines; search domains; options timeout:N attempts:N cat /etc/resolv.conf
Search list Short names get each search domain appended getent hosts db
Status codes NOERROR, NXDOMAIN (name does not exist), SERVFAIL (server failed), REFUSED (policy) status: line of dig
aa flag Authoritative answer from the zone's own server dig @<server> <name>
TTL Seconds a resolver may cache the record; counts down in cached answers dig +noall +answer twice
Record types A, AAAA, CNAME, MX, NS, TXT, SOA, PTR (reverse, dig -x), SRV dig <name> <type>
Transport UDP 53, TCP 53 for large answers and zone transfers ss -ulpn 'sport = :53'
Split DNS resolved routing domain Domains=~zone sends only that zone to a server resolvectl domain
Cache control resolvectl flush-caches, resolvectl statistics sudo resolvectl statistics

Ports and Sockets

Fact Value Verify with
Tool ss (iproute2) replaced netstat (net-tools) ss -tulpn
Flags -t TCP, -u UDP, -l listening, -p process (needs root for other users), -n numeric, -a all sudo ss -tulpn
Socket identity Protocol + local IP:port + remote IP:port; many connections share one listening port ss -tn
0.0.0.0 / [::] / * Listening on all addresses (IPv4, IPv6, both) ss -tln
127.0.0.1 Reachable only from the same host (or the same network namespace) ss -tln
Privileged ports Below ip_unprivileged_port_start (1024) need root or CAP_NET_BIND_SERVICE sysctl net.ipv4.ip_unprivileged_port_start
Ephemeral ports Client source ports from ip_local_port_range (32768 to 60999) sysctl net.ipv4.ip_local_port_range
Service names /etc/services maps names to ports getent services 443
Well-known ports 22 SSH, 25 SMTP, 53 DNS, 80 HTTP, 123 NTP (UDP), 443 HTTPS, 3306 MySQL, 5432 PostgreSQL, 6379 Redis, 6443 Kubernetes API, 2379 etcd, 10250 kubelet ss -tlnp
Who owns a port ss -tlpn 'sport = :8080', lsof -i :8080, fuser -v 8080/tcp sudo lsof -i :8080 -P -n
Refused vs timeout Refused: host answered with RST, nothing listens; timeout: packets dropped on the way nc -vz host port
Raw source /proc/net/tcp and /proc/net/tcp6 in hex, host byte order cat /proc/net/tcp
Socket activation systemd can hold the listening socket (sshd.socket here) systemctl list-sockets

Sockets and TCP States

Fact Value Verify with
Server calls socket(), bind(), listen(backlog), accept() returns a new fd per client strace -e trace=network
Client calls socket(), connect(); the kernel picks an ephemeral source port ss -tn
Handshake SYN, SYN-ACK, ACK; the kernel completes it without the application tcpdump 'tcp[tcpflags] & tcp-syn != 0'
SYN queue Half-open connections (SYN-RECV); limit tcp_max_syn_backlog; tcp_syncookies=1 survives floods sysctl net.ipv4.tcp_syncookies
Accept queue Completed connections waiting for accept(); limit min(backlog, somaxconn), and somaxconn is 4096 since kernel 5.4 ss -ltn (Recv-Q/Send-Q)
Queue overflow New SYNs are dropped, clients sit in SYN-SENT and retry nstat -az TcpExtListenOverflows
Active closer Sends the first FIN, passes FIN-WAIT-1, FIN-WAIT-2, then TIME-WAIT (60 s on Linux) ss -tan state time-wait
Passive closer Receives the FIN and stays in CLOSE-WAIT until the application calls close() ss -tanp state close-wait
CLOSE_WAIT pile-up Always an application bug (sockets not closed) ss -tanp state close-wait
TIME_WAIT pile-up Normal on busy clients; costs little memory; tcp_tw_reuse lets outgoing connections reuse them ss -s
Orphan FIN-WAIT-2 Closed by the kernel after tcp_fin_timeout (60 s) sysctl net.ipv4.tcp_fin_timeout
Connection tracking Netfilter tracks flows for stateful rules and NAT; full table drops new flows conntrack -C, sysctl net.netfilter.nf_conntrack_max
Per-socket detail RTT, congestion window, retransmits ss -tin

Connectivity Testing

Fact Value Verify with
ping ICMP echo request and reply; shows loss, RTT and the reply's TTL ping -c3 <host>
ICMP blocked A host that drops ping can still serve TCP; test the port instead nc -vz <host> <port>
traceroute Sends probes with TTL 1, 2, 3...; each router that drops one returns ICMP Time Exceeded traceroute -n <host>
Probe types traceroute UDP by default, -I ICMP, -T TCP (root); tracepath UDP without root man traceroute
Markers * * * no reply from that hop; !H host unreachable, !N network unreachable, !X prohibited traceroute -n
mtr Continuous traceroute with per-hop loss; loss only at a middle hop is ICMP rate limiting mtr -n -r -c 10 <host>
MTU probe ping -M do -s <size>: payload + 28 bytes of headers must fit the MTU (1472 for 1500) ping -M do -s 1472 <host>
PMTU cache The kernel remembers a lower path MTU per destination for 10 minutes ip route get <host>
Port test nc -vz host port; timeout 2 bash -c '< /dev/tcp/host/port' without nc exit status
Refused vs timeout Refused: RST (nothing listens or reject); timeout: dropped nc -vz -w3
HTTP curl -v (headers), -I (HEAD), -w (timings), --resolve (skip DNS), -k (skip TLS verify) curl -sv <url>
Throughput iperf3 -s on one side, iperf3 -c on the other; port 5201 iperf3 -c <host>
telnet Often not installed; nc or curl telnet://host:port replace it command -v telnet

Packet Capture

Fact Value Verify with
Privilege Capturing needs root or CAP_NET_RAW and CAP_NET_ADMIN sudo tcpdump -D
Interfaces -i eth0, -i any (all, cooked headers with direction); -D lists them tcpdump -D
Basic flags -n no name lookups, -c N stop after N packets, -v more detail, -e MAC addresses sudo tcpdump -ni eth0 -c 5
Payload -A ASCII, -X hex and ASCII, -s 0 full packets (default snap length 262144) sudo tcpdump -A
Files -w file.pcap writes, -r reads, -U flushes per packet; -C/-G/-W rotate tcpdump -nr file.pcap
Filter primitives host, net, port, portrange, src/dst, tcp/udp/icmp, arp man pcap-filter
Combining and, or, not, parentheses in quotes 'host 10.0.0.5 and not port 22'
TCP flags filter tcp[tcpflags] with tcp-syn, tcp-ack, tcp-fin, tcp-rst, tcp-push 'tcp[tcpflags] & tcp-rst != 0'
Flag letters S SYN, . ACK, P push, F FIN, R reset; S. is SYN-ACK handshake capture
Where it sees traffic Incoming before the firewall's input rules, outgoing after the output rules capture plus firewall counters
Other tools tshark (Wireshark CLI), capinfos, termshark tshark -r file.pcap

Bridges, Bonds and VLANs

Fact Value Verify with
Bridge Learns MAC addresses per port (forwarding database); docker0 and cni0 are bridges bridge fdb show
veth pair Two linked interfaces; one end in a container namespace, the other on a bridge ip -br link type veth
Bond modes 0 balance-rr, 1 active-backup, 2 balance-xor, 4 802.3ad (LACP, needs switch support), ⅚ adaptive; miimon polls link state cat /proc/net/bonding/bond0
Teaming teamd deprecated in RHEL 9 and absent from RHEL 10 repositories; use bonding dnf list teamd
VLAN 802.1Q tag with a 12-bit ID (1 to 4094); interface name eth0.10 by convention ip -d link show eth0.10
Persistence NetworkManager type bridge, bond, vlan profiles; netplan bridges:, bonds:, vlans: nmcli con show

Time and Timezones

Fact Value Verify with
Owner timedatectl shows and sets time, timezone and NTP state timedatectl
Timezone /etc/localtime is a symlink into /usr/share/zoneinfo/; TZ= overrides it per process ls -l /etc/localtime
Set timezone timedatectl set-timezone Asia/Karachi timedatectl list-timezones
NTP client chrony (chronyd) on RHEL and Ubuntu with the chrony package; systemd-timesyncd is Ubuntu's minimal default systemctl is-active chronyd
chrony config RHEL /etc/chrony.conf; Ubuntu /etc/chrony/chrony.conf plus sources.d/*.sources grep ^pool /etc/chrony.conf
Server vs pool server is one host; pool resolves a name to several; iburst speeds up the first sync chronyc sources
Step vs slew makestep 1.0 3 steps the clock if it is off by more than 1 s during the first 3 updates; otherwise chrony slews it gradually journalctl -u chronyd
Serving time allow <network> makes chronyd an NTP server on UDP 123 ss -ulpn 'sport = :123'
Stratum Distance from a reference clock; each NTP hop adds one chronyc tracking
Manual time timedatectl set-time requires NTP off, and interprets the value in the local timezone timedatectl set-ntp false
RTC The hardware clock (hwclock), normally in UTC; many cloud and microVM instances have none timedatectl (RTC time)
Epoch Seconds since 1970-01-01 UTC date +%s, date -d @0 -u

Reverse Proxy and Load Balancing

Fact Value Verify with
Reverse vs forward proxy A reverse proxy fronts servers for clients; a forward proxy fronts clients for the internet architecture
nginx pieces upstream block lists backends; proxy_pass http://<upstream> sends requests there sudo nginx -T
Algorithms Round robin (default), least_conn, ip_hash/hash (stickiness), weights upstream config
Client address Backends see the proxy's IP; the original goes in X-Forwarded-For backend log
Passive checks nginx open source marks a backend failed after max_fails errors for fail_timeout nginx error log
Active checks HAProxy option httpchk probes each server; fall/rise set the thresholds show stat
502 Bad Gateway The proxy could not get a valid response: backend down, refused or crashed error log connect() failed
504 Gateway Timeout The backend accepted but did not answer within proxy_read_timeout error log upstream timed out
Retries nginx tries the next upstream on connection errors (proxy_next_upstream) $upstream_addr in the log
Validate first nginx -t; haproxy -c -f <file> (silent on success, -V prints Configuration file is valid) exit status
SELinux On RHEL, nginx needs httpd_can_network_connect to reach backends on non-HTTP ports getsebool httpd_can_network_connect
L4 vs L7 Layer 4 forwards TCP (HAProxy mode tcp, nginx stream); layer 7 reads HTTP (paths, headers) mode in config

VPN (WireGuard)

Fact Value Verify with
Transport UDP, default port 51820; no TCP mode ss -ulpn 'sport = :51820'
Identity Each peer has a key pair; wg genkey, wg pubkey; private keys stay mode 0600 ls -l /etc/wireguard
AllowedIPs Routing table and access list in one: outgoing packets pick the peer by destination, incoming packets are accepted only from these sources wg show wg0 allowed-ips
Handshake Every two minutes while traffic flows; no handshake means no tunnel wg show wg0 latest-handshakes
Keepalive PersistentKeepalive = 25 keeps NAT mappings open for a peer behind NAT wg show
MTU wg-quick sets 1420 (1500 minus 80 bytes of overhead) ip link show wg0
Tooling wg-quick up wg0 reads /etc/wireguard/wg0.conf; wg-quick@wg0.service makes it permanent systemctl status wg-quick@wg0

Troubleshooting Ladder

Fact Value Verify with
Order Link, IP, route, gateway ARP, ping, DNS, port, firewall, application this page
Link UP and LOWER_UP; NO-CARRIER means cable or virtual NIC ip -br link
Address Right IP and prefix on the right interface ip -br addr
Route The path the kernel will use for this destination ip route get <ip>
Gateway Neighbor entry REACHABLE or STALE, never FAILED ip neigh show <gw>
Reachability ICMP may be blocked; a failure here is a hint, not proof ping -c3 <ip>
Name Resolve the way the application does getent hosts <name>
Port refused = nothing listens or reject; timed out = dropped nc -vz -w3 <ip> <port>
Listener Service bound to the right address on the server sudo ss -tlnp 'sport = :<port>'
Firewall Host rules, then security groups and network ACLs sudo nft list ruleset, firewall-cmd --list-all
Proof on the wire SYNs arriving without SYN-ACK = dropped on the server sudo tcpdump -nn -i any port <port>
Application HTTP status, TLS errors, application logs curl -sv <url>, journalctl -u <unit>
Compare A working client or path narrows the fault faster than any single tool same tests from two hosts

SSH and Remote Access

SSH Client

Fact Value Verify with
Key type ed25519 is the default choice; RSA keys need 3072 bits or more ssh-keygen -l -f ~/.ssh/id_ed25519.pub
Key files Private key ~/.ssh/id_ed25519 (mode 600), public key .pub (mode 644), directory ~/.ssh (mode 700) ls -la ~/.ssh
Server side Public keys go into ~/.ssh/authorized_keys of the target user ssh-copy-id user@host
Host identity First connection asks to trust the host key; the answer is stored in ~/.ssh/known_hosts ssh-keygen -F host
Hashed hosts Ubuntu sets HashKnownHosts yes, so entries hold a hash instead of the host name; RHEL stores names in clear text ssh -G host
Agent ssh-agent holds decrypted keys; ssh-add loads them; SSH_AUTH_SOCK points to it ssh-add -l
Config order Command line, then ~/.ssh/config, then /etc/ssh/ssh_config; the first value found wins ssh -G host
Bastion ProxyJump (-J) connects through a jump host without copying keys to it ssh -J gw web
Scripts BatchMode=yes fails instead of prompting; ConnectTimeout bounds the wait ssh -o BatchMode=yes host true
Exit status The remote command's status, or 255 for an SSH error ssh host false; echo $?
Escape keys ~. kills a hung session, ~? lists escapes; only after a newline type Enter ~ .

SSH Tunnels

Fact Value Verify with
-L lport:host:hport Local forward: the client listens on lport, the server connects to host:hport ss -tlnp 'sport = :lport' on the client
-R rport:host:hport Remote forward: the server listens on rport, the client connects to host:hport ss -tln on the server
-D port Dynamic forward: a SOCKS proxy on the client; the server connects wherever the application asks curl --socks5-hostname 127.0.0.1:port URL
Destination view In -L, host is resolved and reached from the server, so 127.0.0.1 means the server itself ssh -L 9000:127.0.0.1:8008 web
Bind address Forwards listen on loopback by default; GatewayPorts (server) and -g or a bind address (client) change that ss -tln
Background -f goes to the background after authentication, -N runs no remote command pgrep -af 'ssh -f'
Failure handling A failed forward is only a warning unless ExitOnForwardFailure=yes echo $?
Server switch AllowTcpForwarding (default yes) and PermitOpen limit forwarding in sshd sudo sshd -T
Config form LocalForward, RemoteForward and DynamicForward in ~/.ssh/config ssh -G host

sshd Server

Fact Value Verify with
Files /etc/ssh/sshd_config, drop-ins in /etc/ssh/sshd_config.d/*.conf (included near the top), host keys /etc/ssh/ssh_host_*_key grep -n Include /etc/ssh/sshd_config
Precedence The first value read for an option wins, so an early drop-in beats the main file sudo sshd -T
Test and dump sshd -t checks syntax; sshd -T prints the effective settings; -C user=,host=,addr= evaluates Match blocks sudo sshd -t && echo ok
Apply systemctl reload sshd (RHEL) or ssh (Ubuntu 24.04, started by ssh.socket); existing sessions survive a reload systemctl list-sockets
Key settings PermitRootLogin (default prohibit-password, shown as without-password), PasswordAuthentication (default yes), AllowUsers, AllowGroups, MaxAuthTries (default 6) sudo sshd -T
Penalties OpenSSH 9.8 and later delay sources that fail authentication (PerSourcePenalties) sudo sshd -T
SFTP jail Match Group with ChrootDirectory and ForceCommand internal-sftp; the chroot path must be owned by root and not group- or world-writable ls -ld /srv/sftp/*
Brute force Key-only login, then fail2ban bans repeated failures (EPEL on RHEL) sudo fail2ban-client status sshd

File Transfer

Fact Value Verify with
scp syntax scp SRC [user@]host:DEST, scp host:SRC DEST; -r recursive, -p keep times and modes, -P port scp -v file host:/tmp/
scp protocol OpenSSH 9.0 and later use SFTP underneath; -O selects the old SCP protocol scp -v shows Sending subsystem: sftp
rsync basics -a archive (recursive, links, modes, times, owner for root), -v verbose, -z compress, -P = --partial --progress rsync -av src/ host:dst/
Trailing slash rsync src host:dst creates dst/src; rsync src/ host:dst copies the contents find dst
Deletion --delete removes files missing from the source; always preview with -n rsync -avn --delete src/ dst/
Change codes -i prints one line per change, such as *deleting or >f.st...... rsync -ai src/ dst/
Both ends rsync must be installed on the remote host too ssh host rsync --version
sftp Interactive or batch (-b file, -b -) file sessions; no remote shell needed sftp -b - host
Streams tar czf - dir piped into ssh host 'tar xzf - -C /dst' copies a tree over one connection ssh host 'ls /dst'
Through a bastion All three honor ProxyJump; rsync -e 'ssh -J gw' for one-offs rsync -e 'ssh -J gw' ...

SSH Troubleshooting

Fact Value Verify with
Client debug -v, -vv, -vvv add detail; the last debug1 line before the error names the stage ssh -v host true
Server log RHEL: unit sshd, file /var/log/secure; Ubuntu: unit ssh, file /var/log/auth.log sudo journalctl -u sshd -n 20
Connection refused The host answered, nothing listens on the port (or a firewall rejects) nc -vz host 22
Connection timed out No answer: a firewall drops the packets, or the route is broken ssh -o ConnectTimeout=5 host
Host key verification failed The stored key does not match; check why before ssh-keygen -R host ssh-keygen -F host
Permission denied (publickey) No offered key was accepted; the list in brackets is what the server allows ssh -v
StrictModes Home, ~/.ssh and authorized_keys not writable by group or others: 755 or stricter, 700, 600 namei -l ~/.ssh/authorized_keys
SELinux authorized_keys must be labeled ssh_home_t; mv keeps the old label ls -Z ~/.ssh
Too many keys Each offered key counts against MaxAuthTries ssh-add -l
Slow login Reverse DNS (UseDNS yes) or GSSAPI waiting on an unreachable server sudo sshd -T
Penalties OpenSSH 9.8 and later refuse connections from a source that keeps failing sudo journalctl -u sshd -g penalty

Security

firewalld and ufw

Fact Value Verify with
Defaults RHEL: firewalld on, zone public allows ssh, dhcpv6-client, cockpit; Ubuntu: ufw installed but inactive sudo firewall-cmd --list-all, sudo ufw status
Backend Both write nftables rules (firewalld table inet firewalld; ufw through iptables-nft) sudo nft list tables
Runtime vs permanent firewall-cmd changes runtime only; --permanent changes the saved config; --reload drops runtime-only rules; --runtime-to-permanent saves them sudo firewall-cmd --permanent --list-all
Zones A packet uses the zone of its source address first, then the zone of its interface, then the default zone sudo firewall-cmd --get-active-zones
Services Named port sets in /usr/lib/firewalld/services/*.xml (263 on Rocky 10.2); zone config in /etc/firewalld/zones/public.xml sudo firewall-cmd --get-services
Rich rules Per-source or logged rules; deny rules run before allow rules unless priority is set sudo firewall-cmd --list-rich-rules
Reject vs drop firewalld rejects (clients see refused or unreachable); ufw deny drops (clients time out) nc -vz host port
ufw rules ufw allow 22/tcp, ufw allow from 10.0.0.0/8 to any port 5432 proto tcp, ufw delete allow 22/tcp; blocks are logged as [UFW BLOCK] sudo ufw status numbered

nftables and iptables

Fact Value Verify with
Hooks prerouting, input, forward, output, postrouting; local traffic uses input, routed traffic uses forward sudo nft list ruleset
Objects Table (family ip, ip6, inet, ...) holds chains; a base chain has a hook, priority and policy; rules have handles sudo nft -a list tables
iptables today RHEL 9/10 and Ubuntu 24.04 ship iptables-nft, which writes nftables tables ip filter, ip nat; iptables-translate prints the nft form iptables -V shows (nf_tables)
Stateful ct state established,related accept lets replies through; conntrack tracks each flow sudo conntrack -L
Evaluation Every base chain on a hook runs; a packet must be accepted by all of them (firewalld, Docker and custom tables together) sudo nft list chains
NAT masquerade or snat in postrouting (source), dnat in prerouting (destination, port forwarding); routing also needs ip_forward=1 sudo nft list table ip nat
Sets Named address or port sets, optionally with timeout, replace long rule lists and ipset sudo nft list sets
Persistence RHEL: /etc/sysconfig/nftables.conf + nftables.service; Ubuntu: /etc/nftables.conf; iptables: iptables-save with iptables-persistent systemctl cat nftables

SELinux

Fact Value Verify with
Modes enforcing (deny and log), permissive (log only, a diagnostic step), disabled; setenforce 0/1 switches until reboot getenforce
Config /etc/selinux/config: SELINUX= and SELINUXTYPE=targeted; full disable needs the kernel option selinux=0 sestatus
Context user:role:type:level; the type decides almost everything in the targeted policy ls -Z, ps -Z, id -Z
Type enforcement A process type (domain, nginx: httpd_t) may access a file type (web content: httpd_sys_content_t, writable: httpd_sys_rw_content_t) only if an allow rule exists sesearch --allow -s httpd_t
Labels New files inherit the parent directory's type; mv keeps the old label, cp takes the new one; touch /.autorelabel and a reboot relabel everything ls -Z
Fix a label semanage fcontext -a -t TYPE 'PATH(/.*)?' then restorecon -Rv PATH; chcon is temporary matchpathcon PATH
Ports Daemons may bind only ports with their type; semanage port -a -t http_port_t -p tcp 8090 semanage port -l
Booleans Policy switches such as httpd_can_network_connect; -P makes them persistent getsebool -a
Logs AVC denials in /var/log/audit/audit.log; ausearch -m AVC -ts recent, audit2why, sealert sudo ausearch -m AVC

AppArmor

Fact Value Verify with
Model Profiles attach to executables by path; rules name files (r, w, ix, ...), capabilities and network types ls /etc/apparmor.d
Modes enforce (deny and log apparmor="DENIED") or complain (log only), per profile; the kernel must run AppArmor as its active LSM sudo aa-status
Tools aa-enforce, aa-complain, aa-disable, apparmor_parser -r (reload), aa-genprof and aa-logprof (build from logs) sudo journalctl -k -g apparmor
Containers Docker and containerd apply docker-default; --security-opt apparmor=unconfined removes it docker inspect -f '{{.AppArmorProfile}}' ID

Capabilities

Fact Value Verify with
Process sets Effective (checked now), Permitted (may enable), Inheritable, Bounding (upper limit), Ambient (kept across execve for non-root) grep Cap /proc/self/status
Root A root process has all capabilities in its effective set; a normal user has none sudo grep CapEff /proc/self/status
Decode capsh --decode=HEX turns a mask into names capsh --decode=0000000000000400
File capabilities Stored in the security.capability extended attribute; setcap cap_x=+ep FILE getcap FILE
Copies cp without --preserve=xattr drops file capabilities; so does rewriting the file getcap on the copy
Low ports Ports below net.ipv4.ip_unprivileged_port_start (1024) need CAP_NET_BIND_SERVICE sysctl net.ipv4.ip_unprivileged_port_start
ping Ubuntu: file capability cap_net_raw; RHEL: unprivileged ICMP sockets via net.ipv4.ping_group_range getcap /usr/bin/ping
systemd AmbientCapabilities= grants, CapabilityBoundingSet= limits; User= alone drops everything systemctl show -p CapabilityBoundingSet UNIT
Dropping capsh --drop=cap_x -- -c CMD runs a command without that capability, even as root sudo capsh --print
Containers Docker keeps 14 capabilities by default; --cap-drop ALL --cap-add NET_BIND_SERVICE is the least-privilege pattern docker inspect
Dangerous ones CAP_SYS_ADMIN (mount, many admin calls), CAP_SYS_PTRACE, CAP_DAC_OVERRIDE, CAP_SETUID are almost root man 7 capabilities

auditd

Fact Value Verify with
Pieces Kernel audit subsystem, auditd daemon, rules in /etc/audit/rules.d/*.rules compiled by augenrules; package audit (RHEL, enabled) or auditd (Ubuntu) sudo auditctl -s
Rules Watch: -w PATH -p rwxa -k KEY; syscall: -a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -k KEY sudo auditctl -l
auid The login UID, set at login and kept through sudo and su; unset for services cat /proc/self/loginuid
Search ausearch -k KEY -i, -m AVC, -ts recent (10 minutes) or today, --format text; reports with aureport -au, -k, -x sudo aureport -k --summary

GPG

Fact Value Verify with
Sign, encrypt gpg --armor --detach-sign FILE, gpg --verify FILE.asc FILE; gpg -e -r RECIPIENT FILE or gpg -c FILE, gpg -d to decrypt echo $? after verify
Keys ~/.gnupg/ holds the keyring and, from creation, a revocation certificate; gpg --armor --export ID shares the public key gpg --show-keys key.asc
RPM Keys imported with rpm --import, listed as gpg-pubkey packages; gpgcheck=1 and gpgkey= per repo rpm -q gpg-pubkey
APT Repository keys in /usr/share/keyrings/ or /etc/apt/keyrings/, bound per repo with Signed-By, so a vendor key cannot sign other repositories; apt-key is deprecated grep Signed-By /etc/apt/sources.list.d/*

OpenSSL and Trust Store

Fact Value Verify with
Chain Server certificate, then intermediates, signed up to a root the client trusts; the server sends leaf and intermediates openssl s_client -showcerts
Names Clients match the host name against subjectAltName; the CN alone is ignored openssl x509 -noout -ext subjectAltName
Inspect openssl x509 -in FILE -noout -subject -issuer -dates openssl x509 -text
Expiry check -checkend SECONDS exits 1 if the certificate expires within that time openssl x509 -checkend 2592000
Key match Public key hash of certificate and private key must be equal openssl pkey -pubout
Test a server openssl s_client -connect HOST:PORT -servername NAME; -servername sends SNI curl -v https://HOST
Formats PEM (Base64, -----BEGIN), DER (binary), PKCS#12 (.p12/.pfx, key and chain in one file) openssl x509 -inform der
RHEL trust Drop CA files in /etc/pki/ca-trust/source/anchors/, run update-ca-trust; bundle /etc/pki/tls/certs/ca-bundle.crt trust list --filter=ca-anchors
Ubuntu trust Drop *.crt files in /usr/local/share/ca-certificates/, run update-ca-certificates; bundle /etc/ssl/certs/ca-certificates.crt ls -l /etc/ssl/certs
Crypto policy RHEL sets allowed protocols and key sizes system-wide update-crypto-policies --show
Private keys Mode 600, owned by root or the service user; never in a repository ls -l *.key

Compliance and Integrity

Fact Value Verify with
Package verify rpm -Va and dpkg --verify: S size, 5 digest, M mode, U user, T mtime, P capabilities; c marks config files; debsums -s on Debian sudo rpm -Va
AIDE aide --init builds a baseline, aide --check compares (nonzero exit on differences), aide --update accepts changes; keep the database off the host; rkhunter and chkrootkit are signature-based and prove little when clean sudo aide --check
OpenSCAP oscap xccdf eval --profile ID --report FILE DATASTREAM with content from scap-security-guide oscap info --profiles FILE
Backporting RHEL and Ubuntu fix CVEs in the shipped version, so the upstream version says nothing; dnf updateinfo list --security, Ubuntu unattended-upgrades rpm -q --changelog PKG

Hardening Checklist

Fact Value Verify with
Updates Security updates applied; automatic on servers that allow it (dnf-automatic, unattended-upgrades) sudo dnf updateinfo list --security
Accounts Only root has UID 0; no empty password fields; service accounts use nologin awk -F: '$3 == 0' /etc/passwd
sudo Narrow command rules; NOPASSWD: ALL only where justified sudo -l -U USER
setuid Inventory setuid files and remove unneeded ones; world-writable directories carry the sticky bit find / -xdev -perm -4000 -type f
Services Every listening socket is known and needed sudo ss -tulpn
SSH Keys only, no root login, MaxAuthTries low, a legal banner sudo sshd -T
Firewall, MAC Default deny for incoming traffic; SELinux enforcing or AppArmor profiles loaded getenforce
Kernel kptr_restrict, dmesg_restrict, rp_filter on; ICMP redirects off; ASLR 2 sysctl -a
Filesystems Unused modules (cramfs, udf, usb-storage on servers) blocked with install ... /bin/false modprobe -n -v cramfs
Logging, audits auditd and time sync active, logs shipped off the host; Lynis or OpenSCAP scans systemctl is-active auditd chronyd

Boot and Recovery

Boot Process

Fact Value Verify with
Firmware UEFI (modern) or BIOS (legacy) starts the bootloader [ -d /sys/firmware/efi ] && echo UEFI
Bootloader GRUB2 on most distributions; loads kernel and initramfs /boot/grub2/grub.cfg
Kernel args Passed by the bootloader, seen at runtime cat /proc/cmdline
initramfs Temporary root with drivers to reach the real root lsinitrd / lsinitramfs
switch_root Pivot from initramfs to the real root filesystem in /proc/cmdline root=
init PID 1 is systemd; brings the system to a target ps -p 1 -o comm=
Default target The target booted by default systemctl get-default
Boot timing Kernel plus userspace startup time systemd-analyze
Slowest units Per-unit startup time systemd-analyze blame
Critical path The dependency chain that gated boot systemd-analyze critical-chain
Secure Boot Firmware verifies the bootloader and kernel signatures mokutil --sb-state

GRUB2

Fact Value Verify with
Config file grub.cfg is generated; do not edit it directly head /boot/grub2/grub.cfg
Source /etc/default/grub plus scripts in /etc/grub.d cat /etc/default/grub
Regenerate (RHEL) grub2-mkconfig -o the active grub.cfg after editing defaults
Regenerate (Ubuntu) update-grub (wraps grub2-mkconfig) after editing defaults
Persistent kernel args GRUB_CMDLINE_LINUX in /etc/default/grub grep CMDLINE /etc/default/grub
One-boot edit Press e at the menu, edit the linux line, Ctrl-x at the console
Active args What the running kernel actually got cat /proc/cmdline
Default entry GRUB_DEFAULT, or grub2-editenv list for saved grub2-editenv list
Change default grubby --set-default (RHEL) grubby --default-kernel
Menu password grub2-setpassword protects editing entries /boot/grub2/user.cfg
Timeout GRUB_TIMEOUT seconds before the default boots /etc/default/grub

Recovery

Fact Value Verify with
Rescue target Minimal system, root shell, local mounts systemctl rescue
Emergency target Bare minimum, read-only root, no mounts systemctl emergency
Boot into a target Append systemd.unit=rescue.target at GRUB at the menu
Reset root password Append rd.break, remount, passwd, relabel RHEL method
SELinux relabel .autorelabel or load_policy -i after a chroot edit touch /.autorelabel
Bad fstab A missing device without nofail drops boot to emergency journalctl -b
Test fstab Validate before reboot sudo mount -a
Failed units List what did not start systemctl --failed
System state running, degraded (a unit failed), maintenance systemctl is-system-running
Read boot logs The last boot's journal journalctl -b
kdump Captures a kernel crash dump for analysis systemctl status kdump

Kernel Panic

Fact Value Verify with
Oops Kernel error in one context; logs and may continue dmesg, journalctl -k
Panic Kernel halts; nothing runs after on-console message
Common panic Unable to mount root fs, init died, hardware fault /proc/cmdline root=
Oops to panic An oops in an atomic context escalates to a panic kernel.panic_on_oops
kernel.panic Seconds to auto-reboot after panic; 0 means halt sysctl kernel.panic
kernel.panic_on_oops 1 makes any oops a panic (fail fast) sysctl kernel.panic_on_oops
Magic SysRq Kernel-level key combos for a hung system cat /proc/sys/kernel/sysrq
SysRq bitmask A number enables a subset of functions sysctl kernel.sysrq
kdump Boots a capture kernel to save a crash dump systemctl status kdump
netconsole Streams kernel messages over UDP to another host modinfo netconsole
Tainted Flag showing non-standard modules loaded cat /proc/sys/kernel/tainted

Kernel Updates

Fact Value Verify with
Running kernel The one booted now uname -r
Installed kernels Packages present, possibly newer than running rpm -q kernel / dpkg -l 'linux-image*'
initramfs Rebuilt per kernel on install dracut / update-initramfs
needs-restarting Reports whether a reboot is required (RHEL, dnf-utils) needs-restarting -r
/boot full Too many kernels fill a small /boot, breaking updates df -h /boot
Livepatch Applies some fixes without reboot kpatch / Ubuntu Livepatch
Rollback Boot the previous kernel from the GRUB menu at the menu

Performance and Troubleshooting

Methodology

Fact Value Verify with
USE method For each resource check Utilization, Saturation, Errors vmstat 1, iostat -xz 1, dmesg
The four resources CPU, memory, disk I/O, network top, free, iostat, ss -s
60-second checklist uptime, dmesg, vmstat 1, mpstat -P ALL 1, pidstat 1, iostat -xz 1, free -m, sar -n DEV 1, top run in order
Saturation signal Work queued behind a busy resource: run queue, aqu-sz, swap-in vmstat r, iostat aqu-sz
Utilization trap 100% busy is not always the limit; a queue behind it is %util with aqu-sz
Live vs history vmstat/iostat show now; sar reads the archive from sysstat sar -f /var/log/sa/saNN
sysstat collector sar needs the collector enabled and running systemctl status sysstat
Load average Runnable plus uninterruptible (D) tasks over 1, 5, 15 min uptime, cat /proc/loadavg
First move Read dmesg for OOM, resets and errors before tuning anything dmesg -T
Baseline A number means nothing without the normal value to compare it to keep sar history
Package sar, iostat, mpstat, pidstat come from sysstat rpm -q sysstat / dpkg -l sysstat

CPU and Load

Fact Value Verify with
Load average Runnable plus uninterruptible (D) tasks, averaged over 1, 5, 15 min uptime, cat /proc/loadavg
Load vs cores Load equal to core count is full; above it, tasks wait nproc
Run queue vmstat r above CPU count means CPU saturation vmstat 1
us User-space CPU time (application code) mpstat -P ALL 1
sy Kernel CPU time (syscalls, drivers) mpstat
wa Idle CPU waiting on I/O; high wa is a disk problem vmstat, iostat
st Steal: time the hypervisor gave to another guest vmstat, top
id Idle: CPU with nothing to run mpstat
Context switch cswch/s voluntary (blocked), nvcswch/s involuntary (preempted) pidstat -w 1
High sy Excessive syscalls, interrupts or lock contention pidstat, strace -c
cgroup throttling cpu.max caps a group; throttling appears in cpu.stat cat /sys/fs/cgroup/.../cpu.stat
/proc/loadavg Load trio, running/total tasks, last PID cat /proc/loadavg

Memory

Fact Value Verify with
available vs free available estimates what a new process can get, including reclaimable cache; free is unused free -h
buff/cache Page cache and buffers; reclaimable under pressure, not waste free -h, /proc/meminfo
Low free is normal Linux uses spare RAM for cache; judge by available free -h
Swap Overflow to disk; si/so in vmstat show active paging vmstat 1, free -h
swappiness 0 to 200, higher favours swapping anon pages; default 60 cat /proc/sys/vm/swappiness
RSS Resident set: physical RAM a process uses now /proc/PID/status VmRSS
VSZ Virtual size: address space reserved, not all backed by RAM /proc/PID/status VmSize
PSS Proportional set: RSS with shared pages divided among sharers /proc/PID/smaps_rollup
OOM killer Frees memory by killing a task; picks by oom_score dmesg, /proc/PID/oom_score
oom_score_adj -1000 (never kill) to 1000 (kill first) cat /proc/PID/oom_score_adj
Exit 137 128 + 9 (SIGKILL); a container OOM shows this echo $? after a kill
Committed_AS Total memory promised to all processes grep Committed /proc/meminfo

Virtual Memory

Fact Value Verify with
Virtual address space Per-process, mapped to physical frames lazily /proc/PID/maps
Page Fixed unit of memory, 4 KiB on x86_64 getconf PAGE_SIZE
Page table + TLB Maps virtual to physical; TLB caches recent translations /proc/PID/status VmPTE
Minor fault Page mapped without disk I/O (cache hit, zero page) /proc/PID/stat, time -v
Major fault Page required a disk read (file or swap-in) time -v, vmstat si
Demand paging RAM is assigned when a page is first touched, not at allocation smaps after touching
RSS vs VSZ RSS is mapped-and-resident; VSZ is the whole address space /proc/PID/status
Overcommit Kernel may promise more than RAM plus swap sysctl vm.overcommit_memory
Committed_AS Sum of memory promised; compared to CommitLimit grep Commit /proc/meminfo
Page cache File pages kept in RAM; Dirty awaits writeback grep Dirty /proc/meminfo
fsync Forces dirty pages to stable storage strace -e fsync
THP Transparent huge pages, 2 MiB, reduce TLB misses /sys/kernel/mm/transparent_hugepage/enabled
Slab Kernel object cache (inodes, dentries) slabtop -o

Disk I/O

Fact Value Verify with
Tool iostat -xz (extended, skip idle devices), from sysstat iostat -xz 1
r/s w/s Reads and writes completed per second (IOPS) iostat -xz 1
rkB/s wkB/s Throughput in KB per second iostat -xz 1
r_await w_await Average ms per read/write, including queue time iostat -xz 1
aqu-sz Average queue depth; high means requests are waiting iostat -xz 1
%util Fraction of time the device had at least one request iostat -xz 1
%util caveat 100% is not saturation on SSDs and arrays that serve in parallel pair with aqu-sz
Per-process I/O iotop needs task_delayacct enabled iotop -o
iowait CPU idle time waiting on I/O; a symptom, not a cause iostat, vmstat wa
First check iostat -xz 1, then find the writer with iotop or pidstat -d run in order
Deleted open file Space held by a process still writing to an unlinked file lsof +L1

Limits and File Descriptors

Fact Value Verify with
Soft vs hard Soft is the enforced limit; hard is the ceiling a user may raise the soft to ulimit -Sn, ulimit -Hn
Open files (-n) Per-process file descriptor limit; default soft often 1024 ulimit -n
EMFILE Too many open files: this process hit its -n limit /proc/PID/limits
ENFILE System-wide file table full (rare); governed by fs.file-max sysctl fs.file-max
file-nr Allocated, unused, and max open file handles system-wide cat /proc/sys/fs/file-nr
Processes (-u) RLIMIT_NPROC: max processes per real user id ulimit -u
EAGAIN on fork Resource temporarily unavailable: hit -u or pid_max ulimit -u
pid_max System-wide ceiling on process ids cat /proc/sys/kernel/pid_max
Shell limits ulimit sets the calling shell and its children ulimit -a
PAM limits /etc/security/limits.conf and limits.d for login sessions man limits.conf
systemd limits LimitNOFILE=, LimitNPROC= in the unit; ulimit does not reach services systemctl show -p LimitNOFILE UNIT
Live limits A running process shows its effective limits cat /proc/PID/limits

Profiling and Tracing

Fact Value Verify with
strace Traces syscalls of one process; high overhead strace -f -p PID
strace -c Summarises syscall counts, time and errors strace -c cmd
strace cost Stops the target on every syscall; not for hot production paths measure before use
perf stat Hardware and software counters for a command perf stat -- cmd
perf record Samples the CPU; perf report shows hot functions perf record -g -- cmd
perf top Live system-wide CPU profile perf top
Sampling vs tracing perf samples periodically (low overhead); strace traces every event choose by overhead
eBPF In-kernel programs for low-overhead tracing bpftrace, bcc tools
bcc tools execsnoop, opensnoop, biolatency, tcplife execsnoop-bpfcc
Flame graph Folded perf stacks rendered as nested bars perf script + FlameGraph
Which tool syscalls → strace; CPU → perf; system-wide, low cost → eBPF match to the question
Symbols Profiling needs debug symbols to name functions -debuginfo/-dbgsym

Monitoring and Capacity

Fact Value Verify with
SLI A measured indicator: latency, error rate, availability dashboards, logs
SLO The target for an SLI, for example 99.9% success agreed internally
SLA The contract with consequences if the SLO is missed contract
Error budget The allowed failure under the SLO (0.1% for 99.9%) derived from SLO
Health check A cheap probe of liveness and readiness curl, systemctl is-active
Liveness vs readiness Alive means the process runs; ready means it can serve probe endpoints
Agent A collector runs on each host (node_exporter, the Datadog agent) on-host service
Agentless A central poller scrapes or SSHes in central config
Baseline The normal value a metric is judged against sar history
Headroom Spare capacity kept for spikes and failures capacity plan

Tuning

Fact Value Verify with
tuned Daemon that applies workload profiles (RHEL family) tuned-adm active
List profiles Show available and current profile tuned-adm list
Recommend Suggest a profile for this host tuned-adm recommend
Apply Switch profile, persists across reboot tuned-adm profile NAME
Common profiles throughput-performance, latency-performance, virtual-guest, powersave tuned-adm list
sysctl Set individual kernel parameters sysctl -w, /etc/sysctl.d
Persist sysctl A .conf in /etc/sysctl.d, applied with sysctl --system sysctl --system
Measure first Tune the proven bottleneck, one change at a time vmstat, iostat
Ubuntu No tuned by default; use sysctl and scheduler settings sysctl -a

Network Storage

NFS

Fact Value Verify with
Server package nfs-utils (RHEL), nfs-kernel-server (Ubuntu) rpm -q nfs-utils
Client package nfs-utils (RHEL), nfs-common (Ubuntu) mount.nfs -V
Exports file /etc/exports defines shared directories and who may mount cat /etc/exports
Apply exports Re-read and show the export table exportfs -rav
Current exports What the server is exporting now exportfs -s
List from client Show a server's exports showmount -e SERVER
Default version NFSv4.2 over TCP on modern distros findmnt -o FSTYPE,OPTIONS
Port NFSv4 uses a single port, 2049 ss -tlnp | grep 2049
hard mount Retries forever if the server stops; process blocks in D mount option hard
soft mount Fails I/O after retries; risks data corruption mount option soft
Hang signature D state, WCHAN rpc_wait_bit_killable, load rising ps -o stat,wchan
Boot-safe mount _netdev and nofail in /etc/fstab man nfs

Autofs

Fact Value Verify with
Package autofs on both families rpm -q autofs
Master map /etc/auto.master and /etc/auto.master.d/* cat /etc/auto.master
Mount point Master map ties a directory to a map file ls /etc/auto.master.d
Direct map Absolute paths, key /- man 5 autofs
Indirect map A base directory with keys as subdirectories the map file
On-demand Mounts on access, not at boot findmnt after ls
Idle timeout Unmounts after inactivity (default 300s) --timeout
Service Reload maps by restarting the service systemctl restart autofs

Samba and CIFS

Fact Value Verify with
Protocol SMB (formerly CIFS); default SMB3 mount -o vers=3.0
Client package cifs-utils on both families rpm -q cifs-utils
Server config /etc/samba/smb.conf, one section per share testparm -s
Samba user A separate password store, not /etc/passwd smbpasswd -a
Mount type -t cifs, with credentials= for the password findmnt -t cifs
Credentials file username=/password=, mode 600 chmod 600

iSCSI and NBD

Fact Value Verify with
Exports A block device, not a filesystem lsblk -S on the client
Target / initiator Server side / client side targetcli / iscsiadm
IQN Name identifying a target or initiator /etc/iscsi/initiatorname.iscsi
ACL Restricts a target to specific initiator IQNs (TCP 3260) targetcli .../acls

Containers

Namespaces

Fact Value Verify with
Count Eight types: mnt, pid, net, uts, ipc, user, cgroup, time ls /proc/self/ns/
mnt Isolates the mount table (each container's own filesystem view) unshare --mount
pid Isolates process ids; the first process becomes PID 1 unshare --pid --fork
net Isolates interfaces, routes, ports, firewall unshare --net
uts Isolates hostname and domain name unshare --uts
ipc Isolates System V IPC and POSIX message queues unshare --ipc
user Isolates uid/gid; maps container root to an unprivileged host uid unshare --user
cgroup Isolates the cgroup root the process sees unshare --cgroup
Identity A namespace is an inode number under /proc/PID/ns/ ls -l /proc/self/ns/net
List them Show namespaces and the processes in each lsns
Create Start a process in new namespaces unshare
Join Enter the namespaces of a running process nsenter -t PID
Syscalls clone, unshare, setns create and join namespaces man 7 namespaces

Cgroups

Fact Value Verify with
Version v2 is the unified hierarchy; v1 had a tree per controller stat -fc %T /sys/fs/cgroup
Mount point A single tree at /sys/fs/cgroup mount | grep cgroup2
Controllers cpu, memory, io, pids, cpuset, hugetlb cat /sys/fs/cgroup/cgroup.controllers
Enable in children Write to cgroup.subtree_control of the parent echo +memory > .../cgroup.subtree_control
Add a process Write its PID to cgroup.procs echo $$ > .../cgroup.procs
CPU limit cpu.max = quota period, in microseconds echo "20000 100000" > cpu.max
CPU throttling cpu.stat counts throttled periods grep throttled .../cpu.stat
Memory limit memory.max is a hard cap; exceeding it triggers cgroup OOM cat .../memory.max
OOM record memory.events counts oom and oom_kill cat .../memory.events
Process limit pids.max caps the number of tasks cat .../pids.max
systemd slices systemd is the cgroup manager; units are scopes and slices systemd-cgls
Exit 137 128 + SIGKILL(9): the OOM killer killed the process dmesg, container exit code

Overlayfs and Chroot

Fact Value Verify with
chroot Changes / for a process and its children chroot <dir> <cmd>
chroot limit Not a security boundary on its own; root can escape man 2 chroot
pivot_root The real container root switch; replaces the mount root man 8 pivot_root
OverlayFS Union filesystem: lower (read-only) under upper (writable) mount -t overlay
lowerdir One or more read-only layers (image layers) mount option
upperdir The single writable layer (container changes) mount option
workdir Empty scratch dir OverlayFS needs on the upper's filesystem mount option
merged The combined view processes actually use the mountpoint
Copy-up Writing a lower file copies it into the upper first edit a merged file
Image layers Each image layer is a lowerdir; the container adds an upperdir podman inspect
Whiteout A deleted lower file is masked by a whiteout in the upper ls -l upperdir

Containers vs VMs

Fact Value Verify with
Container A host process with its own namespaces and cgroups ps on the host finds it
VM A guest kernel on virtual hardware, via a hypervisor virsh list
Kernel Containers share the host kernel; VMs run their own uname -r inside each
Isolation Namespaces plus cgroups vs hardware virtualization /proc/PID/ns/
Startup Container in milliseconds; VM boots a kernel time podman run
Density Many containers per host; fewer VMs memory per instance
Boundary strength A VM's boundary is stronger (own kernel) threat model
Image Container image is layered files; VM image is a full disk podman inspect
Container PID 1 The entrypoint process; must reap and handle signals ps inside
No init by default A bare entrypoint does not reap zombies --init adds one
Guest OS A container has no kernel of its own to boot no init boot logs

Podman and Quadlet

Fact Value Verify with
Daemonless No central daemon; each command is a process podman version
Rootless Runs as a normal user by default podman info | grep rootless
subuid/subgid Ranges a user may map into a container grep $USER /etc/subuid
uid mapping Container root maps to the user; other ids to the subuid range cat /proc/PID/uid_map
Docker CLI podman is command-compatible with docker alias docker=podman
Image build Containerfile (or Dockerfile) with podman build podman build -t x .
Volume Persist data outside the container layer podman volume create
SELinux label :Z relabels a bind mount for the container -v /data:/data:Z
skopeo Inspect and copy images between registries skopeo inspect
Quadlet .container unit files run containers as systemd services systemctl start x
Quadlet path /etc/containers/systemd/ (system), ~/.config/containers/systemd/ (user) drop the unit there

Virtualization and Provisioning

KVM and libvirt

Fact Value Verify with
KVM Kernel module using CPU virtualization (VT-x, AMD-V) lsmod | grep kvm
CPU support vmx (Intel) or svm (AMD) flag, and /dev/kvm grep -E 'vmx|svm' /proc/cpuinfo
QEMU Emulates the virtual hardware for the guest qemu-system-x86_64 --version
libvirt Management API and daemon over KVM/QEMU systemctl status libvirtd
virsh CLI to libvirt: list, start, define domains virsh list --all
Host check Validates KVM readiness virt-host-validate
Default network NAT network default, 192.168.122.0/24 virsh net-list
Snapshot Point-in-time state of a domain virsh snapshot-create-as

VM Images and Cloning

Fact Value Verify with
qcow2 QEMU copy-on-write: thin, snapshots, backing files qemu-img info disk.qcow2
raw Full flat file; fastest, no thin provisioning qemu-img info disk.raw
Convert Change format between qcow2 and raw qemu-img convert
virtio Paravirtual disk and network drivers, faster than emulated lspci in guest
Template A cleaned base image cloned for new VMs virt-clone, virt-sysprep
machine-id Must be unique per host; reset on clone cat /etc/machine-id
SSH host keys Must be unique per host; regenerate on clone ssh-keygen -A
virt-sysprep Cleans an image (logs, keys, machine-id) for templating virt-sysprep -a img

Cloud-init and Kickstart

Fact Value Verify with
cloud-init Configures a cloud image on first boot cloud-init status
user-data The #cloud-config YAML: users, packages, commands cloud-init schema
First-boot only Runs once per instance, keyed by instance-id /var/lib/cloud/instance
Validate Check a cloud-config before use cloud-init schema --config-file
Re-run Reset state so next boot is a first boot cloud-init clean
Kickstart Automates a full RHEL install (Anaconda) inst.ks=URL boot arg
bootc Image-mode RHEL: boot from an OCI image, transactional bootc status