The sysadmin set, grouped by what you are trying to do rather than alphabetically — with the flags that actually get used.
Moving around and seeing what is there. ls -lh and find do most of the work; the rest is knowing which flag saves the second command.
| Command | What it does | Typical use |
|---|---|---|
| ls -lhtr | Long listing, human sizes, oldest last — newest at the bottom where the cursor is | ls -lhtr /var/log |
| ls -lda | Show a directory's own entry rather than its contents | ls -lda /etc/ssl |
| cd - | Jump back to the previous directory | cd - |
| pwd -P | Physical path, resolving symlinks | pwd -P |
| tree -L 2 -d | Directory tree, two levels, directories only | tree -L 2 -d /opt |
| stat | Inode, size, permissions and all three timestamps | stat /etc/passwd |
| file | What a file actually is, by content not extension | file /bin/ls |
| readlink -f | Resolve a symlink chain to its final target | readlink -f $(which python3) |
| basename / dirname | Split a path — useful in scripts | dirname /a/b/c.txt |
| cp -a | Archive copy: preserves mode, owner, timestamps, links | cp -a /etc/nginx /backup/ |
| mv -n | Move without overwriting an existing target | mv -n a.log archive/ |
| rsync -avh --progress | Copy only what changed, with a progress bar | rsync -avh src/ dst/ |
| rsync -avh --delete | Mirror — removes files gone from the source. Dry-run first | rsync -avhn --delete a/ b/ |
| ln -s | Symbolic link | ln -s /opt/app/current /usr/local/bin/app |
| shred -u | Overwrite then remove — for keys on spinning disks | shred -u secret.key |
Numeric mode is three digits of read(4) write(2) execute(1). The fourth digit is setuid(4), setgid(2), sticky(1) — and the sticky bit on a shared directory is what stops users deleting each other's files.
| Command | What it does | Typical use |
|---|---|---|
| chmod 640 | Owner read/write, group read, others nothing | chmod 640 /etc/app/secrets.conf |
| chmod u+x,g-w | Symbolic form — change only what you name | chmod u+x deploy.sh |
| chmod -R g+rX | Recursive; capital X adds execute only to directories | chmod -R g+rX /srv/www |
| chmod 1777 | Sticky bit — only the owner can delete their own files | chmod 1777 /tmp |
| chmod 2775 | setgid on a directory — new files inherit the group | chmod 2775 /srv/shared |
| chown -R user:group | Change owner and group recursively | chown -R www-data:www-data /srv/www |
| umask 027 | Default mask for new files in this shell | umask 027 |
| getfacl / setfacl | Per-user ACLs beyond the owner/group/other model | setfacl -m u:deploy:rx /srv/app |
| lsattr / chattr +i | Immutable flag — even root cannot modify until cleared | chattr +i /etc/resolv.conf |
| sudo -l | What can this user actually run as root? | sudo -l -U deploy |
| id | UID, GID and every supplementary group | id deploy |
| namei -l | Permissions of every component in a path — finds the one bad directory | namei -l /srv/app/data/f |
The pipeline tools. Worth knowing well: most log investigation is grep to narrow, awk to extract, sort | uniq -c to count.
| Command | What it does | Typical use |
|---|---|---|
| grep -rn --include='*.py' | Recursive search with line numbers, one file type | grep -rn --include='*.py' TODO . |
| grep -c / -l / -v | Count matches / list files only / invert the match | grep -c ERROR app.log |
| grep -A3 -B3 | Show context lines after and before each hit | grep -A3 -B3 Traceback app.log |
| grep -P '\d{3}' | Perl regex — the only way to get \d and lookarounds | grep -oP 'status=\K\d+' access.log |
| awk '{print $7}' | Print a field. The default separator is any run of whitespace | awk '{print $7}' access.log |
| awk -F: '$3>=1000{print $1}' | Filter on a field with a custom separator | awk -F: '$3>=1000{print $1}' /etc/passwd |
| awk '{s+=$1} END{print s}' | Sum a column | du -s * | awk '{s+=$1} END{print s}' |
| sed -i.bak 's/a/b/g' | In-place replace, keeping a .bak. Always keep the backup | sed -i.bak 's/8080/9090/g' app.conf |
| sed -n '100,120p' | Print a line range without printing everything else | sed -n '100,120p' huge.log |
| sort -k2 -n -r | Sort by field 2, numeric, descending | sort -k2 -n -r sizes.txt |
| sort | uniq -c | sort -rn | The counting idiom — top offenders in any log | awk '{print $1}' a.log | sort | uniq -c | sort -rn | head |
| cut -d, -f1,3 | Fields from delimited text, when awk is overkill | cut -d, -f1,3 data.csv |
| tr -d '\r' | Strip characters — this one fixes CRLF files | tr -d '\r' < win.txt > unix.txt |
| tail -f / -F | Follow a file; -F survives log rotation | tail -F /var/log/app.log |
| head -n -5 | Everything except the last 5 lines | head -n -5 file.txt |
| wc -l | Line count | wc -l access.log |
| jq -r '.items[].name' | Query JSON. -r drops the quotes | kubectl get po -o json | jq -r '.items[].metadata.name' |
| column -t | Align whitespace-separated output into columns | mount | column -t |
| diff -u / vimdiff | Unified diff between two files | diff -u old.conf new.conf |
A process is doing one of: running, waiting on I/O (D), sleeping (S), or already dead (Z). Which one it is decides where you look next.
| Command | What it does | Typical use |
|---|---|---|
| ps aux --sort=-%mem | Every process, biggest memory first | ps aux --sort=-%mem | head |
| ps -eo pid,ppid,stat,wchan:20,cmd | State and the kernel function it is blocked in | ps -eo pid,stat,wchan:20,cmd |
| pgrep -af | Find PIDs by pattern, showing the full command line | pgrep -af nginx |
| pkill -f -TERM | Signal by full-command-line match. Check with pgrep first | pkill -f -TERM 'python worker.py' |
| kill -TERM / -KILL | 15 asks politely, 9 cannot be caught or cleaned up after | kill -TERM 4412 |
| kill -HUP | Reload config without a restart, for daemons that support it | kill -HUP $(pidof nginx) |
| kill -l | List signal names and numbers | kill -l |
| nice / renice | Scheduling priority, -20 (highest) to 19 | renice 10 -p 4412 |
| ionice -c3 | Idle I/O class — for backups that must not disturb production | ionice -c3 rsync -a src/ dst/ |
| nohup … & | Survive the terminal closing | nohup ./long-job.sh & |
| timeout 30s | Kill a command that runs too long. Use it in every cron job | timeout 30s curl https://api/health |
| lsof -p PID | Every file, socket and pipe a process holds open | lsof -p 4412 |
| lsof -i :8080 | Which process owns a port | lsof -i :8080 |
| fuser -vm /mnt | Who is using a mount point — before you unmount | fuser -vm /mnt/data |
| strace -c -p PID | Syscall summary of a running process | strace -c -p 4412 |
| pstree -p | Process tree with PIDs — shows who forked whom | pstree -p 1 |
Account state lives in /etc/passwd, /etc/shadow and /etc/group. Everything below just edits those safely.
| Command | What it does | Typical use |
|---|---|---|
| useradd -m -s /bin/bash | Create a user with a home directory and a real shell | useradd -m -s /bin/bash deploy |
| useradd -r -s /usr/sbin/nologin | System account that cannot log in — for services | useradd -r -s /usr/sbin/nologin appsvc |
| usermod -aG | Add to a group. Forget the -a and you replace every other group | usermod -aG docker deploy |
| userdel -r | Delete the user and their home directory | userdel -r olduser |
| passwd -l / -S | Lock an account / show its password status | passwd -S deploy |
| chage -l | Password ageing and expiry for an account | chage -l deploy |
| groupadd / gpasswd -a | Create a group, add a member | gpasswd -a deploy sudo |
| getent passwd | Query users through NSS — sees LDAP/SSSD, unlike grepping the file | getent passwd deploy |
| w / who | Who is logged in and what they are running | w |
| last -a | Login history from wtmp | last -a | head |
| lastb | Failed login attempts | lastb | head |
| loginctl list-sessions | systemd's view of active sessions | loginctl list-sessions |
Three families. Know which one you are on before you type — /etc/os-release tells you.
| Command | What it does | Typical use |
|---|---|---|
| apt update && apt upgrade | Refresh the index, then upgrade (Debian/Ubuntu) | apt update && apt upgrade -y |
| apt list --installed | What is installed | apt list --installed | grep nginx |
| apt-cache policy | Installed version, candidate version, and which repo | apt-cache policy nginx |
| dpkg -l / -L / -S | List packages / files in a package / which package owns a file | dpkg -S /usr/sbin/nginx |
| dnf install / update | RHEL 8+, Fedora, Rocky, Alma | dnf install -y nginx |
| dnf history / history undo | Transaction log, and rolling one back | dnf history undo last |
| rpm -qa / -ql / -qf | Query all / files in a package / owner of a file | rpm -qf /usr/sbin/nginx |
| rpm -q --changelog | Why a version exists — includes the CVE it fixed | rpm -q --changelog openssl | head |
| yum / zypper / apk | RHEL 7 / SUSE / Alpine equivalents | apk add --no-cache curl |
| needs-restarting -r | Does this box need a reboot after patching? (RHEL) | needs-restarting -r |
| ls /var/run/reboot-required | Same question on Debian/Ubuntu | cat /var/run/reboot-required |
Two different 'full' conditions: out of blocks (df -h) and out of inodes (df -i). Check both — millions of tiny files exhaust inodes first.
| Command | What it does | Typical use |
|---|---|---|
| df -h / -i | Free space by blocks / by inodes | df -h; df -i |
| du -sh * | sort -h | What is taking the space in this directory | du -sh * | sort -h | tail |
| du -xh --max-depth=1 / | Top-level usage without crossing into other filesystems | du -xh --max-depth=1 / | sort -h |
| lsblk -o NAME,SIZE,ROTA,MOUNTPOINT | Block devices, and whether they are rotational | lsblk -o NAME,SIZE,ROTA,MOUNTPOINT |
| blkid | UUIDs and filesystem types — what to put in /etc/fstab | blkid /dev/sdb1 |
| mount -o remount,rw / | Remount read-write, e.g. in rescue mode | mount -o remount,rw / |
| findmnt | Mounts as a tree, with the options actually in effect | findmnt /var |
| mkfs.ext4 / mkfs.xfs | Create a filesystem | mkfs.xfs -L data /dev/sdb1 |
| xfs_growfs / resize2fs | Grow a filesystem after growing the volume | xfs_growfs /data |
| fsck -n | Check without repairing. Never fsck a mounted filesystem | fsck -n /dev/sdb1 |
| pvs / vgs / lvs | LVM at a glance — physical, group, logical | vgs; lvs |
| lvextend -r -L +50G | Grow a logical volume and its filesystem in one step | lvextend -r -L +50G /dev/vg0/data |
| iostat -x 1 | Per-device await and utilisation — is the disk the bottleneck? | iostat -x 1 |
| lsof +L1 | Deleted files still held open — why df and du disagree | lsof +L1 |
find is a query language. The order of predicates matters: it evaluates left to right and stops early, so put the cheap tests first.
| Command | What it does | Typical use |
|---|---|---|
| find . -name '*.log' -mtime +30 | Files matching a name, older than 30 days | find /var/log -name '*.log' -mtime +30 |
| find . -size +100M | Files over a size | find / -xdev -size +100M 2>/dev/null |
| find . -type f -newer ref | Changed more recently than a reference file | find /etc -type f -newer /tmp/mark |
| find … -delete | Delete matches. Run it without -delete first, every time | find /tmp -name 'core.*' -mtime +7 -delete |
| find … -print0 | xargs -0 | Safe with spaces and newlines in filenames | find . -name '*.gz' -print0 | xargs -0 rm |
| find … -exec … + | One invocation for many files, not one per file | find . -name '*.c' -exec grep -l TODO {} + |
| find / -xdev | Stay on one filesystem — stops it wandering into /proc and NFS | find / -xdev -name core |
| find . -perm -4000 | setuid binaries — a standard audit sweep | find / -xdev -perm -4000 -ls |
| locate / updatedb | Instant filename search from a prebuilt index | locate nginx.conf |
| which / type -a | Where a command comes from; type -a shows aliases too | type -a ls |
cron for wall-clock jobs, systemd timers for anything that needs dependencies, logging or a missed-run catch-up.
| Command | What it does | Typical use |
|---|---|---|
| crontab -l / -e / -u | List, edit, or act on another user's crontab | crontab -l -u deploy |
| systemctl list-timers --all | Every timer, when it last ran and when it runs next | systemctl list-timers --all |
| systemd-analyze blame | Which units made the boot slow | systemd-analyze blame | head |
| systemd-analyze critical-chain | The dependency path that determined boot time | systemd-analyze critical-chain |
| at now + 1 hour | One-off scheduled command | echo 'systemctl restart app' | at now + 1 hour |
| run-parts --test | What would /etc/cron.daily actually run? | run-parts --test /etc/cron.daily |
| uptime / who -b | How long since boot, and when it booted | who -b |
| last reboot | Reboot history | last reboot | head |
ifconfig, netstat and route are deprecated and missing on modern minimal images. The ip and ss equivalents are below.
| Command | What it does | Typical use |
|---|---|---|
| ip a | Interfaces and addresses (replaces ifconfig) | ip -br a |
| ip r | Routing table (replaces route -n) | ip r get 8.8.8.8 |
| ip -s link | Per-interface counters, including errors and drops | ip -s link show eth0 |
| ss -tulpn | Listening TCP/UDP sockets with the owning process (replaces netstat) | ss -tulpn |
| ss -s | Socket summary — how many in each state | ss -s |
| ss -tan state time-wait | wc -l | Count sockets in one state | ss -tan state time-wait | wc -l |
| dig +short / +trace | DNS answer only / the full delegation path | dig +trace api.example.com |
| dig @1.1.1.1 | Ask a specific resolver — proves whether it is your resolver | dig @1.1.1.1 example.com |
| curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' | Status and timing without the body | curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' https://api/health |
| curl -v --resolve host:443:IP | Test one backend directly, bypassing DNS | curl -v --resolve api:443:10.0.1.5 https://api/ |
| tcpdump -nni any port 443 -w f.pcap | Capture to a file for Wireshark | tcpdump -nni any port 443 -c 100 |
| mtr -rw | traceroute and ping combined, in a report | mtr -rw 8.8.8.8 |
| nc -zv | Is the port open? The quickest connectivity test there is | nc -zv db.internal 5432 |
| ethtool -S | NIC statistics — drops, errors, ring exhaustion | ethtool -S eth0 | grep -i drop |
| nft list ruleset | Firewall rules (nftables; iptables-save on older systems) | nft list ruleset |