Senior 22 min · March 06, 2026

Linux Command Line Basics — rm / Server Wipe Mistake

One misplaced space in rm -rf / home wipes a server from root.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Production
production tested
May 23, 2026
last updated
1,554
articles · all by Naren
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Core concept: The Linux terminal is a text-based interface where you type commands to control the system.
  • Shell (Bash) translates your typed words into system actions.
  • Every command follows the pattern: command [flags] [arguments].
  • Performance: One command can replace 50 clicks — touch file_{1..50}.txt creates 50 files in milliseconds.
  • Production insight: A single typo like rm -rf / home can delete your entire root filesystem — always double-check paths.
  • Biggest mistake: Running commands as root (sudo) for everything — leads to accidental system-wide changes.
✦ Definition~90s read
What is Linux Command Line Basics?

This article covers the absolute fundamentals of the Linux command line, framed around the infamous rm -rf / mistake — a command that recursively and forcefully deletes everything from the root directory, effectively destroying the operating system without confirmation. You'll learn exactly why that command is catastrophic (it bypasses all safety checks and removes every file the process can access), and more importantly, how to avoid it by understanding the terminal, the shell, and the permission model that governs what you can and cannot do.

Think of the Linux command line like a walkie-talkie conversation with your computer.

The article walks you through the essential commands (ls, pwd, cd, cp, mv, rm, cat, chmod) and explains how pipes, redirection, and command chaining let you compose powerful operations from simple building blocks. It's designed for absolute beginners who need to get productive on any Linux server or desktop — whether you're managing a $5 DigitalOcean droplet, a Raspberry Pi, or a production AWS instance — and want to internalize the safety habits that separate a competent operator from someone who accidentally wipes a database at 3 AM.

Plain-English First

