Linux Commands in DevOps: The Complete Practical Guide
#linux
#linux-commands
#bash-scripting
Somewhere around 3 AM, a pager goes off. The deployment that looked fine in staging is now throwing 502s in production. There's no dashboard open, no fancy GUI , just an SSH session into a box, a blinking cursor and whatever Linux commands you actually remember under pressure. That's the real test of "knowing Linux commands." Not whether you can recite ls -la from memory, but whether you can find the process eating all the memory, tail the right log file and roll back a bad deploy before your coffee gets cold.
This guide is built around that reality. It's not a dictionary of every flag every command has ever supported. it's the command set that actually shows up in DevOps work: provisioning servers, debugging containers, writing deploy scripts, chasing down disk space and reading logs that were clearly not written with humans in mind. We'll go from the basics up, but we won't pretend the basics are the whole story, because in DevOps, they rarely are.
Why Linux Commands Still Matter, Even in a World of Dashboards
It's fair to ask this question, because a lot of infrastructure work today happens through Terraform, Kubernetes dashboards, cloud consoles and CI/CD pipelines that hide the terminal completely. So why bother learning raw commands?
Because underneath almost all of that tooling is still Linux and eventually something breaks in a way the dashboard doesn't explain. A pod is crash-looping and kubectl logs isn't enough, so you kubectl exec into it and you're back in a shell. A CI job fails with a cryptic error and the only way to actually see what happened is to SSH into the runner and look at the raw output. An EC2 instance's disk is full and AWS's console will happily tell you "disk usage: high" without telling you which directory is the actual problem , that's a du command away, not a console click away.
Containers are Linux processes. Cloud VMs are Linux boxes. Kubernetes nodes run a Linux kernel. Even the CI/CD runner executing your pipeline steps is, more often than not, a Linux container running a shell script you wrote , or one a tool generated for you. Learning these commands isn't nostalgia for the "old days" of ops. It's understanding the layer that every abstraction on top of it eventually leaks through.
A Quick Primer: How the Shell Actually Works
Before diving into command lists, it helps to understand three ideas that make almost every Linux command make sense once you get them.
Everything is a stream. A command reads from standard input (stdin), writes normal output to standard output (stdout) and writes errors to standard error (stderr). These are just numbered file descriptors , 0, 1 and 2 , and once you see them that way, redirection stops looking like magic syntax and starts looking like plumbing.
Pipes chain commands together. The | symbol takes the stdout of one command and feeds it directly into the stdin of the next, without ever touching a temporary file. This is the single most useful habit in the entire Linux command line , small commands, each doing one thing, chained into a pipeline that does something none of them could do alone.
Almost everything is a file or acts like one. Devices, process information, even kernel settings are exposed as files under paths like /dev, /proc and /sys. That's why you can read CPU info with cat /proc/cpuinfo instead of needing a special tool.
STDIN, STDOUT, STDERR and how pipes chain commands together
keyboard/file --> [ command1 ] --> stdout (fd 1)
|
stderr (fd 2) --> screen (unless redirected with 2>)
Piping two commands with | (stdout of the first becomes stdin of the next)
cat access.log | grep "500" | wc -l
| | |
reads file filters lines counts
to stdout matching "500" matching lines
Redirection operators:
command > file write stdout to file (overwrite)
command >> file append stdout to file
command 2> file write stderr to file
command &> file write both stdout and stderr to file
command < file read stdin from file
Keep this mental model in your back pocket. Almost every "advanced" one-liner you'll see later in this guide is really just this idea, applied a few times in a row.
File and Directory Commands
These are the commands you'll type without even thinking about it , dozens of times a day, in almost any Linux session.
| Command | What it does | Example |
|---|---|---|
pwd |
Prints the current directory path | pwd |
ls |
Lists directory contents | ls -la (long format, includes hidden files) |
cd |
Changes the current directory | cd /var/log |
mkdir |
Creates a directory | mkdir -p app/logs/2026 (-p creates parent dirs too) |
rmdir |
Removes an empty directory | rmdir old_build |
rm |
Deletes files or directories | rm -rf node_modules |
cp |
Copies files or directories | cp -r config/ config_backup/ |
mv |
Moves or renames files | mv app.log app.log.bak |
touch |
Creates an empty file or updates its timestamp | touch .env |
find |
Searches for files matching criteria | find /var/log -name "*.log" -mtime +7 |
locate |
Finds files using a prebuilt index (faster than find, but can be stale) |
locate nginx.conf |
tree |
Shows a directory structure visually | tree -L 2 project/ |
stat |
Shows detailed metadata about a file (size, permissions, timestamps) | stat deploy.sh |
A couple of these deserve more than a one-line description.
find is one of the most underused commands in DevOps. It's not just for locating files , it's a full query language for the filesystem. This is a genuinely common cleanup job on a server running low on disk:
# Delete log files older than 7 days from /var/log
find /var/log -name "*.log" -mtime +7 -delete
# Find files larger than 500MB anywhere under /var
find /var -type f -size +500M
# Find and change permissions on every .sh file in a directory
find . -name "*.sh" -exec chmod +x {} \;
rm -rf deserves genuine respect, not just a warning label. It deletes recursively (-r) and forcefully without confirmation (-f) and it does not check whether you meant the right directory. In January 2017, an engineer at GitLab intended to clear disk space by wiping the data directory on a secondary database server, but ran the command against the primary production database server instead. By the time the process was stopped, roughly 300 GB of production data had been removed and GitLab permanently lost around six hours of data , including thousands of projects, comments and new user accounts , because most of their backup mechanisms had also silently failed. GitLab published a detailed public postmortem about it and it's still one of the most cited "this is why you double-check before you hit enter" stories in the industry. The lesson isn't "never use rm -rf" , it's "always confirm which host and which path you're actually on before you run it," especially over SSH where the terminal prompt looks identical across ten different servers.
Viewing and Editing Files
| Command | What it does | Example |
|---|---|---|
cat |
Prints an entire file to the screen | cat /etc/hosts |
tac |
Same as cat, but reverses line order |
tac app.log |
less |
Views a file page by page, scrollable, searchable | less /var/log/syslog |
more |
Older, simpler pager than less |
more README.md |
head |
Shows the first N lines of a file | head -n 50 access.log |
tail |
Shows the last N lines of a file | tail -n 100 access.log |
tail -f |
Follows a file live as new lines are written | tail -f /var/log/app.log |
nano |
Simple, beginner-friendly terminal text editor | nano config.yaml |
vim / vi |
Powerful but steeper-learning-curve terminal editor | vim deploy.sh |
tee |
Writes output to a file and the screen at the same time | echo "ok" | tee status.txt |
tail -f is probably the single most-run command during an active incident. It's how you watch an application log in real time while you reproduce a bug or trigger a deploy. Pair it with grep for something more targeted:
tail -f /var/log/nginx/access.log | grep --line-buffered "50[0-9]"
That command tails the Nginx access log live and only shows you lines containing a 5xx status code as they happen , genuinely useful while watching a rollout for errors in real time.
Text Processing and Filtering
This is where Linux commands stop being "file management" and start being a genuine data-processing toolkit. If you work with logs , and in DevOps, you always work with logs , this category pays for itself constantly.
| Command | What it does | Example |
|---|---|---|
grep |
Searches text for a pattern | grep -i "error" app.log |
sed |
Stream editor , finds and replaces text | sed -i 's/DEBUG/INFO/g' config.env |
awk |
Pattern-scanning and column-based text processing | awk '{print $1, $9}' access.log |
cut |
Extracts specific columns/fields from text | cut -d',' -f2 users.csv |
sort |
Sorts lines of text | sort -n numbers.txt |
uniq |
Removes or counts duplicate adjacent lines | sort ips.txt | uniq -c |
wc |
Counts lines, words or characters | wc -l access.log |
tr |
Translates or deletes characters | tr 'a-z' 'A-Z' < file.txt |
diff |
Compares two files line by line | diff config.old config.new |
xargs |
Passes piped input as arguments to another command | find . -name "*.tmp" | xargs rm |
Here's a pipeline that comes up constantly in real DevOps work , finding your noisiest IP addresses or your most common error codes from a web server log:
# Top 10 IP addresses hitting your server, most frequent first
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# Count how many requests returned each HTTP status code
awk '{print $9}' access.log | sort | uniq -c | sort -rn
Each command in that pipe does one small job , extract a column, sort it, count duplicates, sort by count , and together they answer a question that would otherwise need a script or a log analytics tool. This is the actual philosophy behind Unix tools: small, composable pieces beat one giant do-everything program.
sed is what most people reach for to do search-and-replace across config files without opening an editor, especially useful in deployment scripts where you're templating environment-specific values:
# Replace the API endpoint in a config file, in-place
sed -i 's/api.staging.example.com/api.prod.example.com/' app.config
# Delete every line containing "DEBUG" from a log before archiving it
sed '/DEBUG/d' app.log > app_clean.log
Permissions and Ownership
Linux permissions confuse a lot of people early on, mostly because the octal notation (755, 644, 600) looks like a secret code until someone actually explains it.
| Permission | Symbol | Octal value |
|---|---|---|
| Read | r |
4 |
| Write | w |
2 |
| Execute | x |
1 |
| No permission | - |
0 |
Permissions are grouped into three sets: owner, group and others. So 755 means owner gets 7 (4+2+1 = read+write+execute), group gets 5 (4+1 = read+execute) and others get 5 too. 644 means owner can read and write, but everyone else can only read.
| Command | What it does | Example |
|---|---|---|
chmod |
Changes file permissions | chmod 755 deploy.sh or chmod +x deploy.sh |
chown |
Changes file owner (and optionally group) | chown appuser:appgroup app.log |
chgrp |
Changes just the group ownership | chgrp devops config.yaml |
umask |
Sets default permissions for newly created files | umask 022 |
sudo |
Runs a single command with elevated (root) privileges | sudo systemctl restart nginx |
su |
Switches to another user's shell entirely | su - deploy |
One extremely common beginner failure mode: a deploy script pulled from Git works fine locally but fails on the server with "Permission denied." Nine times out of ten, the script lost its executable bit somewhere along the way (Git doesn't always preserve it depending on how the file was created) and the fix is just:
chmod +x deploy.sh
./deploy.sh
Also worth saying plainly: chmod 777 is not a fix, it's a shortcut that creates a security problem. Giving every user on the system read, write and execute access to a file is rarely what you actually need and it's a common finding in security audits of production servers. If a script needs to run, chmod +x (adding execute for the owner or 755 if a group needs it too) is almost always the correct scope.
Process and Resource Management
| Command | What it does | Example |
|---|---|---|
ps |
Snapshot of currently running processes | ps aux | grep node |
top |
Live, refreshing view of process and resource usage | top |
htop |
A friendlier, colorized version of top (often needs installing) |
htop |
kill |
Sends a signal to a process by PID | kill 4521 |
kill -9 |
Forcefully terminates a process (SIGKILL) | kill -9 4521 |
killall |
Kills all processes matching a name | killall node |
nice / renice |
Sets or changes a process's CPU scheduling priority | renice -n 10 -p 4521 |
jobs |
Lists background jobs in the current shell | jobs |
bg / fg |
Sends a job to background/foreground | bg %1 |
nohup |
Runs a command immune to session hangups (survives logout) | nohup ./worker.sh & |
systemctl |
Manages systemd services (start, stop, restart, status) | systemctl restart nginx |
journalctl |
Views systemd service logs | journalctl -u nginx -f |
The difference between kill and kill -9 trips up a lot of people and it genuinely matters in production. A plain kill sends SIGTERM , a polite request asking the process to shut down, giving it a chance to close database connections, flush buffers and clean up temp files. kill -9 sends SIGKILL, which the operating system enforces immediately and the process cannot intercept, catch or ignore. That's useful when a process is truly stuck, but using kill -9 as your default habit on things like databases or message queues risks leaving data half-written or connections in a broken state. The general rule: try kill (SIGTERM) first, give the process a few seconds and only escalate to kill -9 if it's genuinely unresponsive.
systemctl and journalctl are the modern replacements for older service and manually grepping log files and they're everywhere in DevOps work because most current Linux distributions use systemd to manage services:
# Check whether a service is running
systemctl status docker
# Restart a service and immediately tail its logs
systemctl restart myapp
journalctl -u myapp -f
# See only the last hour of logs for a service
journalctl -u myapp --since "1 hour ago"
Monitoring System Health
A huge share of "why is production slow" investigations start with a handful of these commands, in roughly this order.
| Command | What it does | Example |
|---|---|---|
df -h |
Shows disk space usage, human-readable | df -h |
du -sh |
Shows how much space a directory is using | du -sh /var/log/* |
free -h |
Shows memory usage (RAM and swap) | free -h |
uptime |
Shows how long the system has been running and load averages | uptime |
vmstat |
Reports on memory, processes and CPU activity | vmstat 2 5 |
iostat |
Reports on CPU and disk I/O statistics | iostat -x 2 |
sar |
Historical system activity reporting (needs sysstat installed) |
sar -u 1 5 |
dmesg |
Shows kernel-level messages, useful for hardware/driver issues | dmesg | tail -30 |
lsof |
Lists open files and which process holds them | lsof -i :8080 |
w / who |
Shows who's currently logged into the system | w |
A genuinely common scenario: a server's disk fills up and something starts failing , deploys can't write build artifacts, the database can't write its logs or an app can't create temp files. df -h tells you a partition is at 100%, but not why. That's where du comes in:
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/xvda1 20G 20G 0 100% /
du -sh /var/* | sort -rh | head -10
# quickly shows which top-level directory under /var is the actual hog
Log files that were never rotated, an old core dump or a build directory that never got cleaned up are the usual suspects. This two-command combo , df -h to find the "what," du -sh to find the "where" , is one of the most reliable troubleshooting patterns in this entire list.
lsof -i :8080 is the fastest way to answer "what's already using this port," which comes up constantly when a service fails to start with "address already in use."
Networking Commands
| Command | What it does | Example |
|---|---|---|
ping |
Tests basic network reachability | ping -c 4 api.example.com |
curl |
Makes HTTP(S) requests from the command line | curl -I https://example.com |
wget |
Downloads files over HTTP/FTP | wget https://example.com/app.tar.gz |
ssh |
Connects to a remote machine securely | ssh -i key.pem ubuntu@10.0.0.5 |
scp |
Copies files over SSH | scp app.tar.gz ubuntu@10.0.0.5:/tmp/ |
rsync |
Efficiently syncs files/directories (only transfers changes) | rsync -avz ./build/ user@server:/var/www/ |
netstat |
Shows network connections and listening ports (older tool) | netstat -tulpn |
ss |
Modern replacement for netstat, faster on busy systems |
ss -tulpn |
dig / nslookup |
Queries DNS records | dig example.com |
traceroute / mtr |
Shows the network path to a destination | traceroute example.com |
nc (netcat) |
Tests raw TCP/UDP connections | nc -zv example.com 443 |
curl -I (capital I, for "headers only") is a fast health-check habit worth building , it hits an endpoint and shows you the response headers and status code without downloading the whole body:
curl -I https://api.example.com/health
# HTTP/2 200
# content-type: application/json
# ...
rsync deserves a specific mention because it's genuinely more efficient than scp for anything beyond a one-off file copy , it compares source and destination and transfers only the differences, which matters a lot when you're syncing a large build directory to a server repeatedly:
rsync -avz --delete ./dist/ deploy@server:/var/www/app/
The --delete flag removes files on the destination that no longer exist in the source, which is exactly what you want for a clean deploy, but it's also exactly the kind of flag you should double-check before running against the wrong target directory.
One security habit worth calling out directly: piping curl straight into a shell (curl https://some-site.com/install.sh | bash) is a common installation pattern, but it means you're executing a script you never actually read, from a server you don't control, with whatever privileges your shell has. It's convenient and it's also a real supply-chain risk , if that URL ever gets compromised or redirected, the script that runs on your machine changes without you noticing. Downloading the script first, reading it and then running it is a small amount of extra friction for a meaningful reduction in risk.
Package and Service Management
| Command | Distro family | Example |
|---|---|---|
apt / apt-get |
Debian, Ubuntu | sudo apt update && sudo apt install nginx |
dpkg |
Debian, Ubuntu (lower-level) | dpkg -l | grep nginx |
yum |
Older RHEL, CentOS | sudo yum install httpd |
dnf |
Newer RHEL, Fedora, CentOS Stream | sudo dnf install httpd |
rpm |
RHEL family (lower-level) | rpm -qa | grep httpd |
snap |
Ubuntu and others (sandboxed packages) | sudo snap install docker |
systemctl enable |
Any systemd-based distro | sudo systemctl enable nginx (starts on boot) |
Knowing which package manager belongs to which distro family matters more than it sounds , running yum on an Ubuntu box or apt on a CentOS box, is a common mistake when you're managing a mixed fleet or writing provisioning scripts meant to work across environments. Tools like Ansible often abstract this away with a generic package module, but when you're troubleshooting directly on a box, you still need to know which flavor you're dealing with.
Archiving and Compression
| Command | What it does | Example |
|---|---|---|
tar |
Bundles files into a single archive (optionally compressed) | tar -czvf backup.tar.gz /var/www/app |
tar -xzvf |
Extracts a gzip-compressed tar archive | tar -xzvf backup.tar.gz |
gzip / gunzip |
Compresses/decompresses a single file | gzip access.log |
zip / unzip |
Creates/extracts zip archives (cross-platform friendly) | zip -r site.zip ./dist |
The tar flags are genuinely worth memorizing because you'll type them constantly: c create, x extract, z gzip-compress, v verbose (show file names as it works), f specify the filename. tar -czvf and tar -xzvf cover the overwhelming majority of real-world archive needs , packaging a build for deployment or unpacking one on the target server.
Environment, Variables and Shell Productivity
| Command | What it does | Example |
|---|---|---|
export |
Sets an environment variable for the current shell and its children | export NODE_ENV=production |
env |
Lists all current environment variables | env | grep PATH |
alias |
Creates a shortcut for a longer command | alias ll='ls -la' |
source (or .) |
Runs a script in the current shell instead of a subshell | source .env |
history |
Shows previously run commands | history | grep ssh |
which |
Shows the full path of a command | which python3 |
whereis |
Similar to which, but also finds man pages and source |
whereis nginx |
man |
Opens the manual page for a command | man tail |
A quiet but real security issue: anything you type directly on the command line , including passwords or API keys passed as arguments , can end up in your shell's history file and is briefly visible to other users on the same system via ps aux. Passing secrets through environment variables set from a secrets manager or through files with restricted permissions, is safer than typing them inline in a command.
User and Access Management
| Command | What it does | Example |
|---|---|---|
useradd |
Creates a new user | sudo useradd -m deploy |
usermod |
Modifies an existing user (e.g., adds to a group) | sudo usermod -aG docker deploy |
passwd |
Sets or changes a user's password | sudo passwd deploy |
whoami |
Shows the current logged-in user | whoami |
id |
Shows user and group IDs | id deploy |
groups |
Shows which groups a user belongs to | groups deploy |
usermod -aG docker deploy is a command you'll type a lot when setting up new servers , it adds the deploy user to the docker group so they can run Docker commands without needing sudo every time, since Docker's daemon socket is normally only accessible by root and members of that group.
Cron Jobs and Scheduled Tasks
Cron is the Linux scheduler that quietly runs a massive amount of DevOps infrastructure , backups, cleanup scripts, health checks, certificate renewals , without anyone watching.
crontab -e # edit the current user's cron jobs
crontab -l # list current cron jobs
Cron syntax has five time fields, in this order:
* * * * * command-to-run
| | | | |
| | | | +----- day of week (0-6, Sunday = 0)
| | | +-------- month (1-12)
| | +----------- day of month (1-31)
| +-------------- hour (0-23)
+----------------- minute (0-59)
A realistic example , running a database backup script every night at 2:30 AM and appending its output to a log file:
30 2 * * * /opt/scripts/backup_db.sh >> /var/log/backup.log 2>&1
The 2>&1 at the end is worth understanding rather than just copy-pasting: it redirects file descriptor 2 (stderr) to wherever file descriptor 1 (stdout) is currently pointing , in this case, the log file , so both normal output and errors land in the same place instead of errors silently disappearing to the terminal (which, for a cron job, has no terminal to disappear to anyway).
Bash Scripting Basics for DevOps
Most real DevOps work isn't running one command at a time , it's stringing several together into a script that runs unattended, in a pipeline or on a schedule. Here's a small but realistic example: a health-check script that pings an endpoint and restarts a service if it's not responding.
#!/bin/bash
# health_check.sh , restarts the app service if the health endpoint fails
URL="http://localhost:8080/health"
SERVICE="myapp"
LOGFILE="/var/log/health_check.log"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
if [ "$STATUS" -ne 200 ]; then
echo "$(date): Health check failed with status $STATUS. Restarting $SERVICE." >> "$LOGFILE"
systemctl restart "$SERVICE"
else
echo "$(date): Health check OK ($STATUS)." >> "$LOGFILE"
fi
A few things worth pointing out, because they're common sources of bugs: the shebang line (#!/bin/bash) tells the system which interpreter to run the script with, $(...) captures a command's output into a variable and quoting variables ("$URL", "$STATUS") matters , without the quotes, a variable containing a space or being empty can silently break the command it's used in (this is called word splitting and it's one of the most common bash scripting mistakes). Making the script executable (chmod +x health_check.sh) and scheduling it with cron every few minutes turns this from a manual command into an actual piece of infrastructure.
Putting It Together: A Real Deployment Workflow
Here's roughly how these commands map onto a typical CI/CD deployment, from code checkout to verifying the app is healthy afterward.
A typical CI/CD deploy and the Linux commands doing the real work under the hood
+------------+ +------------+ +------------+ +------------+ +------------+
| CHECKOUT |-->| BUILD |-->| TEST |-->| PACKAGE |-->| DEPLOY |
+------------+ +------------+ +------------+ +------------+ +------------+
git clone npm/mvn/go grep/awk on tar -czvf ssh + scp/rsync
chmod +x build cmds test logs zip -r systemctl restart
build.sh exit code check journalctl -f
+--------------------------------------------------------------------------------+
| VERIFY / MONITOR |
| curl -I <health-endpoint> | ps aux | grep app | df -h | top |
+--------------------------------------------------------------------------------+
Even in a pipeline driven by Jenkins, GitLab CI or GitHub Actions, the individual steps inside each stage are, more often than not, exactly these commands , the CI tool is mostly an orchestrator deciding when to run them and what to do if they fail.
Common Mistakes That Bite People in Production
-
Running
rm -rfwithout checking the current directory or hostname first. The GitLab incident above is the famous example, but smaller versions of this happen constantly , always runpwdand check your prompt before a destructive command, especially over SSH where every terminal window can look identical. -
Defaulting to
kill -9on everything. It skips a process's cleanup logic entirely. Fine for a truly hung process, risky for a database or queue that needs to close connections gracefully. -
Using
chmod 777as a quick fix. It solves the immediate permission error and quietly creates a security gap that outlives the person who set it. -
Forgetting that
sudodoesn't carry your normal environment variables. A script that works fine as your user can behave differently undersudobecause$PATHand other variables can differ, which is a common source of "but it worked when I ran it manually" confusion. -
Not quoting variables in bash scripts. As shown above,
$VARwithout quotes can break on spaces, empty values or special characters."$VAR"is almost always the safer default. -
Confusing
dfanddu.dfreports space at the filesystem/partition level;dureports space used by specific files and directories. Mixing them up wastes time during an actual disk-full incident. -
Piping
curlstraight intobashwithout reading the script first. Convenient, but it means trusting a remote script blindly. -
Leaving secrets in shell history or command-line arguments. Anyone with
historyaccess or anyone who can runps auxat the right moment, could potentially see them.
Security Practices Around Linux Commands in DevOps
A few habits genuinely reduce risk without slowing you down much:
Prefer SSH key-based authentication over passwords and disable password-based SSH login on production servers where possible , it removes an entire category of brute-force attacks.
Avoid running things as root by default. Use sudo for the specific commands that need elevated privileges, rather than staying logged in as root for a whole session , it limits the damage a mistake or a compromised session can do.
Use journalctl and auditd (where configured) to review who ran what and when, especially after an incident , command history alone isn't reliable evidence since it's per-user and can be cleared.
Restrict firewall rules with ufw or iptables/nftables to only the ports a service actually needs. A database port open to the entire internet is a recurring, avoidable finding in security reviews.
Be deliberate about file permissions on anything holding credentials , config files with database passwords or API keys should typically be 600 (owner read/write only), not world-readable.
When to Use the Terminal and When Not To
Direct Linux commands are the right tool when you're debugging something specific, doing a one-off diagnostic check or writing a small automation script for a repeatable task. They're the wrong tool when you're trying to manage infrastructure state at scale , that's what tools like Ansible, Terraform or Kubernetes manifests exist for. Running chmod, useradd or systemctl commands by hand across fifty servers isn't just tedious, it's how configuration drift happens: server #37 quietly ends up different from the other forty-nine because someone fixed something manually and forgot to document it. The general rule that holds up well in practice: reach for the terminal to investigate and fix things quickly, but reach for infrastructure-as-code to make that fix permanent and repeatable across your whole fleet.
Linux Commands Cheat Sheet
| Category | Commands |
|---|---|
| Navigation | pwd, ls, cd, mkdir, rmdir, find, locate, tree |
| File operations | cp, mv, rm, touch, stat |
| Viewing files | cat, less, head, tail, tail -f |
| Editing | nano, vim, sed, tee |
| Text processing | grep, awk, cut, sort, uniq, wc, tr, diff, xargs |
| Permissions | chmod, chown, chgrp, umask, sudo, su |
| Processes | ps, top, htop, kill, killall, nice, jobs, nohup |
| Services | systemctl, journalctl |
| System health | df, du, free, uptime, vmstat, iostat, dmesg, lsof |
| Networking | ping, curl, wget, ssh, scp, rsync, ss, netstat, dig, traceroute, nc |
| Packages | apt, yum, dnf, dpkg, rpm |
| Archiving | tar, gzip, zip, unzip |
| Environment | export, env, alias, source, history, which, man |
| Users | useradd, usermod, passwd, whoami, id, groups |
| Scheduling | crontab |
Frequently Asked Questions
Which Linux commands should a DevOps beginner learn first?
Start with navigation (pwd, ls, cd), file operations (cp, mv, rm), viewing files (cat, less, tail -f) and permissions (chmod, chown). These cover the bulk of daily work before you touch anything more specialized.
Do I need to memorize every flag for every command?
No. Most experienced engineers use man command or command --help regularly, even for tools they use daily. What matters more is understanding what each command is generally for and building the habit of chaining simple commands together with pipes.
Is kill -9 bad to use?
It's not inherently bad, but it should be a last resort rather than a default. It forces immediate termination without letting the process clean up, which can cause data corruption for stateful services like databases. Try a regular kill (SIGTERM) first.
What's the real difference between netstat and ss?
They report similar information , active connections and listening ports , but ss is newer, faster on systems with many connections and is the tool most current distributions favor. netstat still works but is considered legacy on many systems.
Why is rsync preferred over scp for deployments?
rsync only transfers the differences between source and destination, which is much faster for repeated syncs of large directories. scp copies everything every time, which is fine for a single file but wasteful for a full build directory synced repeatedly.
How do I safely test a destructive command like rm -rf?
Use ls on the exact path first to confirm what's actually there, consider a dry run with find <path> -name "pattern" before adding -delete and where possible, avoid using wildcards in destructive commands until you've verified what they'll match.
Interview Perspective
Linux command questions in DevOps and SRE interviews usually aren't about trivia , they're testing whether you can reason about a system under pressure. Expect variations like: "A server's disk is full, walk me through how you'd find out why" (this is the df -h → du -sh pattern from earlier). "A process won't respond to a normal kill, what do you do and why" (tests whether you understand SIGTERM vs. SIGKILL, not just the syntax). "How would you find out what's listening on port 443 on this box" (ss -tulpn or lsof -i :443). "Explain what a pipe does and give an example combining three commands" (tests whether you actually understand composability, not just memorized commands). Interviewers generally care far more about your troubleshooting process than whether you remember an exact flag , narrating your reasoning out loud, even if you have to check a man page along the way, tends to land better than silently guessing.
The Bottom Line
Nobody memorizes every Linux command and nobody needs to. What separates someone comfortable in DevOps from someone who freezes at a terminal prompt isn't a bigger vocabulary of commands , it's knowing which handful of tools to reach for when something breaks and understanding what each one is actually doing well enough to combine them on the fly. Learn the categories in this guide, actually type the examples instead of just reading them and the rest tends to stick the way anything sticks: by using it when it actually matters.
