Linux cheat sheet
198 commands from every lesson of Linux — Level by Level, on one page.
Where am I, who am I?
whoami | Your username |
hostname | The machine's name |
pwd | Print working directory (where you are) |
uname -a | Kernel and architecture |
cat /etc/os-release | Which Linux distribution and version |
Getting help
man ls | The manual page for ls (q to quit, / to search) |
ls --help | Short built-in help for most commands |
type cd | Is it a built-in, an alias, or a program? |
apropos network | Search manual page descriptions |
Save typing
Tab / Tab Tab | Complete a name / list the options |
↑ / ↓ | Previous / next command |
Ctrl+R | Search your command history |
history | tail -20 | Your last 20 commands |
Ctrl+C / Ctrl+L | Cancel the running command / clear the screen |
Move around
ls -lah | List all files, long format, human-readable sizes |
cd /var/log | Go to an absolute path |
cd .. / cd - / cd | Up one level / previous directory / home |
tree -L 2 | Directory tree two levels deep (may need installing) |
Create, copy, move, delete
mkdir -p app/config/dev | Create nested directories |
touch notes.txt | Create an empty file (or update its timestamp) |
cp -r src/ backup/ | Copy a directory recursively |
mv old.txt new.txt | Rename (or move) a file |
rm -i file.txt | Delete, asking first |
rm -r dir/ | Delete a directory and its contents (no undo!) |
Find things
find /etc -name '*.conf' | Files by name pattern |
find /var/log -size +100M | Files larger than 100 MB |
find . -mtime -1 | Modified in the last 24 hours |
ln -s /opt/app/current app | Create a symbolic link |
Read
less /var/log/syslog | Page through a file (/ search, n next, G end, q quit) |
head -20 file | First 20 lines |
tail -50 file | Last 50 lines |
tail -f /var/log/syslog | Follow a file as it grows (Ctrl+C to stop) |
Search
grep -i error app.log | Lines containing 'error', any case |
grep -rn 'listen' /etc/nginx/ | Search recursively, show file and line number |
grep -v DEBUG app.log | Lines NOT matching |
grep -C 3 'Traceback' app.log | 3 lines of context around each match |
Pipes & redirection
cmd | grep x | wc -l | Count lines matching x in cmd's output |
cmd > out.txt / cmd >> out.txt | Write / append output to a file |
cmd 2> errors.txt | Send only errors to a file |
cmd > all.txt 2>&1 | Output and errors to one file |
sort | uniq -c | sort -rn | head | Count and rank repeated lines |
Who am I?
id | Your user ID, group ID and all groups |
groups asha | Groups a user belongs to |
getent passwd asha | A user's account entry |
Change permissions and owners
chmod 640 app.env | rw- for owner, r-- for group, --- for others |
chmod u+x deploy.sh | Add execute for the owner |
chmod -R g+w shared/ | Recursively add group write |
sudo chown app:app /srv/app | Change owner and group |
Users and sudo
sudo adduser deploy | Create a user (Debian/Ubuntu helper) |
sudo usermod -aG docker deploy | Add a user to a group (-a = append!) |
sudo -l | What am I allowed to run with sudo? |
sudo -u app whoami | Run a command as another user |
Processes
ps aux --sort=-%mem | head | Top memory users |
ps -ef --forest | Processes with parent/child tree |
top (or htop) | Live view; press M (memory), P (CPU), q to quit |
pgrep -a nginx | Find processes by name |
kill <pid> / kill -9 <pid> | Ask to stop (SIGTERM) / force kill (SIGKILL) |
Services (systemd)
systemctl status nginx | Running? Since when? Last log lines |
sudo systemctl restart nginx | Restart a service |
sudo systemctl enable --now nginx | Start now AND at every boot |
systemctl list-units --type=service --state=failed | Services that failed |
Packages
sudo apt update && sudo apt install -y nginx | Debian/Ubuntu |
sudo dnf install -y nginx | RHEL/Rocky/Alma/Fedora |
apt list --installed | grep nginx | Is it installed? (Debian/Ubuntu) |
dpkg -L nginx / rpm -ql nginx | Files a package installed |
Commands you'll use
systemctl status nginx | Is it running? What went wrong? |
sudo nginx -t | Check nginx config syntax, with file and line number |
journalctl -u nginx --since '10 min ago' | The service's recent logs |
sudo ss -ltnp | Which process listens on which TCP port |
curl -I http://localhost | HTTP status code only |
sudo du -ah /var/log | sort -rh | head | Largest files and folders under /var/log |
Units
systemctl cat nginx | Show the unit file(s) actually in use |
sudo systemctl edit nginx | Create a drop-in override (survives package upgrades) |
sudo systemctl daemon-reload | Reload unit files after changing them |
systemctl list-dependencies multi-user.target | What starts at boot |
systemctl --failed | Everything that failed |
Journal
journalctl -u nginx -f | Follow one service's logs |
journalctl -u nginx --since '1 hour ago' -p warning | Warnings and worse, last hour |
journalctl -b -1 -p err | Errors from the previous boot |
journalctl -k | Kernel messages (like dmesg) |
journalctl --disk-usage / sudo journalctl --vacuum-size=500M | Journal size / shrink it |
Boot
systemd-analyze blame | head | Slowest units at boot |
systemd-analyze critical-chain | The chain that delayed boot |
Where did the space go?
df -h | Free space per mounted filesystem |
df -i | Free inodes (file slots) per filesystem |
sudo du -xh --max-depth=1 / | sort -rh | head | Biggest top-level directories on the root filesystem |
sudo lsof +L1 | Deleted files still held open (space not yet freed) |
sudo find / -xdev -size +1G -type f 2>/dev/null | Files over 1 GB on this filesystem |
Disks & mounts
lsblk -f | Disks, partitions, filesystems, mount points |
findmnt /var/lib | Which filesystem a path lives on |
sudo mount -a | Mount everything in /etc/fstab (test after editing it!) |
LVM grow
sudo pvs; sudo vgs; sudo lvs | Physical volumes, volume groups, logical volumes |
sudo lvextend -r -L +10G /dev/vg0/data | Grow a logical volume AND its filesystem |
Interfaces & routes
ip -br addr | Interfaces and IPs, one line each |
ip route | Routing table (look for 'default via') |
ip route get 8.8.8.8 | Which interface and gateway a destination would use |
ip -s link show eth0 | Packet and error counters |
Sockets & connectivity
sudo ss -ltnp | Listening TCP ports and their processes |
ss -tn state established | Current TCP connections |
nc -vz db.internal 5432 | Can I open a TCP connection to this port? |
curl -v https://api.example.com/health | Full HTTP/TLS exchange |
DNS & packets
dig +short api.example.com | Resolve a name |
resolvectl status | Which DNS servers this host uses (systemd-resolved) |
sudo tcpdump -ni any port 5432 | Watch packets to/from port 5432 |
sudo nft list ruleset / sudo iptables -S | Firewall rules |
The 60-second checklist
uptime | Load averages: 1, 5, 15 minutes |
sudo dmesg -T | tail | Kernel errors: OOM kills, disk or network errors |
vmstat 1 5 | Run queue, memory, swap, I/O wait, context switches |
mpstat -P ALL 1 3 | Per-CPU usage: one hot CPU? |
pidstat 1 3 | Which processes use CPU |
iostat -xz 1 3 | Per-disk utilisation and latency (await) |
free -h | Memory: look at 'available' |
sar -n DEV 1 3 | Network throughput per interface |
top (or htop) | Everything live |
Install the tools
sudo apt install -y sysstat / sudo dnf install -y sysstat | Provides iostat, mpstat, pidstat, sar |
Keys
ssh-keygen -t ed25519 -C 'asha@laptop' | Create a key pair (protect it with a passphrase) |
ssh-copy-id asha@web-01 | Install your public key on a server |
ssh-add ~/.ssh/id_ed25519 | Load the key into the agent (type the passphrase once) |
ssh -v asha@web-01 | Verbose: see why a login fails |
Getting around
ssh -J bastion.example.com asha@10.0.5.20 | Jump through a bastion host |
ssh -L 8080:localhost:80 asha@web-01 | Local port forward: laptop:8080 → web-01:80 |
rsync -avz --progress ./site/ asha@web-01:/srv/site/ | Sync a directory (only changed files) |
scp backup.tar.gz asha@web-01:/tmp/ | Copy one file |
tmux
tmux new -s work | Start a named session |
Ctrl+b d | Detach (the session keeps running) |
tmux attach -t work | Re-attach later |
Ctrl+b % / Ctrl+b " | Split pane vertically / horizontally |
Your first five commands on any sick server
uptime; free -h; df -h; df -i | Load, memory, space and inodes at a glance |
systemctl --failed | Failed services |
journalctl -p err -b --no-pager | tail -30 | Recent errors this boot |
sudo ss -ltnp | What's listening |
sudo dmesg -T | tail -20 | Kernel complaints (OOM, disk, network) |
Kernel & modules
uname -r | Running kernel version |
lsmod | grep br_netfilter | Is a module loaded? |
modinfo overlay | Module details and parameters |
sudo modprobe br_netfilter | Load a module now |
echo br_netfilter | sudo tee /etc/modules-load.d/k8s.conf | Load it at every boot |
sysctl
sysctl net.ipv4.ip_forward | Read one parameter |
sudo sysctl -w vm.swappiness=10 | Change it now (lost at reboot) |
sudo sysctl --system | Apply all files in /etc/sysctl.d/ |
cat /proc/cmdline | Parameters the kernel was booted with |
Namespaces
lsns | List namespaces on the host |
sudo unshare --pid --fork --mount-proc bash | A shell in a new PID namespace (it becomes PID 1) |
sudo unshare --net bash | A shell with its own, empty network stack |
sudo nsenter -t <pid> -n ip addr | Run a command inside another process's network namespace |
ls -l /proc/<pid>/ns | Which namespaces a process belongs to |
cgroups v2
stat -fc %T /sys/fs/cgroup | cgroup2fs = cgroup v2 in use |
systemd-cgls | The cgroup tree |
systemd-cgtop | Live resource use per cgroup |
sudo systemd-run --unit=demo -p MemoryMax=64M -p CPUQuota=20% sleep 600 | Run something in a limited cgroup |
cat /sys/fs/cgroup/<path>/memory.max | A cgroup's memory limit |
Inspect the hardware
lscpu | grep -i -E 'numa|socket|thread' | Sockets, NUMA nodes, threads per core |
numactl --hardware | NUMA nodes, their CPUs, memory and distances |
lscpu -e | Each CPU with its core, socket and NUMA node |
grep -i huge /proc/meminfo | Huge page totals and usage |
Control placement
taskset -c 2,3 ./app | Run on CPUs 2 and 3 only |
numactl --cpunodebind=0 --membind=0 ./app | Run and allocate memory on NUMA node 0 |
sudo sysctl -w vm.nr_hugepages=512 | Reserve 512 × 2 MiB huge pages |
cat /sys/kernel/mm/transparent_hugepage/enabled | Transparent huge pages mode |
perf (CPU profiling)
sudo perf top | Live view of the hottest functions system-wide |
sudo perf record -F 99 -a -g -- sleep 30 | Sample all CPUs at 99 Hz for 30 s, with stacks |
sudo perf report --stdio | head -50 | Where CPU time went |
sudo perf record -F 99 -g -p <pid> -- sleep 30 | Profile one process |
eBPF tools (bcc; Ubuntu names end in -bpfcc)
sudo execsnoop-bpfcc | Every new process, as it starts |
sudo opensnoop-bpfcc -p <pid> | Files a process opens |
sudo biolatency-bpfcc 10 1 | Disk I/O latency histogram over 10 s |
sudo tcpconnect-bpfcc | Outgoing TCP connections as they happen |
sudo runqlat-bpfcc 10 1 | How long tasks wait for a CPU |
bpftrace one-liners
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }' | System calls per process (Ctrl+C to print) |
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s\n", comm); }' | Which programs call execve |
SELinux (RHEL, Rocky, Alma, Fedora)
getenforce / sestatus | Mode: Enforcing, Permissive or Disabled |
ls -Z /var/www/html / ps -eZ | grep nginx | File and process contexts (labels) |
sudo ausearch -m AVC -ts recent | Recent denials from the audit log |
sudo restorecon -Rv /srv/www | Reset files to their policy-defined labels |
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/www(/.*)?' | Teach the policy a new web content path |
sudo setsebool -P httpd_can_network_connect on | Flip a policy boolean permanently |
sudo semanage port -a -t http_port_t -p tcp 8081 | Allow a service type to use another port |
AppArmor (Ubuntu, Debian, SUSE)
sudo aa-status | Loaded profiles and their modes |
sudo journalctl -k | grep -i apparmor | Denials (apparmor="DENIED") |
sudo aa-complain /etc/apparmor.d/<profile> | Log instead of block (for debugging) |
sudo aa-enforce /etc/apparmor.d/<profile> | Enforce again |
Attack surface
sudo ss -ltnup | Everything listening on TCP and UDP |
systemctl list-unit-files --state=enabled | Services that start at boot |
sudo find / -xdev -perm -4000 -type f 2>/dev/null | setuid programs |
SSH & firewall
sudo sshd -t | Validate sshd_config before reloading |
sudo ufw default deny incoming && sudo ufw allow OpenSSH && sudo ufw enable | Default-deny firewall (Ubuntu) |
sudo firewall-cmd --permanent --add-service=https && sudo firewall-cmd --reload | Open HTTPS (RHEL family) |
Updates & audit
sudo apt install unattended-upgrades | Automatic security updates (Ubuntu/Debian) |
sudo systemctl enable --now dnf-automatic.timer | Automatic updates (RHEL family; configure apply_updates) |
sudo auditctl -l | Active audit rules |
sudo ausearch -k identity -ts today | Audit events tagged 'identity' |
Evidence to capture before and after
sudo ss -ltnup > listen.txt | Open ports |
systemctl list-unit-files --state=enabled > enabled.txt | Enabled services |
sudo sysctl -a 2>/dev/null > sysctl.txt | All kernel parameters |
vmstat 1 10 > vmstat.txt; iostat -xz 1 10 > iostat.txt | Performance baseline |
diff before/listen.txt after/listen.txt | What changed |
Load test (from another machine)
ab -n 20000 -c 100 http://<server>/ | Apache Bench: requests/second and latency |
wrk -t4 -c200 -d60s --latency http://<server>/ | wrk: throughput and latency percentiles |