Think of the Linux command line like a walkie-talkie conversation with your computer. Instead of pointing and clicking on icons (that's the GUI, your visual menu), you're typing direct orders and the computer talks back instantly. Just like a chef shouting orders in a kitchen gets faster results than writing everything on a whiteboard, the terminal gets things done faster and more precisely. It feels scary at first — like learning a new language — but you only need about 15 words to hold a full conversation.

Every DevOps engineer, cloud architect, and backend developer you've ever admired spends a big chunk of their day in a black window full of text. That window is the Linux terminal, and it's not some relic from the 1980s that professionals reluctantly use — it's genuinely the fastest, most powerful way to control a computer. Servers that run Netflix, GitHub, and your favourite apps don't have a mouse or a pretty desktop. They only speak one language: the command line.

The problem is that most beginners open a terminal, see a blinking cursor on a blank screen, and immediately feel lost. There's no menu to browse, no buttons to click, and no obvious starting point. Every guide on the internet assumes you already know what a 'shell' is, what 'flags' are, or why there are two different slash characters. That assumption leaves beginners stranded on the first paragraph.

By the end of this article you'll be able to open a terminal and confidently navigate your entire filesystem, create and delete files and folders, read file contents, move things around, and understand file permissions well enough to fix the most common 'Permission denied' errors. You'll also know exactly why each command exists — not just how to type it — so you can adapt when things don't go as planned.

What 'rm -rf /' Actually Does — And Why It's Not a Joke

The Linux command line is a text-based interface to the operating system kernel. At its core, it's a read-evaluate-print loop (REPL) that translates typed commands into system calls via the shell (bash, zsh, etc.). The 'rm' command, short for 'remove', unlinks files or directories from the filesystem by calling the unlink() or rmdir() syscall. The infamous 'rm -rf /' recursively forces deletion starting at the root directory — and because Linux does not enforce a 'safe delete' by default, it will wipe every file it has permission to remove, including critical system binaries, libraries, and configuration files.

In practice, 'rm' operates on inodes, not file names. When you run 'rm file', the kernel decrements the link count on the inode. If the count reaches zero and no process holds the file open, the disk blocks are marked free. The '-f' flag suppresses prompts and ignores nonexistent files; '-r' traverses directories recursively. There is no undo, no trash bin, no confirmation beyond what you explicitly script. The command does not check if the target is a system directory — it simply executes the syscall.

You use the command line when you need precise, scriptable, low-overhead control over a system. It's the foundation for automation, remote administration, and resource-constrained environments. Understanding 'rm' matters because one mistyped character in a production script — like a missing '.' in 'rm -rf ./foo' — can take down an entire fleet of servers. The command line is not a toy; it's a surgical instrument that demands exact syntax.

No 'Are you sure?' by default
Unlike graphical file managers, 'rm' does not move files to a trash folder. Once unlinked, the data is only recoverable via forensic tools — and only if the blocks haven't been overwritten.
Production Insight
A CI/CD pipeline script used 'rm -rf ${BUILD_DIR}/' where BUILD_DIR was empty due to a config error, expanding to 'rm -rf /' and wiping the build server's OS. Symptom: immediate SSH disconnection, all services dead, server unreachable. Rule: always validate variable expansion with 'set -u' and never run 'rm -rf' without a full path prefix check.
Key Takeaway
The shell does not protect you from yourself — 'rm -rf /' will run without confirmation.
Always double-check the target path before executing destructive commands, especially in scripts.
Use 'rm -i' or 'safe-rm' wrappers in shared environments to add a confirmation prompt.
Linux Command Line Basics: rm / Server Wipe Mistake THECODEFORGE.IO Linux Command Line Basics: rm / Server Wipe Mistake Flow from terminal understanding to dangerous rm -rf / command Terminal & Navigation ls, pwd, cd for filesystem orientation File Operations Create, move, copy, delete files Permissions & Reading Understand file permissions and view content Command Chaining Pipes, redirection, and chaining commands rm -rf / Mistake Recursive force delete from root System Monitoring Monitor system after accidental wipe ⚠ Running rm -rf / wipes entire filesystem Always double-check path and use safe aliases THECODEFORGE.IO
thecodeforge.io
Linux Command Line Basics: rm / Server Wipe Mistake
Linux Command Line Basics

Your First Five Minutes: Understanding the Terminal and the Shell

Before you type a single command, you need a mental model of what's actually happening. When you open a terminal application — whether that's GNOME Terminal on Ubuntu, iTerm2 on a Mac, or Windows Terminal running WSL — you're starting a program called a shell. The shell is the translator between you and the operating system kernel.

Think of it this way: the kernel is the engine of your car. You can't reach in and directly touch the pistons. The shell is the steering wheel and pedals — it takes your input and converts it into actions the engine understands.

The most common shell is called Bash (Bourne Again SHell). When you see a prompt like username@hostname:~$, that's Bash waiting for your instructions. The ~ symbol is shorthand for your home directory — the personal folder that belongs to your user. The $ means you're a regular user. If you ever see a # instead, that means you have root (administrator) access, which is powerful and risky.

Every command you type follows the same basic recipe: command [options] [arguments]. The command is the action. Options (also called flags) modify how the action works. Arguments tell the command what to act on. Once you see this pattern, everything clicks.

terminal_basics.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Print the name of the shell you are currently using
echo $SHELL

# Print the current user's name — useful to confirm who you're logged in as
whoami

# Print the current date and time — a simple sanity-check command
date

# Display how long the system has been running and how many users are logged in
uptime

# Clear the terminal screen so you get a fresh, clean view
# (Nothing is deleted — it just scrolls the view)
clear
Output
/bin/bash
johndev
Tue Oct 15 09:32:17 UTC 2024
09:32:17 up 3 days, 4:11, 1 user, load average: 0.12, 0.08, 0.05
Pro Tip:
You can recall any previous command by pressing the Up arrow key. Press it multiple times to scroll back through your history. This saves an enormous amount of re-typing and is one of the first productivity habits every terminal user builds.
Production Insight
Shell history saves you from retyping – but also records mistakes.
Ctrl+R searches backward through history; history | grep command finds past commands.
Don't let a panic-stricken rm -rf live in your history – alias rm to rm -i for safety.
Key Takeaway
The shell is your translator to the kernel.
Commands follow the pattern: command [flags] [arguments].
Master this pattern and you master the terminal.

The Linux filesystem is like a tree — there's one root at the top (literally called /), and everything branches out from there. Your home folder, system programs, configuration files, and connected USB drives all live somewhere on this single tree. Learning to move around it is the single most important terminal skill.

pwd stands for Print Working Directory. It tells you exactly where you are right now. Think of it as asking Google Maps 'where am I?' before you try to navigate anywhere.

ls stands for List. It shows you everything inside the current folder — just like opening a drawer to see what's inside. On its own, ls shows visible files. Add the -a flag and it also shows hidden files (files whose names start with a dot, like .bashrc). Add -l and you get a detailed view with permissions, file size, and the last modified date. Combine them as ls -la for the full picture.

cd stands for Change Directory. This is how you move around. cd Documents moves you into a folder called Documents. cd .. moves you one level up — back towards the root. cd ~ always takes you home, no matter where you are. cd - takes you back to the last directory you were in, which is surprisingly handy when you're jumping between two locations.

Understanding absolute vs relative paths is crucial. An absolute path starts with / and describes the full address from root (like a full postal address). A relative path is described from where you currently are (like saying 'turn left at the corner').

filesystem_navigation.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Show the full path of where you currently are in the filesystem
pwd
# Output: /home/johndev

# List files in the current directory — basic view
ls
# Output: Desktop  Documents  Downloads  Music  Pictures

# List ALL files including hidden ones, with detailed info (-l = long format, -a = all)
ls -la
# The 'd' at the start of a line means it's a directory, '-' means it's a regular file

# Move into the Documents directory (relative path — works from where we are)
cd Documents

# Confirm we moved successfully
pwd
# Output: /home/johndev/Documents

# Move back up one level to the parent directory
cd ..

# Move to an absolute path — works from anywhere in the filesystem
cd /var/log

# Jump instantly back to your home directory no matter where you are
cd ~

# Go back to the previous directory you were in (/var/log in this case)
cd -
# Output: /var/log
Output
/home/johndev
Desktop Documents Downloads Music Pictures
total 48
drwxr-xr-x 12 johndev johndev 4096 Oct 15 09:00 .
drwxr-xr-x 3 root root 4096 Oct 10 08:00 ..
-rw-r--r-- 1 johndev johndev 220 Oct 10 08:00 .bash_logout
-rw-r--r-- 1 johndev johndev 3526 Oct 10 08:00 .bashrc
drwxr-xr-x 2 johndev johndev 4096 Oct 15 08:45 Documents
drwxr-xr-x 2 johndev johndev 4096 Oct 14 17:30 Downloads
/home/johndev/Documents
/var/log
Why This Matters:
In a DevOps role, you'll constantly navigate servers that have no graphical interface. Knowing pwd saves you from running a dangerous command in the wrong folder. Senior engineers run pwd almost automatically before any destructive command — it's a reflex, not a crutch.
Production Insight
pwd before any destructive command is a professional reflex.
In production, one wrong cd can lead to rm -rf the wrong directory.
Always run pwd and ls before deleting.
Key Takeaway
pwd shows where you are before you act.
Use absolute paths when precision matters.
cd - returns to previous location – a lifesaver in deep directory trees.

Creating, Moving, Copying and Deleting Files and Folders

Now that you can find your way around, let's actually do things. This is where you start to feel the real power of the command line — operations that would take ten clicks in a file manager take two seconds here.

touch filename.txt creates a new empty file. The name is slightly misleading — it was originally designed to update the 'last modified' timestamp of a file, but if the file doesn't exist yet, it creates it. It's your go-to for quickly spinning up a new file.

mkdir folder_name creates a new directory. Add the -p flag (mkdir -p) to create nested directories all at once — for example mkdir -p projects/website/css creates all three folders in one shot even if none of them exist yet.

cp source destination copies a file. To copy an entire folder and everything inside it, you need the -r flag (recursive): cp -r source_folder/ destination_folder/. Without -r, Linux will refuse to copy a directory.

mv source destination moves a file or folder. There's a neat trick here: mv is also how you rename things. mv old_name.txt new_name.txt renames the file in place because you're 'moving' it to the same location with a different name.

rm filename deletes a file permanently. There is no Recycle Bin. It's gone. To delete a folder and all its contents, use rm -rf folder_name. The r means recursive and f means force (no confirmation prompts). This is one of the most powerful and dangerous commands in Linux — treat it with respect.

file_management.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Create a new empty file called project_notes.txt
touch project_notes.txt

# Verify it was created — ls shows the file in the current directory
ls -l project_notes.txt
# Output: -rw-r--r-- 1 johndev johndev 0 Oct 15 10:05 project_notes.txt

# Create a nested directory structure in one command
# -p flag: creates parent directories as needed, no error if they already exist
mkdir -p my_project/src/components

# Verify the full structure was created
ls -R my_project
# Output shows all nested subdirectories

# Copy project_notes.txt into the my_project folder
cp project_notes.txt my_project/project_notes.txt

# Rename the original file by 'moving' it to the same location with a different name
mv project_notes.txt project_notes_v1.txt

# Move the renamed file into the my_project/src directory
mv project_notes_v1.txt my_project/src/

# Delete a single file — permanent, no confirmation
rm my_project/project_notes.txt

# Delete the entire my_project folder and all its contents
# WARNING: This is irreversible. Double-check your path before running this.
rm -rf my_project
Output
-rw-r--r-- 1 johndev johndev 0 Oct 15 10:05 project_notes.txt
my_project:
src
my_project/src:
components project_notes_v1.txt
my_project/src/components:
Watch Out:
Never run rm -rf / or rm -rf ./ without being absolutely certain of your current directory. The first deletes your entire root filesystem. The second deletes everything in your current folder. Modern Linux systems have a --no-preserve-root safeguard, but don't test it. Always run pwd first, then double-check your path before any rm -rf command.
Production Insight
mkdir -p creates nested dirs in one go – common in deployment scripts.
mv is also rename – use it in CI to atomically replace files.
Never use rm -rf in production scripts without guard rails: check variable is non-empty, run pwd, echo the command first.
Key Takeaway
Create with touch and mkdir.
Copy with cp -r for directories.
Move and rename with mv.
Delete with rm – but only after triple-checking your path.

Reading Files and Understanding Linux Permissions

Two skills that unlock a huge amount of real-world DevOps work are reading file contents without opening an editor, and understanding what those cryptic permission strings like drwxr-xr-x actually mean.

For reading files, you have three main tools. cat filename dumps the entire file to your screen — great for short files. For long files, less filename opens an interactive viewer you can scroll through with arrow keys (press q to quit). head filename shows you the first 10 lines, and tail filename shows you the last 10. tail -f filename is particularly powerful — the -f flag means 'follow', so it keeps watching the file and prints new lines as they appear. This is how you watch a live server log in real time.

Now, permissions. Every file and folder in Linux has three sets of permissions: one for the owner, one for the owner's group, and one for everyone else. Each set has three possible permissions: read (r), write (w), and execute (x). A dash - means that permission is absent.

So `rwxr-xr--` breaks down as: owner can read, write, execute | group can read and execute but not write | everyone else can only read.

chmod changes these permissions. The easiest way to use it is with numbers. Read = 4, Write = 2, Execute = 1. You add them together for each group. So chmod 755 script.sh means owner gets 7 (4+2+1 = rwx), group gets 5 (4+0+1 = r-x), others get 5 (r-x). This is the standard permission for a script you want everyone to run but only you to edit.

chown changes who owns the file: chown username:groupname filename.

reading_and_permissions.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Write some content into a file using echo and the redirect operator >
# > creates the file if it doesn't exist, or overwrites it if it does
echo 'Server started successfully' > app.log
echo 'Connecting to database...' >> app.log  # >> appends instead of overwrites
echo 'Database connection established' >> app.log
echo 'Request received: GET /api/users' >> app.log

# cat: print the entire file to the screen — good for short files
cat app.log

# head: show only the first 2 lines of the file
head -n 2 app.log

# tail: show only the last 1 line — useful for checking the most recent log entry
tail -n 1 app.log

# Create a simple shell script file
echo '#!/bin/bash' > deploy.sh
echo 'echo Deploying application...' >> deploy.sh

# Check the current permissions on deploy.sh
ls -l deploy.sh
# Output: -rw-r--r-- 1 johndev johndev 42 Oct 15 10:30 deploy.sh
# Right now, no one can EXECUTE (run) this script

# chmod 755: give owner full access, group and others can read and execute
# 7 = rwx (4+2+1), 5 = r-x (4+0+1), 5 = r-x
chmod 755 deploy.sh

# Confirm the permissions changed
ls -l deploy.sh
# Output: -rwxr-xr-x 1 johndev johndev 42 Oct 15 10:30 deploy.sh

# Now run the script — ./ means 'in the current directory'
./deploy.sh
Output
Server started successfully
Connecting to database...
Database connection established
Request received: GET /api/users
Server started successfully
Connecting to database...
Request received: GET /api/users
-rw-r--r-- 1 johndev johndev 42 Oct 15 10:30 deploy.sh
-rwxr-xr-x 1 johndev johndev 42 Oct 15 10:30 deploy.sh
Deploying application...
Interview Gold:
Interviewers love asking what chmod 777 means and whether it's ever appropriate. The answer: it gives full read, write, and execute access to everyone on the system. It's almost never appropriate in production because it means any user or process on that machine can modify or delete the file. The correct answer shows you understand security tradeoffs, not just syntax.
Production Insight
tail -f is your best friend for live logs – but beware of log rotation: use tail -F (follow with retry) to survive logrotate.
chmod 755 is standard for scripts. Never use chmod 777 in production – any process can tamper.
Key Takeaway
Read files with cat/less/head/tail.
Permissions: r=4, w=2, x=1 – add for groups.
chmod 755 for scripts, 644 for docs.
chown changes ownership.

Combining Commands: Pipes, Redirection, and Chaining

So far you've run single commands. But the real power of the terminal comes from combining commands together. You can take the output of one command and feed it into another, redirect output to files, or chain commands so the next one runs only if the previous succeeded.

Pipes (|) send the output of the first command as input to the second command. For example, ls -la | grep '^d' lists only directories. cat log.txt | grep error | head -n 5 shows the first 5 error lines.

Redirection controls where output goes. > writes output to a file (overwrites). >> appends. < feeds a file as input to a command. For example, sort < unsorted.txt > sorted.txt reads from unsorted.txt, sorts it, and writes to sorted.txt.

Command chaining lets you run multiple commands in sequence. && runs the next command only if the previous succeeded (make && ./run). || runs the next only if the previous failed. ; runs them unconditionally.

These techniques are essential for writing efficient one-liners and shell scripts.

combining_commands.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Pipe: list only directories (lines starting with 'd')
ls -la | grep '^d'

# Pipe: find error lines in a log and show first 5
# (assuming app.log exists from previous section)
cat app.log | grep -i error | head -n 5

# Redirection: sort a file and overwrite output
sort < unsorted.txt > sorted.txt

# Command chaining: build and test only if build succeeds
make clean && make && ./test

# Chaining with OR: try to start server, if fails, print error
./server || echo 'Server failed to start'

# Semicolon: run three commands regardless of success
echo 'Step 1' ; echo 'Step 2' ; echo 'Step 3'
Output
drwxr-xr-x 2 johndev johndev 4096 Oct 15 08:45 Documents
(Error lines from app.log if any)
(Contents of sorted.txt)
(Output of make and test)
Server failed to start
Step 1
Step 2
Step 3
Pro Tip:
Use set -o pipefail in your shell scripts so that a pipe command fails if any component fails. By default, only the exit code of the last command in the pipe is used – previous failures are silently ignored.
Production Insight
Pipes avoid intermediate files – but can hide errors if you don't use set -o pipefail in scripts.
> overwrites silently – use >> unless you intend to replace.
&& stops on first failure – perfect for deployment scripts where every step must succeed.
Key Takeaway
Pipes send output of one command to input of another.
> overwrites, >> appends.
&& runs next only if previous succeeds.
Master these for efficient automation.

Linux Commands Cheat Sheet: Quick Reference by Category

Once you're comfortable with the core commands, you'll want a mental map of the most common utilities grouped by task. This cheat sheet covers file operations, navigation, text processing, system administration, networking, and archiving — all the commands you'll use daily.

CategoryCommandDescriptionExample
NavigationpwdPrint current directorypwd
lsList directory contentsls -la
cdChange directorycd /var/log
File OperationstouchCreate empty file or update timestamptouch error.log
cpCopy file/directorycp -r src/ dest/
mvMove or renamemv old.txt new.txt
rmDelete file/directoryrm -rf tmp/
mkdirCreate directorymkdir -p a/b/c
Viewing FilescatConcatenate and displaycat config.json
lessView file with scrollless large.log
headFirst 10 lineshead -n 20 data.csv
tailLast 10 lines or followtail -f app.log
Text ProcessinggrepSearch text with patternsgrep -i error log.txt
sedStream editor for find/replacesed 's/foo/bar/g' file.txt
awkPattern scanning and processingawk '{print $1}' data.txt
PermissionschmodChange file mode bitschmod 755 script.sh
chownChange file owner/groupchown user:group file
System Monitoringtop / htopReal-time process overviewtop
psSnapshot of processesps aux
killSend signal to processkill -9 1234
dfDisk space usagedf -h
duDirectory disk usagedu -sh /var
freeMemory usagefree -m
NetworkingsshSecure shell to remote hostssh user@192.168.1.1
pingTest reachabilityping -c 4 google.com
wgetNon-interactive downloadwget https://example.com/file
curlTransfer data with URLscurl -O https://example.com/file
netstatNetwork connections statusnetstat -tulpn
ArchivingtarArchive filestar -czf archive.tar.gz dir/
gzip / gunzipCompress/uncompressgzip file.log
SearchfindLocate filesfind /var -name '*.log'
locateQuick file lookup (from index)locate nginx.conf
Process ControlnohupRun command immune to hangupsnohup longtask &
&Run in background./server &
fg / bgBring to foreground/backgroundfg %1

Keep this table bookmarked or printed — it covers 90% of the commands you'll need in daily DevOps work.

Production Insight
A cheat sheet is only useful if you use it actively. Instead of memorising every flag, commit the core command and the --help flag to memory. For example, tar --help shows you all options instantly. That's why senior engineers rarely internalise tar flags — they just check the help text.
Key Takeaway
Master 30 commands across categories, not 300 commands you rarely use. The cheat sheet above is your daily driver.

System Monitoring Commands: Keeping an Eye on Your Server

When a server starts acting up — high load, slow responses, out of memory — you need to diagnose the problem without a GUI. These system monitoring commands are your stethoscope.

top shows a real-time, updating view of all running processes, sorted by CPU usage by default. It tells you which process is eating your CPU or memory, how long the system has been up, load averages, and memory stats. Press q to quit. For a more user-friendly version, install htop.

ps gives a snapshot of processes at the moment you run it. ps aux lists all processes from all users with detailed columns: PID (process ID), %CPU, %MEM, VSZ/RSS (virtual/resident memory), STAT (process state), START, TIME, COMMAND. Combine with grep to find a specific process: ps aux | grep nginx.

kill sends signals to processes. The most common use is kill -9 PID (SIGKILL), which forcefully terminates a process. But always try kill PID (SIGTERM) first — it asks nicely. kill -15 PID is the same as plain kill. Use kill -l to list all signal names.

df reports disk space usage by filesystem. df -h shows human-readable sizes (G, M). Look for partitions that are 90%+ full — that's a classic cause of application failures.

du shows disk usage of files and directories. du -sh /var/log tells you the total size of the /var/log directory. du -h --max-depth=1 /home gives you a size breakdown one level deep.

free displays memory usage. free -m shows in MB, free -g in GB. Pay attention to the 'available' column — that's memory that can be used by new processes without swapping. If available is low and swap is high, you're in trouble.

system_monitoring.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Show real-time process list (CPU-heavy processes at top)
top -b -n 1 | head -20   # batch mode, single snapshot

# List all processes with user, PID, CPU, memory, command
ps aux

# Find a specific process by name
ps aux | grep nginx

# Forcefully terminate process (last resort)
kill -9 2560

# Check disk space usage of all mounted filesystems
df -h

# Check disk usage of a specific directory
du -sh /var/log

# Check memory and swap usage in human-readable units
free -m
Output
top - 12:34:56 up 3 days, 2:15, 1 user, load average: 0.08, 0.12, 0.10
Tasks: 123 total, 1 running, 122 sleeping
%Cpu(s): 2.5 us, 1.2 sy, 0.0 ni, 96.0 id, 0.3 wa
MiB Mem : 7936.0 total, 2048.0 free, 3072.0 used, 2816.0 buff/cache
MiB Swap: 2048.0 total, 512.0 free, 1536.0 used. 4096.0 avail Mem
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 170568 8344 ? Ss Mar01 0:03 /sbin/init
root 172 0.0 0.0 115456 1484 ? Ss Mar01 0:00 /lib/systemd/systemd-journald
...
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 98G 45G 53G 46% /
1.2G /var/log
total used free shared buff/cache available
Mem: 7936 3072 2048 128 2816 4096
Swap: 2048 1536 512
Don't kill -9 without thinking:
SIGKILL doesn't let the process clean up — open file handles remain dirty, databases can corrupt data, and child processes become orphaned. Always try kill (SIGTERM) first. Only use kill -9 when the process is truly unresponsive and you can afford a restart.
Production Insight
Monitoring is about trends, not snapshots. Install sysstat for historical sar data, or set up nmon for detailed logging. Real-time top is great for ad-hoc debugging, but for production you need persistent metrics. Always investigate why a process is using 100% CPU before killing it — it might be a legitimate workload spike.
Key Takeaway
Use top for live CPU/memory, ps for process details, kill to stop processes gracefully, df for disk space, du for directory sizes, and free for memory pressure.

Networking Commands: Connecting to Servers and Transferring Data

In a world where servers talk to other servers, networking commands are as essential as ls or cd. You'll use them to check connectivity, download files, manage remote machines, and inspect network activity.

ssh (Secure Shell) lets you log into a remote machine securely. The basic syntax is ssh username@hostname. On first connection, you'll see a fingerprint prompt — type 'yes' to accept. For scripted access, use SSH key authentication instead of passwords: ssh-keygen then ssh-copy-id user@host.

ping tests whether a remote host is reachable and how long the round-trip takes. ping -c 4 google.com sends 4 packets and shows time in ms. High latency or packet loss indicates network issues.

wget downloads files from the internet non-interactively. wget https://example.com/file.tar.gz saves the file in the current directory. Add -O outputname to rename, -q for quiet mode, and -c to continue an interrupted download.

curl is more feature-rich than wget. It supports multiple protocols and can send HTTP headers, handle cookies, upload files, and more. curl -O https://example.com/file saves a file. curl -I https://example.com shows response headers only — great for debugging APIs.

netstat displays network connections, routing tables, interface statistics. netstat -tulpn shows all TCP and UDP listening ports with the associated process (requires sudo for all info). This is your go-to for answering 'what is running on port 8080?'

Pro tip: On modern systems, ss replaces netstat for faster and more detailed socket statistics. Both commands are widely used.

networking_commands.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# SSH into a remote server
ssh johndev@192.168.1.100

# Ping a host 4 times
ping -c 4 google.com

# Download a file with wget
wget https://example.com/deploy.tar.gz

# Download with curl and show progress
curl -O https://example.com/deploy.tar.gz

# Check HTTP response headers
curl -I https://api.example.com/health

# List all listening TCP and UDP ports with process IDs
sudo netstat -tulpn

# Faster alternative: ss
sudo ss -tulpn
Output
PING google.com (142.250.68.14) 56(84) bytes of data.
64 bytes from 142.250.68.14: icmp_seq=1 ttl=117 time=14.2 ms
64 bytes from 142.250.68.14: icmp_seq=2 ttl=117 time=13.8 ms
--- google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 13.8/14.0/14.2/0.2 ms
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1234/sshd
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 5678/redis-server
udp 0 0 0.0.0.0:68 0.0.0.0:* 890/dhclient
SSH Key Authentication:
If you're typing a password every time you SSH, you're wasting time. Generate a key pair with ssh-keygen -t ed25519, then copy the public key with ssh-copy-id user@server. Future logins will be passwordless and more secure.
Production Insight
In production, never use ping as a health check — ICMP packets are often blocked by firewalls or rate-limited. Use curl -f http://localhost:8080/health or a TCP connect test instead. For performance testing, use iperf3. For DNS debugging, use dig or nslookup.
Key Takeaway
SSH for remote access, ping for basic connectivity, wget/curl for file transfers and API testing, netstat/ss for checking listening ports.

Text Processing: grep, sed, and awk for Log Analysis

Logs are the lifeblood of debugging, but they're often gigabytes of unstructured text. Three tools turn that noise into signal: grep for searching, sed for editing, and awk for structured extraction.

grep searches for patterns in text. grep 'error' app.log prints all lines containing 'error'. -i for case-insensitive, -v for lines that do NOT match, -c for count only, -n for line numbers, -r for recursive directory search. grep -r 'FATAL' /var/log/ finds all fatal errors across logs.

sed (stream editor) performs find-and-replace operations. sed 's/old/new/g' file.txt replaces all occurrences of 'old' with 'new' and prints to stdout. Add -i to edit the file in place. Common patterns: delete lines containing a word (sed '/debug/d' file.txt), or print lines 10-20 (sed -n '10,20p' file.txt).

awk is a full programming language for text processing, but you only need a few patterns. awk '{print $1}' file.txt prints the first column of each line. awk '/error/ {print $2, $5}' log.txt prints columns 2 and 5 of lines containing 'error'. awk '{count[$1]++} END {for (ip in count) print ip, count[ip]}' access.log counts unique IP addresses from an Apache log.

Real-world pattern: find the top 10 IPs hitting a web server: awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10.

text_processing.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Search for 'error' in a log file (case-insensitive)
grep -i 'error' app.log

# Count lines containing 'FATAL'
grep -c 'FATAL' app.log

# Search recursively in /var/log for 'OutOfMemory'
grep -r 'OutOfMemory' /var/log/

# Replace 'password' with '****' in a config file (in-place)
sed -i 's/password/****/g' config.ini

# Delete lines containing 'DEBUG' from output
sed '/DEBUG/d' app.log

# Print first column of output (e.g., IP addresses from access log)
awk '{print $1}' access.log

# Print lines containing 'error' and then columns 2 and 5
awk '/error/ {print $2, $5}' app.log

# Count occurrences of each IP address and sort by frequency
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10
Output
2024-10-15 10:00:00 error: Connection refused
2024-10-15 10:01:00 error: Timeout
count: 2
/var/log/syslog:2024-10-15 09:00:00 OutOfMemory: java.lang.OutOfMemoryError
(No output, file modified)
(Only non-DEBUG lines shown)
192.168.1.1
10.0.0.2
...
10:00:00 Connection refused
10:01:00 Timeout
187 192.168.1.1
142 10.0.0.2
98 203.0.113.5
Know your regex:
grep, sed, and awk all use regular expressions. Learn the basics: . matches any character, * matches zero or more of the previous character, ^ matches start of line, $ matches end of line, [abc] matches any character in the set. Test your regex with echo 'text' | grep -E 'pattern' before running on a real file.
Production Insight
In production, log rotation means files are constantly being replaced. Always use tail -F (capital F) to follow rotated logs. For real-time alerting, pipe logs through a script that triggers notifications when grep -c 'ERROR' exceeds a threshold in a time window. Never run grep on a 20GB log file without constraints — use head, tail, or timeout to limit risk.
Key Takeaway
grep searches patterns, sed edits streams, awk extracts columns. Combine them with pipes for powerful one-liner log analysis.

Shell Types Comparison: Bash vs Zsh vs Fish

Not all shells are created equal. While Bash is the default on almost every Linux distribution, developers often switch to Zsh or Fish for improved productivity features. Here's a comparison to help you choose.

FeatureBashZshFish
Default onAlmost all Linux distros, macOS (legacy)macOS (current default), many Linux distros via installNot default; manual install required
Scripting compatibilityPOSIX-compliant; runs vast majority of shell scriptsMostly Bash-compatible with enhancements; many zsh-specific featuresNot Bash-compatible; uses own scripting syntax
Auto-completionBasic: TAB for files and commandsAdvanced: case-insensitive, contextual suggestions, cd completion with pathsExcellent: autosuggestions as you type (based on history), tab-complete with descriptions
Plugin/theme ecosystemLimited to .bashrc customizationsVery large: oh-my-zsh, antigen, zinit; thousands of themesGrowing: fisher, omf (oh-my-fish); fewer than Zsh
Prompt customizationManual via PS1 variableRich: powerlevel10k theme, git info, async promptBuilt-in: web-based configuration (fish_config)
Syntax highlightingNo (by default)Yes (via zsh-syntax-highlighting plugin)Yes (built-in)
Error messagesTerse and crypticSimilar to Bash but with optional enhancementsColorful, easy-to-understand suggestions
PerformanceFastSlightly slower due to pluginsFast; minimal startup time
Learning curveSteep (cryptic syntax, manual config)Medium (advanced features but Bash-like syntax)Low (friendly default behavior, web config)

Which one should you use? - Bash: Stick with it if you're on a server or writing portable scripts. Every Linux machine has it. - Zsh: Best for daily driver development on personal machines. The powerlevel10k prompt with git integration is a game-changer. - Fish: Great for beginners because of autosuggestions and error highlighting, but scripts written in Fish won't run on most servers.

A common setup: use Bash for root/automation/servers, and Zsh for your interactive terminal with oh-my-zsh.

Production Insight
In production, always use Bash. Your deployment scripts, cron jobs, and Dockerfiles must run on standard Linux base images that only have Bash. Never write production automation in Zsh or Fish — you'll break the build when it runs on a bare Ubuntu server. Save Zsh and Fish for your local development comfort.
Key Takeaway
Bash for portability and scripts, Zsh for daily driver with rich plugins, Fish for beginner-friendly autosuggestions. But always default to Bash in production environments.

Practice Exercises: 10 Real-World Challenges to Solidify Your Skills

Theory without practice is forgettable. These exercises simulate real tasks you'll face as a developer or DevOps engineer. Try to complete them without looking up the exact answer — use --help and man pages if needed.

1. Find all .log files in /var: Write a command that locates every file ending in .log under /var and its subdirectories. Hint: use find.

2. Count lines containing 'ERROR' in a log file: Use grep to count how many lines contain the word 'ERROR' (case-insensitive) in /var/log/syslog.

3. Find the top 5 largest files in a directory: Use du and sort to list the 5 largest files or directories in /var/log.

4. Set up a cron job to run a script every hour: Create a crontab entry that runs /home/user/backup.sh at minute 0 of every hour. Use crontab -e.

5. Extract IP addresses from an Apache access log: Use awk to print only the first column (IP) of /var/log/apache2/access.log, then count unique IPs and find the top 3.

6. Replace all instances of 'localhost' with 'prod.example.com' in a config file: Use sed to edit config.yml in place.

7. Create a directory structure for a project: Use mkdir -p to create project/{src,test,docs} in one command.

8. Monitor a process in real-time: Run top and identify the process using the most CPU. Then use ps to find its PID and kill to stop it (use SIGTERM).

9. Check disk space and memory: Run df -h and free -m. If root is over 80%, find the largest directories with du -sh /*.

10. Download a file and verify its integrity: Use curl to download a file, then use sha256sum to check its hash against an expected value.

Bonus: Combine grep, sort, uniq, and head to answer 'How many unique HTTP 404 errors occurred yesterday in my access log?'

practice_exercises.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Exercise 1: Find all .log files under /var
find /var -name '*.log'

# Exercise 2: Count ERROR lines in syslog
grep -ci 'error' /var/log/syslog

# Exercise 3: Find top 5 largest files in /var/log
du -ah /var/log | sort -rh | head -5

# Exercise 4: Set up a cron job (run crontab -e, then add line)
# 0 * * * * /home/user/backup.sh

# Exercise 5: Extract top 3 IPs from access log
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -3

# Exercise 6: Replace localhost in config file
sed -i 's/localhost/prod.example.com/g' config.yml

# Exercise 7: Create project structure
mkdir -p project/{src,test,docs}

# Exercise 9: Check disk and memory
df -h
free -m

# Exercise 10: Download and verify checksum
curl -O https://example.com/package.tar.gz
sha256sum package.tar.gz
Output
(Results will vary based on system)
Progression path:
If you can complete all 10 exercises without help, you're ready to move on to shell scripting. The next logical step is to combine these commands into reusable scripts with variables, conditionals, and functions.
Production Insight
These exercises are extracted from real incident reports. The 'find all .log files' exercise helped a junior engineer locate log files that were filling up a disk. The 'cron job' exercise is the foundation of automated backups. Practising these patterns will save you time and prevent data loss in production.
Key Takeaway
Master these 10 exercises to convert passive knowledge into active skill. Each one directly maps to a real-world task you'll face in a DevOps role.

Whoami, History, and Your Identity in the Shell

When shit hits the fan, the first question is always: who is logged in, and what did they just run? The shell keeps receipts. whoami prints your current user. Obvious? Sure. Until you've SSH'd into five boxes and forgot which one is production. history shows the last N commands (default 1000). That's your audit trail. Use !123 to re-run command 123. Use history | grep kubectl to find that rollout you botched. Never run history -c to "clean up" — that's what juniors do when they break something. Instead, own the mistake. The shell remembers. So should you.

ShellAudit.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// io.thecodeforge — devops tutorial

$ ssh deploy@web-01
$ whoami
> deploy

$ history | tail -5
  101  sudo systemctl restart nginx
  102  vim /etc/nginx/sites-enabled/default
  103  nginx -t
  104  systemctl status nginx
  105  history | tail -5

# Re-run command 103
$ !103
> nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
> nginx: configuration file /etc/nginx/nginx.conf test is successful
Output
deploy
101 sudo systemctl restart nginx
...
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
Production Trap:
whoami doesn't tell you if you're on the right server. Pair it with hostname and pwd. Trust nothing. Verify everything.
Key Takeaway
Always run history | grep <command> before blaming the system. The problem is usually between the chair and the keyboard.

Your SSH Key: The Crown Jewel You Keep Losing

Password-based SSH is a vulnerability masquerading as convenience. Stop. Generate a key pair with ssh-keygen -t ed25519 -C "your_email@example.com". ED25519 is faster, smaller, and more secure than RSA. The private key (~/.ssh/id_ed25519) stays on your machine — never, ever copy it to a server. The public key (id_ed25519.pub) gets appended to ~/.ssh/authorized_keys on the target. Use ssh-copy-id user@server to do this automatically. If you lose your private key, you lose access. Full stop. Back it up to a hardware token or a password manager. Treat it like the key to your datacenter. Because it is.

SSHKeyGen.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// io.thecodeforge — devops tutorial

$ ssh-keygen -t ed25519 -C "ops@thecodeforge.io"
> Generating public/private ed25519 key pair.
> Enter file in which to save the key (~/.ssh/id_ed25519):
> Enter passphrase (empty for no passphrase):
> Your identification has been saved in ~/.ssh/id_ed25519
> Your public key has been saved in ~/.ssh/id_ed25519.pub

# Copy to a server
$ ssh-copy-id deploy@prod-02
> Number of key(s) added: 1

# Test
$ ssh deploy@prod-02 'whoami'
> deploy
Output
Generating public/private ed25519 key pair.
...
The key fingerprint is: SHA256:... ops@thecodeforge.io
Senior Shortcut:
Add -o PasswordAuthentication=no to your SSH config so you can't accidentally fall back to passwords. Force yourself to use keys.
Key Takeaway
SSH keys are non-negotiable. Generate one today, lose the password habit tomorrow. Your server will thank you.

uname: Know What OS You're On Before You Break It

Every senior engineer has seen someone blindly run commands designed for a different kernel version. Stop guessing. uname tells you exactly what operating system, kernel release, and architecture you're working with.

The `-a` flag dumps everything: kernel name, hostname, release, version, machine hardware, and processor type. When you're debugging a production server, this is the first thing you type after ssh in. Not ls, not whoami. uname -a. If the kernel string doesn't match your deployment playbook, abort immediately.

For scripting, use targeted flags: uname -s for kernel name (Linux vs Darwin), -r for release, -m for architecture (x86_64 vs aarch64). This is how you write conditional logic that doesn't shit the bed when someone replaces your Ubuntu VM with a macOS dev box. Don't assume. Check first.

ServerCheck.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.thecodeforge — devops tutorial

// example- Check target architecture before deploying binary
- name: Check kernel and architecture
  shell: |
    echo "Kernel: $(uname -s)"
    echo "Release: $(uname -r)"
    echo "Arch: $(uname -m)"
  register: sysinfo

- name: Fail if not Linux x86_64
  fail:
    msg: "This playbook only runs on Linux x86_64"
  when: sysinfo.stdout.find('Linux') == -1 or sysinfo.stdout.find('x86_64') == -1
Output
Kernel: Linux
Release: 5.15.0-91-generic
Arch: x86_64
Production Trap:
Never trust /etc/os-release alone. Containers often have minimal filesystems. uname -r gives you the host kernel, not the container—critical for kernel module compatibility.
Key Takeaway
Always confirm OS and architecture with uname -a before running install scripts or deploying binaries.

whereis: Find Binaries Fast, Skip the Database Hell

which tells you if a command exists. whereis tells you where everything lives—binary, source, and man pages—in one shot. It's faster than find or locate because it queries a hardcoded list of common paths, not a filesystem scan or database.

When you're SSH'd into a bare-metal server at 3 AM and need to know where nginx is installed so you can read its man page, whereis nginx beats fumbling with which and apropos separately. It returns the binary path first, then source, then man pages. If you see no man pages, that's a signal the package was stripped or compiled locally.

Use -b for binaries only, -m for man pages, -s for source. In CI pipelines, use whereis -b to verify a tool is installed in a predictable location before running shell commands. It's not perfect—it misses custom paths or symlink farms. But for standard installs on any Unix system, it's the fastest check you have. Stop grepping PATH. Use whereis.

ToolCheck.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// io.thecodeforge — devops tutorial

// example- Verify critical tools exist before deployment
- name: Check required binaries
  shell: |
    for tool in nginx curl git; do
      location=$(whereis -b "$tool" | awk '{print $2}')
      if [ -z "$location" ]; then
        echo "MISSING: $tool"
        exit 1
      fi
      echo "FOUND: $tool at $location"
    done
  register: tool_check
  failed_when: tool_check.rc != 0
Output
FOUND: nginx at /usr/sbin/nginx
FOUND: curl at /usr/bin/curl
FOUND: git at /usr/bin/git
Senior Shortcut:
Run whereis -b -m to get binary path and man page path simultaneously. Pipe to xargs man for instant documentation.
Key Takeaway
Use whereis for instant binary and documentation discovery—faster than which or find for standard installs.

apt-get: Package Management Without the Panic

Package managers exist so you don't have to compile everything from source. apt-get is Debian's tool for installing, updating, and removing software. Why it matters: forgetting to run apt-get update before install pulls outdated package lists, giving you broken dependencies or old security patches. sudo apt-get update refreshes the index. sudo apt-get install <package> grabs and installs. apt-get remove leaves config files behind; apt-get purge wipes them. The critical sequence: update first, upgrade second, install third. Never run apt-get upgrade on a production server without reading the changelog — automatic upgrades break kernel modules. Use apt list --upgradable to preview changes. Pro tip: apt-get autoremove cleans orphaned dependencies that waste disk space. Know this before you blindly copy-paste apt commands from Stack Overflow.

apt-get-workflow.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// io.thecodeforge — devops tutorial

# Update package index
sudo apt-get update

# Upgrade all packages (review first!)
sudo apt-get list --upgradable
sudo apt-get upgrade -y

# Install a package
sudo apt-get install nginx -y

# Remove but keep config
sudo apt-get remove nginx

# Completely wipe package
sudo apt-get purge nginx

# Clean orphaned dependencies
sudo apt-get autoremove
Output
[output truncated; live demo shows 0 upgraded, 1 newly installed, 0 to remove]
Production Trap:
Never use apt-get upgrade during peak traffic. It restarts services without warning. Schedule updates with apt-get dist-upgrade in maintenance windows.
Key Takeaway
Always run apt-get update before install; preview upgrades before applying them.

ifconfig: Diagnose Network Interfaces Before Something Breaks

ifconfig is the legacy command to inspect and configure network interfaces. Modern Linux pushes ip addr instead, but ifconfig still ships on most distros and is muscle memory for sysadmins. Why it matters: when your server goes offline, ifconfig shows which interfaces are up, their IPs, and packet errors. Run ifconfig -a to see all interfaces, even down ones. Look for RX errors or dropped packets — those mean cable faults or driver issues. If you see no IP on eth0, run dhclient eth0 to request one. The command also controls interfaces: sudo ifconfig eth0 down temporarily disables your network. Downside: ifconfig doesn't show routing tables — that's route or ip route. Use ifconfig eth0 mtu 9000 to set jumbo frames for high-throughput servers. Master this before your next outage.

ifconfig-inspect.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// io.thecodeforge — devops tutorial

# Show active interfaces
ifconfig

# Show all interfaces including down
ifconfig -a

# Bring interface down/up
sudo ifconfig eth0 down
sudo ifconfig eth0 up

# Set static IP (temporary)
sudo ifconfig eth0 192.168.1.100 netmask 255.255.255.0

# Check for packet errors
ifconfig eth0 | grep -i errors

# Find your MAC address
ifconfig eth0 | grep ether
Output
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.0.5 netmask 255.255.255.0 broadcast 10.0.0.255
Production Trap:
ifconfig changes are not persistent across reboots. Use /etc/network/interfaces or nmcli for permanent config. Restarting networking drops active SSH sessions.
Key Takeaway
ifconfig shows live interface status; check RX errors first when debugging network drops.

nslookup: DNS Troubleshooting Without the Guesswork

When a server suddenly becomes unreachable, the problem often isn't the server itself—it's DNS resolution. nslookup queries DNS records interactively or inline, revealing which name server responded, the IP address resolved, and any CNAME aliases. Before you SSH into a failing box, run nslookup target.example.com to verify DNS delivers the intended IP. Specify record types: nslookup -type=MX example.com finds mail servers, while nslookup -type=NS example.com reveals authoritative name servers. This prevents chasing ghosts: if nslookup returns a stale IP, your bind configuration or TTL caching is wrong, not the application. Use the interactive mode to query multiple domains and switch servers with server 8.8.8.8. Never assume DNS is correct—egrep for "NXDOMAIN" or "server failed" in output. This command saves hours by isolating network vs. application failures.

nslookup_quick.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// io.thecodeforge — devops tutorial
// 25 lines max
- name: Check A record for myserver.internal
  command: nslookup myserver.internal
  example_output: |
    Server: 8.8.8.8
    Address: 8.8.8.8#53
    
    Name: myserver.internal
    Address: 10.0.1.42
- name: Query MX record for example.com
  command: nslookup -type=MX example.com
  example_output: |
    example.com mail exchanger = 10 mail.example.com.
Output
Server: 8.8.8.8
Address: 8.8.8.8#53
Name: myserver.internal
Address: 10.0.1.42
Production Trap:
nslookup bypasses /etc/hosts. If you have local overrides, use getent hosts instead.
Key Takeaway
Verify DNS before debugging network connectivity—90% of 'server down' is DNS misconfiguration.

pidof: Find Process IDs Faster Than a Grep

Killing or debugging a runaway process should not require ps aux | grep -i and manual PID extraction. pidof returns the numeric PID of any running program by its exact name—instantly. pidof nginx outputs all nginx master and worker PIDs, one per line. Use it in scripts: kill -9 $(pidof stalled-process) terminates without grep overhead. Unlike pgrep, pidof matches the program name precisely, avoiding false positives from partial matches (e.g., "python3" won't match "python"). Combine with -s to return only the first PID, ideal for single-instance daemons. For zombie processes, pidof still shows the PID while ps marks it defunct—use kill -9 to force removal. This command is essential for automation: cron jobs can check if a service is running with if pidof apache2; then echo "running"; fi. Minimal, deterministic, and piping-safe.

pidof_check.yamlYAML
1
2
3
4
5
6
7
// io.thecodeforge — devops tutorial
// 25 lines max
- name: Kill all nginx processes
  command: kill $(pidof nginx)
- name: Single PID for ssh-agent
  command: pidof -s ssh-agent
  example_output: "2341"
Output
2341
Production Trap:
pidof fails if the full command path differs from binary name—use procfs on exotic systems.
Key Takeaway
Use pidof for precise PID retrieval without parsing ps output—faster and script-friendly.
● Production incidentPOST-MORTEMseverity: high

rm -rf / home: The Space That Deleted a Server

Symptom
Server became unresponsive after running rm -rf / home/user on a production instance.
Assumption
The command would only target /home/user.
Root cause
The space split the argument into two: / (the root) and home/user. rm -rf / started deleting every file from the root filesystem until the system crashed.
Fix
Always type the exact path without trailing spaces. Use echo rm -rf /your/path to print the command without executing it. For destructive commands, use rm -i for interactive confirmation.
Key lesson
  • Never type destructive commands manually in production — always use scripts with dry-run flags.
  • Always run pwd before rm -rf and visually confirm the target path.
  • Alias rm to rm -i in your .bashrc for an extra safety net.
Production debug guideSymptom → Action guide for beginners and pros4 entries
Symptom · 01
Command not found
Fix
Check spelling letter by letter. Run which command to verify installation. Install missing package with apt or yum.
Symptom · 02
Permission denied
Fix
Run ls -l to check current permissions. Use chmod if you own the file. If root owns it, use sudo only when necessary.
Symptom · 03
File not found
Fix
Double-check your current directory with pwd. Use tab completion to avoid typos. Use find /path -name 'filename' to locate files.
Symptom · 04
rm -rf doesn't delete directory
Fix
Check with ls -la for hidden files (names starting with dot). Ensure you have write permission on the parent directory. As a last resort, use strace rm -rf dir to see which system call fails.
TaskGUI (File Manager) ApproachCommand Line Approach
Create 50 empty files50 individual right-click > New File actionstouch file_{1..50}.txt — one command, done in milliseconds
Copy folder to remote serverDownload a separate SFTP app, configure it, drag and dropscp -r my_folder/ user@server:/destination — one line
Find a file containing specific textOpen each file and use Ctrl+F manuallygrep -r 'search_term' /path/to/search — instant results
View a live log fileRefresh manually or use a separate log viewer apptail -f /var/log/app.log — streams updates in real time
Rename 100 files with a patternRename each one individually by handfor f in *.txt; do mv "$f" "new_$f"; done — all at once
Set file permissionsRight-click > Properties > Permissions tab (if available)chmod 644 filename — precise, scriptable, instant

Key takeaways

1
Every Linux command follows the same pattern
command [flags] [arguments] — once you see this structure, learning new commands becomes a matter of reading the manual (man command), not memorising from scratch.
2
pwd before any destructive command is a professional reflex, not a beginner habit
senior engineers do it automatically because one wrong directory can mean real data loss.
3
The permission string rwxr-xr-x isn't random
it's three groups of three letters (owner, group, others), and you can convert them to numbers (r=4, w=2, x=1) to use with chmod precisely.
4
> overwrites a file completely while >> appends to it
confusing these two characters is one of the most common ways beginners accidentally destroy file contents they meant to preserve.
5
Pipes (|), redirection (>, >>, <), and chaining (&&, ||) turn single commands into powerful pipelines
master these to automate workflows and avoid manual errors.

Common mistakes to avoid

3 patterns
×

Using `rm -rf` with a trailing space before the path

Symptom
The command deletes the entire root filesystem if the space splits the argument into two: / and home/user.
Fix
Always type the exact path without trailing spaces. Use echo rm -rf /your/path to print the command without executing it. Consider using rm -i for interactive prompts in dangerous contexts.
×

Forgetting that `>` overwrites the entire file instead of appending

Symptom
All previous content in the target file is lost when using >, because it overwrites instead of appending.
Fix
Use >> (double arrow) to append to a file. Memorise: > = overwrite, >> = append. When in doubt, cat the file after the operation to confirm.
×

Running commands as root unnecessarily

Symptom
Accidental deletion of system files or changing permissions globally due to a typo while using sudo.
Fix
First understand why permission is denied: run ls -l to check ownership. If you own the file, fix permissions with chmod. Only use sudo for genuinely system-level tasks like installing packages or editing files in /etc.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the difference between an absolute path and a relative path in L...
Q02SENIOR
Walk me through what happens when you run `chmod 644` on a file — what p...
Q03SENIOR
If a script fails with 'Permission denied' even though you're the file's...
Q01 of 03JUNIOR

What is the difference between an absolute path and a relative path in Linux, and when would you use each?

ANSWER
An absolute path starts from the root directory (e.g., /home/user/docs) and works from anywhere. A relative path is based on your current directory (e.g., docs/ or ../docs). Use absolute paths in scripts and configuration files to avoid dependency on the current working directory. Use relative paths for quick navigation and ad‑hoc commands.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between the terminal, the console, and the shell?
02
How do I stop a command that is running and seems stuck?
03
Is the Linux command line the same on Ubuntu, CentOS, and macOS?
04
What does the `~` symbol mean in the terminal prompt?
05
Can I recover a file I deleted with `rm`?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Verified
production tested
May 23, 2026
last updated
1,554
articles · all by Naren
🔥

That's Linux. Mark it forged?

22 min read · try the examples if you haven't

1 / 12 · Linux
Next
Linux File System