Linux commands remain the most direct way to understand, operate and troubleshoot Linux systems. They are best learned as practical task families rather than as isolated syntax to memorize.
Table of Contents
Introduction: Why Linux commands still matter
Linux commands are not just shortcuts for people who do not want to use a graphical interface. They are the natural language of the Linux operating system. When you open a terminal, you are not opening an old or limited tool. You are opening the most direct control layer of the system.
A graphical desktop shows icons, windows and menus. The command line shows the real structure underneath those visual elements. Files become paths. Programs become processes. Background applications become services. Storage becomes mounted filesystems. Internet connections become interfaces, routes, ports and packets. Permissions become visible rules that decide who can read, write or execute something.
This is why the Linux command line still matters everywhere: on personal computers, servers, Raspberry Pi devices, cloud infrastructure, containers, cybersecurity labs, development machines, hosting environments, routers, embedded devices, automation platforms and production systems. A person who understands Linux commands does not only know how to type instructions. They understand how the operating system thinks.
The goal of this guide is to explain Linux commands deeply, practically and clearly. It is not a dry list of commands. A list can help you remember syntax, but it does not teach real competence. Real competence comes from understanding what each command does, what problem it solves, what can go wrong, how it combines with other commands and how it fits into the larger structure of Linux administration.
A beginner often tries to memorize commands one by one. That is understandable, but it is not the best approach. Linux becomes much easier when you understand that commands belong to families of tasks.
| Task family | What you are trying to do | Commands you will use often |
|---|---|---|
| Orientation | Understand where you are in the filesystem | pwd, cd, ls, tree |
| File creation | Create folders and files | mkdir, touch, nano |
| File operations | Copy, move, rename and delete data | cp, mv, rm |
| File reading | Display and inspect text files | cat, more, less, head, tail |
| Searching | Find text, files and patterns | grep, find |
| Compression | Create and extract archives | tar, zip, unzip |
| Permissions | Control access to files and folders | chmod, chown |
| Package management | Install, update and remove software | apt, dpkg |
| System management | Restart, shut down and manage services | reboot, shutdown, service, systemctl |
| Process control | Inspect and stop running programs | ps, htop, kill, killall |
| Disk analysis | Check free space and folder size | df, du, mount, umount |
| Networking | Inspect connections and communicate remotely | ip, ping, wget, ssh, scp, rsync |
| Automation | Repeat commands and schedule work | history, crontab, screen |
| Text processing | Transform structured text and logs | awk, sed, cut, wc |
| Diagnostics | Investigate system activity | lsof, watch, netstat, ss, dmesg |
The command line becomes powerful because Linux commands are designed to be combined. One command produces output. Another command filters it. A third command sorts it. A fourth command saves it. A fifth command can run the whole workflow every night.
This is one of the deepest ideas in Linux: a command is useful by itself, but a command pipeline can become a custom tool.
Why Linux commands are still essential
Linux commands remain essential because many important systems do not have a graphical desktop at all. Most production servers are managed through SSH. Cloud instances often start as minimal systems. Containers usually run without a graphical interface. Network devices, Raspberry Pi projects and embedded systems may expose only a shell. Even when dashboards exist, they often show only part of the truth.
The command line is also important because it is precise. When you click a button in a graphical interface, you may not know exactly what happened. When you run a command, the instruction is explicit. You can copy it, document it, repeat it, automate it and audit it.
This makes Linux commands especially valuable in professional environments.
| Professional situation | Why commands matter |
|---|---|
| Server administration | Servers are often managed remotely through terminal sessions |
| Web hosting | Websites, logs, permissions, services and packages are controlled through shell tools |
| DevOps | Deployment, automation, containers and monitoring rely heavily on command-line workflows |
| Cybersecurity | Logs, network connections, permissions and processes must be inspected precisely |
| Software development | Git, package managers, build tools and deployment scripts are command-line oriented |
| Data processing | Large text files, logs and exports can be filtered faster from the terminal |
| Troubleshooting | Real errors are often visible in logs before they appear in dashboards |
| Automation | Repetitive work can be converted into scripts and scheduled jobs |
| Recovery | Broken systems may boot into a minimal shell where commands are the only option |
A graphical interface is comfortable when everything works. The terminal is essential when something does not.
For example, a web application may show a generic error page. The real reason may be inside a log file. You might need to inspect the last lines of a log, search for a specific error, check whether the web server is running, verify disk space, restart a service and confirm that the port is listening.
That is not one command. It is a workflow.
tail -n 50 /var/log/syslog
grep -i "error" /var/log/syslog
df -h
systemctl status apache2
sudo systemctl restart apache2
sudo ss -tulpnA person who only knows individual commands may see this as a collection of strange lines. A person who understands Linux sees a logical diagnostic sequence: read recent logs, search errors, check disk space, inspect service state, restart service, confirm network listening ports.
Why command combinations matter more than memorization
A single command answers a simple question. A combination answers a real operational question.
Simple question:
lsWhat files are here?
Better question:
ls -lahWhat files are here, including hidden files, with permissions and readable sizes?
Operational question:
find . -type f -name "*.log" -exec ls -lh {} \;Which log files exist under this directory, and how large are they?
More operational:
find . -type f -name "*.log" -size +10M -exec ls -lh {} \;Which log files are larger than 10 MB?
Even more useful:
find . -type f -name "*.log" -size +10M -exec gzip {} \;Compress log files larger than 10 MB.
This progression shows how Linux thinking works. You begin by inspecting. Then you filter. Then you act.
Never skip directly to the action stage when using find, rm, chmod, chown or administrative commands.
Key takeaways
At this point, the foundation is clear. Linux commands operate inside a structured filesystem. Paths can be absolute or relative. Commands have options and arguments. Output can be displayed, filtered, redirected and combined. The safest workflow is to inspect before changing anything.
The core beginner commands are already enough to perform real work:
| Command | Essential purpose |
|---|---|
pwd | Know your location |
cd | Move through directories |
ls | List files and metadata |
tree | Understand structure visually |
mkdir | Create directories |
touch | Create empty files |
nano | Edit text files |
cat | Display short files |
more | Read long files page by page |
less | Read and search long files comfortably |
head | Inspect the beginning of a file |
tail | Inspect the end of a file and follow logs |
grep | Search text patterns |
cp | Copy files and directories |
mv | Move and rename files |
rm | Remove files and directories |
rmdir | Remove empty directories |
man | Read command manuals |
These commands are not “basic” in the sense of being unimportant. They are basic in the sense that everything else depends on them.
If you understand these commands deeply, then package management, permissions, services, networking, logs, processes, automation and scripting become much easier. Linux is not a collection of disconnected tools. It is a coherent environment built around files, processes, users, permissions and text streams.
How to think at the Linux command line
The command line becomes safer and more useful when it is approached with the right mental model: inspect first, understand the context, make controlled changes and verify the result.
The Linux command line mindset
Before learning syntax, it is important to understand the mindset. Linux does exactly what you tell it to do. It does not always ask whether you are sure. It does not always protect you from your own mistakes. It assumes that the user knows what they are doing, especially when administrative privileges are used.
This is both powerful and dangerous.
A careful Linux user follows a basic principle: inspect first, understand second, change third, delete last.
| Stage | Purpose | Typical commands |
|---|---|---|
| Inspect | See where you are and what exists | pwd, ls, tree, df, du, ps |
| Understand | Read files, logs and service status | cat, less, head, tail, grep, systemctl status |
| Change | Edit, move, install, restart or reconfigure | nano, mv, cp, apt install, systemctl restart |
| Delete | Remove only after verification | rm, apt remove, find -delete |
A beginner often wants to jump directly to action. An expert first gathers context.
For example, before deleting a directory, do not immediately run this:
rm -rf old-projectA safer workflow is:
pwd
ls -lah
du -sh old-project
tree -L 2 old-project
rm -ri old-projectThis workflow confirms your location, lists the current directory, checks the size of the target, previews its structure and then removes it interactively.
Linux rewards this kind of discipline.
A professional mental model for Linux commands
Linux command-line work is not about typing fast. It is about thinking accurately.
A professional mental model follows these questions:
| Question | Command examples |
|---|---|
| Where am I? | pwd |
| What is here? | ls -lah, tree -L 2 |
| How big is it? | du -sh, df -h |
| What does it contain? | cat, less, head, tail |
| Does it contain a pattern? | grep |
| Where is the file? | find |
| Who owns it? | ls -l, stat |
| Can I access it? | ls -l, chmod, chown |
| Is a process running? | ps, htop |
| Is a service running? | systemctl status |
| Is a port listening? | ss -tulpn |
| What changed recently? | ls -lt, find -mtime |
| What is failing? | tail, grep, journalctl |
A beginner memorizes commands. An expert asks better questions.
Practical workflow: build a command-line investigation habit
When something is wrong, do not jump to solutions. Collect facts.
For a service problem:
hostname
uptime
df -h
systemctl status service-name
journalctl -u service-name -n 100
sudo ss -tulpnFor a disk problem:
df -h
sudo du -h --max-depth=1 / | sort -h
sudo find /var/log -type f -size +100M -exec ls -lh {} \;
sudo lsof | grep deletedFor a network problem:
ip a
ip route
ping -c 4 8.8.8.8
getent hosts example.com
sudo ss -tulpnFor a permission problem:
whoami
pwd
ls -ld .
ls -l target
namei -l /full/path/to/targetThe command namei -l shows permissions along each component of a path. It is very useful for permission troubleshooting.
Example:
namei -l /var/www/site/uploads/image.jpgThis can reveal that the file itself is readable, but one parent directory blocks access.
Practical workflow: write commands as documentation
A good Linux command is also documentation. It shows what was done.
Compare this vague note:
I cleaned the logs.with this clear command record:
sudo find /var/log/myapp -type f -name "*.gz" -mtime +30 -deleteThe command tells you:
| Detail | Information |
|---|---|
| Location | /var/log/myapp |
| Type | Regular files only |
| Pattern | Compressed .gz files |
| Age | Older than 30 days |
| Action | Delete |
This is why command-line work is reproducible. The exact action can be reviewed and repeated.
For important operations, save the command and output.
sudo find /var/log/myapp -type f -name "*.gz" -mtime +30 -exec ls -lh {} \; | tee cleanup-preview.txtThen after deletion:
sudo find /var/log/myapp -type f -name "*.gz" -mtime +30 -deleteSave a maintenance log:
{
date
hostname
uptime
df -h
systemctl --failed
} | tee maintenance-check.txtThis creates a simple record of system state.
How to think about advanced shell work
Advanced shell usage is not about writing unreadable one-liners. It is about building reliable, inspectable workflows.
A good shell workflow has these qualities:
| Quality | Meaning |
|---|---|
| Clear | You can understand it later |
| Safe | It previews or limits destructive actions |
| Quoted | Variables and paths are protected |
| Logged | Important output is saved |
| Repeatable | It can run again with predictable behavior |
| Testable | It can be run manually before scheduling |
| Recoverable | It creates backups before risky changes |
| Minimal | It changes only what must be changed |
A bad shell workflow is clever but fragile. A good shell workflow is boring, explicit and safe.
For example, this is risky:
rm -rf $(find . -name "*.tmp")This is better:
find . -type f -name "*.tmp" -printThen:
find . -type f -name "*.tmp" -deleteBetter still when learning:
find . -type f -name "*.tmp" -exec rm -i {} \;The safest Linux users are not the slowest. They are the ones who build verification into their workflow.
Terminal, shell, commands and built-in help
Before individual commands make sense, the relationship between the terminal, the shell, command syntax and built-in documentation must be clear.
Understanding the terminal, shell and command
Many people use the words terminal, shell and command as if they were the same thing. They are related, but not identical.
| Term | Meaning | Practical explanation |
|---|---|---|
| Terminal | The interface window where you type | The visible program that accepts keyboard input |
| Shell | The command interpreter | The program that reads and executes your commands |
| Command | The instruction you run | A program, shell built-in or script |
| Prompt | The text shown before your input | It often shows user, host and current directory |
| Argument | The target of a command | A filename, directory, user, service or option value |
| Option | A modifier that changes behavior | Usually begins with - or -- |
When you type this:
ls -lah /home/patyou are typing into a terminal. The shell interprets the line. The command is ls. The options are -lah. The argument is /home/pat.
The shell then runs the ls program and displays the result in the terminal.
A typical shell prompt may look like this:
pat@server:~/projects$This can be understood as:
| Prompt part | Meaning |
|---|---|
pat | Current user |
server | Hostname of the machine |
~/projects | Current directory |
$ | Normal user prompt |
If the prompt ends with #, it often means you are acting as the root user.
root@server:/etc#This deserves attention. A root shell can change or damage the entire system.
The basic structure of a Linux command
Most Linux commands follow a predictable structure.
command options argumentsA simple example:
lsThis runs the command ls without extra options or arguments.
A more specific example:
ls -lah /var/logThis command has three parts.
| Part | Value | Meaning |
|---|---|---|
| Command | ls | List directory contents |
| Options | -lah | Long format, show hidden files, human-readable sizes |
| Argument | /var/log | The directory to list |
Options change how a command behaves. Arguments tell the command what to work on.
Short options usually begin with one dash.
ls -lLong options usually begin with two dashes.
ls --allSeveral short options can often be combined.
ls -l -a -hcan usually be written as:
ls -lahThis is why Linux commands may look cryptic at first. They are compact, but the logic is consistent.
| Command | Expanded meaning |
|---|---|
ls -l | List in long format |
ls -a | List all files including hidden files |
ls -h | Show human-readable sizes |
ls -lah | Combine long format, all files and readable sizes |
Not every command supports the same options. You should not assume that -h, -r or -f always means the same thing in every command. Many common patterns exist, but the manual is the final authority.
Getting help from Linux itself
One of the most important commands is man, which opens the manual page for another command.
man lsInside a manual page, you can usually use these keys:
| Key | Action |
|---|---|
| Space | Move forward one page |
b | Move backward one page |
/word | Search for a word |
n | Go to next search result |
q | Quit |
The manual page may seem dense, but it teaches you how Linux documents itself.
Most commands also support quick help.
ls --helpAnother useful tool is type, which tells you what kind of command you are running.
type cd
type ls
type grepExample output may show that cd is a shell built-in while ls is an external program.
cd is a shell builtin
ls is /usr/bin/ls
grep is /usr/bin/grepThis matters because some commands are built directly into the shell, while others are separate executable files.
You can also locate commands with:
command -v ls
which lsA good learning habit is this:
command --help
man command
type command
command -v commandReplace command with the command you are learning.
type, command, which and whereis
Linux offers several ways to identify commands.
type lsThis tells whether ls is a shell built-in, alias, function or external command.
command -v lsThis prints the path or command resolution.
which lsThis often prints the path of the executable.
whereis lsThis can show binary, source and manual locations.
Comparison:
| Command | Best use |
|---|---|
type command | Understand how the shell interprets a command |
command -v command | Script-friendly command lookup |
which command | Quick executable path lookup |
whereis command | Locate binary and related manual/source paths |
Examples:
type cd
type ls
type grep
command -v bash
whereis bashYou may see that cd is a shell built-in, while grep is an external program.
This distinction matters because built-ins affect the current shell. For example, cd must be a shell built-in because it changes the current directory of the shell itself.
man pages are the built-in university of Linux
The command man opens the manual page for a command.
man findManual pages are not always beginner-friendly, but they are precise and complete. A serious Linux user should learn how to read them.
A typical manual page has sections such as:
| Section | Purpose |
|---|---|
| NAME | Short command name and summary |
| SYNOPSIS | Syntax structure |
| DESCRIPTION | What the command does |
| OPTIONS | Available options |
| EXAMPLES | Sometimes included |
| FILES | Related files |
| SEE ALSO | Related commands |
| AUTHOR | Author information |
| BUGS | Known issues or reporting information |
Inside man, use:
| Key | Action |
|---|---|
| Space | Next page |
b | Previous page |
/word | Search for word |
n | Next search result |
N | Previous search result |
q | Quit |
Example workflow:
man chmodSearch inside manual:
/recursivePress n to move to the next occurrence.
Read the synopsis carefully. For example, syntax like this:
chmod [OPTION]... MODE[,MODE]... FILE...has meaning.
| Symbol | Meaning |
|---|---|
[OPTION] | Optional option |
... | Can be repeated |
MODE | Permission mode |
FILE | Target file or directory |
Manual pages become easier once you understand their notation.
apropos searches manual descriptions
If you do not know the command name, use apropos.
apropos passwordThis searches manual page descriptions for the word password.
Another example:
apropos networkIf apropos gives poor results, the manual database may need updating:
sudo mandbThe command apropos is useful when you know what you want to do but not which command does it.
Example:
apropos "disk usage"You may discover commands such as du or related tools.
help for shell built-ins
Some commands are shell built-ins, not external programs. For Bash built-ins, use help.
help cdhelp exporthelp historyYou can also use:
type cdIf it says:
cd is a shell builtinthen help cd is often more relevant than searching for an external executable.
This matters for commands such as:
| Built-in | Purpose |
|---|---|
cd | Change shell directory |
echo | Print text |
export | Export variables to environment |
history | Show shell history |
alias | Define command shortcuts |
jobs | Show shell jobs |
fg | Bring job to foreground |
bg | Resume job in background |
The shell itself is a tool, and built-ins are part of that tool.
The Linux filesystem, paths and essential directories
Linux commands operate inside a structured filesystem. Paths, core directories and file-like system interfaces explain where data lives and how Linux exposes system state.
The Linux filesystem as a tree
The most important structural idea in Linux is that the filesystem is a tree. Everything starts at /, called the root directory. Every file, folder, device and mounted disk appears somewhere under this root.
This is different from Windows, where drives are commonly shown as C:, D: and so on. Linux does not organize the system around drive letters. It organizes everything under one unified tree.
The root directory is written as:
/A full path may look like this:
/home/pat/Documents/report.txtThis path can be read from left to right.
| Path part | Meaning |
|---|---|
/ | Start at the root directory |
home | Enter the home area |
pat | Enter the user folder named pat |
Documents | Enter the Documents directory |
report.txt | Open or refer to the file |
Linux paths are case-sensitive. This means:
Documents
documents
DOCUMENTSare three different names.
This is a common source of beginner errors. If a folder is named Downloads, this command will fail:
cd downloadsThe correct command is:
cd DownloadsAbsolute paths and relative paths
A path tells Linux where something is. There are two main types: absolute paths and relative paths.
| Path type | Description | Example |
|---|---|---|
| Absolute path | Starts from / and gives the full location | /home/pat/Documents/file.txt |
| Relative path | Starts from the current directory | Documents/file.txt |
An absolute path always begins with /.
cd /home/pat/DocumentsThis command works no matter where you currently are, because it gives the full address.
A relative path does not begin with /.
cd DocumentsThis command depends on your current directory. If you are in /home/pat, it means /home/pat/Documents. If you are in /var/log, it means /var/log/Documents.
This is why pwd is so important. Before running commands that change files, always know your current location.
pwdExample output:
/home/patA few special path symbols are used constantly.
| Symbol | Meaning | Example |
|---|---|---|
. | Current directory | find . -name "*.txt" |
.. | Parent directory | cd .. |
~ | Current user’s home directory | cd ~ |
/ | Root directory | cd / |
- | Previous directory with cd | cd - |
Examples:
cd .This technically means stay in the current directory.
cd ..This moves one level up.
cd ~This moves to your home directory.
cd /This moves to the root directory.
cd -This returns to the previous directory.
Essential Linux directories
Linux directories are not random. They follow a long-established structure. You do not need to memorize every directory immediately, but you should understand the major ones because they appear constantly in commands, logs, configuration and administration.
| Directory | Purpose | Practical importance |
|---|---|---|
/ | Root of the entire filesystem | Everything begins here |
/home | Home directories of normal users | Personal files, documents, scripts and user configuration |
/root | Home directory of the root user | Administrator’s private home folder |
/etc | System configuration files | Network, services, users, package sources and system settings |
/var | Variable data that changes over time | Logs, caches, mail, databases and web data |
/var/log | Log files | One of the most important troubleshooting locations |
/tmp | Temporary files | Short-lived files created by users and programs |
/usr | Installed software and shared system resources | Applications, libraries and documentation |
/usr/bin | Many user commands | Common executable programs |
/usr/sbin | Many administrative commands | System administration tools |
/bin | Essential basic commands | Critical commands needed during boot and recovery |
/sbin | Essential system commands | Administrative commands needed during boot and repair |
/boot | Bootloader and kernel files | Needed for system startup |
/dev | Device files | Disks, partitions, terminals and hardware interfaces |
/proc | Virtual process and kernel information | Live system data exposed as files |
/sys | Kernel and hardware information | Device and driver information |
/media | Mount point for removable media | USB drives and external disks may appear here |
/mnt | Temporary manual mount point | Common place for manually mounted filesystems |
/opt | Optional third-party software | Some manually installed applications live here |
The most important practical distinction is between user space and system space.
Your home directory is usually safe for personal work.
/home/patSystem directories require caution.
/etc
/usr
/var
/boot
/devA useful rule is: inside your home directory you are usually managing your own data; outside your home directory you may be changing the operating system.
The root directory and the root user are not the same thing
Beginners often confuse / with the root user.
The root directory is the top of the filesystem.
/The root user is the administrative superuser.
rootThe root user’s home directory is:
/rootThese are three related but different concepts.
| Term | Meaning |
|---|---|
/ | Root directory of the filesystem |
root | Administrative superuser account |
/root | Home directory of the root user |
This distinction matters. When someone says “go to root,” they may mean the root directory /. When someone says “run as root,” they mean run with administrative privileges. When someone says “root’s home,” they mean /root.
The deeper structure behind Linux commands
At this point, the command line is no longer just a set of tools. It is a coherent model of the operating system.
Linux exposes system reality through:
| System reality | Command-line representation |
|---|---|
| Files | Paths, permissions, ownership, timestamps |
| Programs | Processes, PIDs, services |
| Users | UID, GID, groups |
| Storage | Filesystems, mounts, blocks, inodes |
| Network | Interfaces, routes, ports, sockets |
| Logs | Text files or journal entries |
| Configuration | Mostly text files under /etc |
| Automation | Scripts, cron jobs, services |
| Security | Permissions, ownership, sudo, SSH keys |
The same command patterns repeat everywhere.
To inspect:
ls
cat
less
systemctl status
journalctlTo search:
grep
find
awkTo count:
wc
uniq -cTo transform:
sed
awk
cut
sortTo act:
cp
mv
rm
chmod
chown
systemctl restart
apt installTo automate:
bash scripts
crontab
systemd timersA beginner learns commands. An expert sees patterns.
Linux becomes powerful when you understand what is a file and what only looks like a file
One of the most important ideas in Linux is that many things are represented as files or file-like objects. This does not mean everything is a normal document stored on disk. It means the operating system exposes devices, processes, kernel information, terminals, sockets and system state through file paths.
This design gives the command line extraordinary power. You can inspect a process through /proc. You can inspect hardware through /sys. You can see devices in /dev. You can read logs in /var/log. You can configure services through text files under /etc. You can redirect output into files, read from files and connect commands through streams.
A beginner sees files as documents. A Linux administrator sees files as the interface of the operating system.
| Location | What it represents | Practical meaning |
|---|---|---|
/etc | Configuration files | System and service behavior |
/var/log | Log files | Evidence of events and errors |
/dev | Device files | Disks, terminals, random generators, hardware interfaces |
/proc | Process and kernel information | Live runtime system state |
/sys | Kernel device model | Hardware, drivers and low-level system information |
/run | Runtime state | PID files, sockets, temporary service state |
/tmp | Temporary files | Short-lived files used by users and programs |
/var/tmp | Temporary files that may persist longer | Temporary data not always removed on reboot |
The directories /proc and /sys are especially important because they are virtual filesystems. Their contents are not ordinary files stored on disk. They are generated by the kernel to expose live information.
For example:
cat /proc/cpuinfoshows CPU information.
cat /proc/meminfoshows memory information.
ls /procshows many numbered directories. Those numbers are process IDs.
If a process has PID 1234, then this directory may exist:
/proc/1234Inside it, Linux exposes information about that running process.
ls -lah /proc/1234This can show process command line, environment, file descriptors, current working directory and executable path.
This is why Linux troubleshooting often feels transparent. The system exposes itself through inspectable paths.
Understanding /proc through real examples
The /proc filesystem is one of the most useful places for understanding a running Linux system. It contains live information about processes and kernel state.
Show CPU information:
cat /proc/cpuinfoShow memory information:
cat /proc/meminfoShow kernel command line used at boot:
cat /proc/cmdlineShow mounted filesystems:
cat /proc/mountsShow uptime directly from proc:
cat /proc/uptimeShow load average:
cat /proc/loadavgMany normal commands read from /proc internally or present the same kind of information in a more readable way.
| Command | Related proc information |
|---|---|
uptime | /proc/uptime, /proc/loadavg |
free | /proc/meminfo |
ps | /proc/PID/ directories |
mount | /proc/mounts |
top | Process and memory information from /proc |
This means Linux tools often transform raw system information into readable output.
For example, compare:
cat /proc/meminfowith:
free -hThe first shows detailed raw memory fields. The second summarizes memory in a more human-friendly way.
Both are useful. The command is easier for daily work. The proc file is useful for deeper understanding and scripts.
Inspecting a running process through /proc
Suppose a process has PID 1234.
Show the command line that started it:
tr '\0' ' ' < /proc/1234/cmdlineShow the executable path:
readlink -f /proc/1234/exeShow the process working directory:
readlink -f /proc/1234/cwdShow open file descriptors:
ls -lah /proc/1234/fdShow environment variables:
sudo tr '\0' '\n' < /proc/1234/environThe null character \0 is used internally as a separator in some proc files, so tr converts it into spaces or new lines for readability.
This process inspection is extremely useful when you need to answer questions such as:
| Question | Command |
|---|---|
| What command started this process? | tr '\0' ' ' < /proc/PID/cmdline |
| Which binary is running? | readlink -f /proc/PID/exe |
| What directory is it running from? | readlink -f /proc/PID/cwd |
| What files does it have open? | ls -lah /proc/PID/fd |
| What environment does it have? | tr '\0' '\n' < /proc/PID/environ |
This is especially helpful when ps aux shows a process but not enough detail.
Example workflow:
ps aux | grep myappFind the PID, then:
readlink -f /proc/PID/exe
readlink -f /proc/PID/cwd
tr '\0' ' ' < /proc/PID/cmdlineReplace PID with the real process ID.
File descriptors explain open files and sockets
Every running process can have open file descriptors. A file descriptor is a numeric handle used by a process to read from or write to something.
Common file descriptors are:
| Descriptor | Meaning |
|---|---|
0 | Standard input |
1 | Standard output |
2 | Standard error |
But processes can have many more. They may point to files, pipes, sockets, devices or deleted files.
Inspect file descriptors for a process:
ls -lah /proc/PID/fdYou may see output like:
0 -> /dev/null
1 -> /var/log/myapp/output.log
2 -> /var/log/myapp/error.log
3 -> socket:[123456]
4 -> /var/lib/myapp/data.dbThis tells you what the process is connected to.
A process writing to a deleted file may show something like:
1 -> /var/log/myapp/old.log (deleted)This explains why disk space may remain used even after deleting a large file.
Use lsof for a more readable view:
sudo lsof -p PIDFind deleted open files:
sudo lsof | grep deletedThis is one of the most important advanced disk troubleshooting commands.
Understanding /dev and device files
The /dev directory contains device files. These are special files that represent devices or kernel interfaces.
List some devices:
ls -lah /dev | headCommon examples:
| Device | Meaning |
|---|---|
/dev/sda | First detected disk in many systems |
/dev/sda1 | First partition on that disk |
/dev/sdb | Another disk |
/dev/null | Discards all data written to it |
/dev/zero | Produces endless zero bytes |
/dev/random | Random data source |
/dev/urandom | Non-blocking random data source |
/dev/tty | Current terminal |
/dev/pts/0 | Pseudo-terminal session |
The special file /dev/null is used constantly in shell work.
Discard normal output:
command > /dev/nullDiscard error output:
command 2> /dev/nullDiscard everything:
command > /dev/null 2>&1This is useful in scripts when you intentionally do not need output. But it can also hide errors, so use it carefully.
Example:
tar -tzf backup.tar.gz > /dev/null 2>&1This tests whether the archive can be read without printing the file list. The exit status tells whether it succeeded.
echo $?Understanding /run and runtime state
The /run directory stores runtime data. This data is created after boot and usually does not persist across reboots.
Examples include:
| Runtime data | Purpose |
|---|---|
| PID files | Store process IDs for services |
| Sockets | Local communication endpoints |
| Lock files | Indicate that a resource is in use |
| Service state | Temporary status files |
| User sessions | Runtime information about logged-in users |
List /run:
ls -lah /runLook for service-related runtime files:
ls -lah /run | headSome services create sockets under /run.
Example:
find /run -type s 2> /dev/null | headThe -type s option in find searches for sockets.
Runtime files can explain how services communicate locally. For example, databases, PHP-FPM, system services and desktop sessions may use Unix sockets stored under /run.
A socket is not a normal text file. It is a communication endpoint.
Check file type:
ls -l /run/some-socketYou may see the first character s, meaning socket.
Understanding /etc as the configuration center
The /etc directory is where most system-wide configuration lives. It is one of the most important directories on a Linux system.
Examples:
| Path | Purpose |
|---|---|
/etc/hosts | Local hostname mappings |
/etc/hostname | Static hostname on many systems |
/etc/fstab | Persistent filesystem mounts |
/etc/passwd | User account information |
/etc/group | Group information |
/etc/shadow | Protected password hash information |
/etc/ssh/sshd_config | SSH server configuration |
/etc/systemd/system/ | Custom systemd unit files |
/etc/apt/sources.list | APT repository configuration |
/etc/apt/sources.list.d/ | Additional APT source files |
/etc/nginx/ | Nginx configuration |
/etc/apache2/ | Apache configuration on Debian-based systems |
Because /etc controls system behavior, editing it requires discipline.
A safe configuration editing workflow is:
sudo cp /etc/service/config.conf /etc/service/config.conf.$(date +%Y-%m-%d-%H-%M).backup
sudo nano /etc/service/config.confThen test if possible:
sudo service-name --test-configThen reload or restart:
sudo systemctl reload service-nameThen verify:
systemctl status service-name
journalctl -u service-name -n 50Never treat /etc as an ordinary personal notes folder. It is the control panel of the operating system.
The difference between configuration, state and data
Linux systems contain different kinds of information. Mixing them up causes bad backups, bad migrations and bad repairs.
| Type | Meaning | Common location |
|---|---|---|
| Configuration | Defines behavior | /etc, application config directories |
| State | Current runtime condition | /run, service memory, PID files |
| Data | User or application content | /home, /var/lib, /var/www, databases |
| Logs | Records of events | /var/log, journal |
| Cache | Rebuildable temporary data | /var/cache, application cache folders |
| Binaries | Executable programs | /bin, /usr/bin, /usr/sbin |
| Libraries | Shared code used by programs | /lib, /usr/lib |
This matters for backups.
A website may require:
| Component | Example |
|---|---|
| Code files | /var/www/site |
| Uploaded files | /var/www/site/uploads |
| Configuration | /etc/nginx, application .env file |
| Database | MySQL, PostgreSQL, SQLite or another database |
| Scheduled jobs | User crontab or systemd timers |
| Service definitions | /etc/systemd/system |
| SSL certificates | Often under /etc/letsencrypt or service-specific paths |
A backup of /var/www/site alone may not be enough.
A migration plan should identify all required components.
Understanding /var/lib as application state
The directory /var/lib often stores application state and persistent data managed by services.
Examples may include:
| Path | Possible purpose |
|---|---|
/var/lib/mysql | MySQL or MariaDB data |
/var/lib/postgresql | PostgreSQL data |
/var/lib/docker | Docker data |
/var/lib/apt | APT package state |
/var/lib/systemd | systemd state |
/var/lib/dpkg | Debian package database |
Do not casually delete content from /var/lib. It may contain databases, package metadata or service state.
Check size:
sudo du -h --max-depth=1 /var/lib | sort -hIf /var/lib/docker is huge, the answer is not simply:
sudo rm -rf /var/lib/dockerThat can destroy containers, images and volumes.
Instead, use the appropriate application tools.
The same logic applies to databases. Do not delete database files manually unless you understand the database engine and recovery plan.
Understanding /var/cache
The directory /var/cache stores cache data. Cache is usually rebuildable, but it can still matter.
Check size:
sudo du -h --max-depth=1 /var/cache | sort -hAPT package cache:
sudo du -sh /var/cache/aptClean APT cache:
sudo apt cleanApplication caches may live under /var/cache, but do not delete them blindly. Some services expect cache directories to exist with correct ownership.
A safe cache cleanup process is:
sudo du -h --max-depth=1 /var/cache | sort -hThen identify the application. Read documentation or service status. Stop the service if needed. Clean only the correct cache. Restart or reload. Verify logs.
Cache is generally less critical than data, but uncontrolled deletion can still cause service disruption.
Understanding /var/log as evidence
The directory /var/log is not just storage. It is evidence.
Before deleting logs, consider whether you need them for troubleshooting, security analysis or compliance.
Check log size:
sudo du -h --max-depth=1 /var/log | sort -hFind large logs:
sudo find /var/log -type f -size +100M -exec ls -lh {} \;Search errors:
sudo grep -R -i "error" /var/log 2> /dev/null | headCheck recent logs:
journalctl -p warning -n 100If logs are huge, ask why.
| Symptom | Possible cause |
|---|---|
| One log file grows rapidly | Application error loop |
| Many compressed logs exist | Retention too long |
| Journal uses large space | systemd journal retention not limited |
| Auth log grows quickly | SSH brute force attempts |
| Web access log huge | High traffic or bot activity |
| Error log huge | Misconfiguration or application failure |
Deleting logs may free space but hide the cause.
A safer approach is to archive, truncate if necessary and fix the underlying problem.
Working with journal and text logs together
Some systems use both traditional log files and systemd journal logs. You may need both.
For a service:
journalctl -u nginx -n 100Traditional Nginx logs:
tail -n 100 /var/log/nginx/error.log
tail -n 100 /var/log/nginx/access.logFor SSH:
journalctl -u ssh -n 100or:
sudo tail -n 100 /var/log/auth.logFor package operations:
cat /var/log/apt/history.logand:
journalctl -u apt-daily.serviceThe exact logging location depends on the distribution, service and configuration.
A good habit is to check both service logs and application-specific logs.
Understanding system boot at a practical level
Boot is the process that starts the Linux system. You do not need to know every low-level detail at first, but you should understand the practical sequence.
A simplified boot flow:
| Stage | Meaning |
|---|---|
| Firmware | Hardware initializes and loads bootloader |
| Bootloader | Loads Linux kernel and initial RAM disk |
| Kernel | Initializes hardware and core system |
| init system | Starts user space services |
| systemd target | Brings system to multi-user or graphical state |
| Services | Network, SSH, web servers, databases and other daemons start |
On many modern systems, systemd manages services during boot.
Check boot logs:
journalctl -bCheck previous boot:
journalctl -b -1List boots:
journalctl --list-bootsCheck failed services after boot:
systemctl --failedCheck boot time summary:
systemd-analyzeShow slow services:
systemd-analyze blameShow critical chain:
systemd-analyze critical-chainThis helps diagnose slow boot or services delaying startup.
Safety principles before changing the system
Administrative power is useful only when it is controlled. The safest Linux work happens when every edit, deletion, restart or installation is preceded by inspection and followed by verification.
Normal user privileges and sudo
Linux is designed around users and permissions. A normal user should be able to manage personal files but should not accidentally damage system files. Administrative changes require elevated privileges.
The most common way to run a command with administrative privileges is sudo.
sudo apt updateThis means: run apt update as a superuser, if the current user is allowed to use sudo.
Common tasks that usually require sudo include installing software, editing system configuration, restarting system services, changing ownership of system files and mounting disks.
| Task | Needs sudo? | Example |
|---|---|---|
| List files in your home directory | Usually no | ls ~/Documents |
| Create a folder in your home directory | Usually no | mkdir ~/projects |
| Edit your own text file | Usually no | nano notes.txt |
| Install software | Yes | sudo apt install htop |
| Update package lists | Yes | sudo apt update |
| Edit system configuration | Yes | sudo nano /etc/hosts |
| Restart a service | Yes | sudo systemctl restart apache2 |
| Change ownership of system files | Yes | sudo chown user:group file |
| Mount a disk manually | Usually yes | sudo mount /dev/sdb1 /mnt/usb |
A dangerous beginner habit is using sudo for everything. That is not necessary and not safe. Use sudo only when the task requires it.
Compare these two commands:
rm notes.txtThis removes a file in the current directory if your user has permission.
sudo rm notes.txtThis removes it with elevated privileges. If you are in the wrong directory, the risk is higher.
The more power a command has, the more carefully it should be checked.
How to read command examples correctly
Many command examples use placeholders. A placeholder is not meant to be typed literally. It represents something you must replace.
Example:
cp <source> <target>You should not type <source> and <target> exactly. You replace them with real paths.
Correct example:
cp report.txt /home/pat/Documents/Common placeholders include:
| Placeholder | Meaning | Real example |
|---|---|---|
<file> | A file name | notes.txt |
<folder> | A directory name | /home/pat/projects |
<user> | A username | pat |
<host> | A hostname or IP address | 192.168.1.10 |
<package> | A package name | htop |
<service> | A service name | apache2 |
<command> | A command to run | ls -lah |
When you see:
ssh <user>@<ip>you should type something like:
ssh pat@192.168.1.10Understanding placeholders prevents many beginner mistakes.
Your first safety workflow
Before learning many commands, learn this safety workflow:
pwd
ls -lahThis shows where you are and what is there.
Add one more command when working with directories:
tree -L 2If tree is not installed, use:
find . -maxdepth 2A safe inspection sequence is:
pwd
ls -lah
tree -L 2
du -sh .This gives four layers of understanding.
| Command | What it tells you |
|---|---|
pwd | Current location |
ls -lah | Files, folders, permissions, owners, sizes and hidden entries |
tree -L 2 | Structure two levels deep |
du -sh . | Total size of the current directory |
This sequence does not modify anything. It is safe. It is also professional. Many serious mistakes happen because users act before inspecting.
The safest way to learn Linux commands
The safest way to learn Linux commands is to create a practice directory in your home folder and experiment there.
mkdir -p ~/linux-command-lab
cd ~/linux-command-lab
pwdInside this lab, create sample files:
mkdir documents logs scripts backups
touch documents/report.txt
touch logs/app.log
touch scripts/backup.shDisplay the structure:
treeExpected structure:
.
├── backups
├── documents
│ └── report.txt
├── logs
│ └── app.log
└── scripts
└── backup.shNow you can safely practice copying, moving, renaming, reading and deleting.
cp documents/report.txt backups/report-backup.txt
mv logs/app.log logs/application.log
ls -lah logs
treeRemove practice files only after inspecting:
ls -lah backups
rm -i backups/report-backup.txtThis practice environment prevents damage to real files.
Remote administration safety habits
Remote Linux work requires extra caution because mistakes can affect systems you cannot physically access.
Use these habits:
| Habit | Why it matters |
|---|---|
| Confirm hostname after SSH login | Prevents working on wrong server |
| Keep one SSH session open during critical changes | Maintains recovery access |
Use screen or tmux for long tasks | Survives connection loss |
| Back up config files before editing | Allows quick rollback |
| Test configuration before reloading | Prevents service failure |
Use --dry-run with rsync | Prevents accidental file deletion |
Avoid rm -rf over SSH unless fully verified | Remote deletion is hard to recover |
| Check disk space before upgrades | Full disks can break package operations |
| Reload instead of restart when appropriate | Reduces disruption |
| Read logs before changing settings | Avoids blind troubleshooting |
A safe remote maintenance start:
ssh pat@server
hostname
whoami
uptime
df -h
systemctl --failedA safe config edit pattern:
sudo cp /etc/service/config.conf /etc/service/config.conf.backup
sudo nano /etc/service/config.conf
sudo service-name --test-config
sudo systemctl reload service-nameThe exact test command depends on the service.
Security notes for remote commands
Remote commands are powerful. They also carry risk.
Be especially careful with commands like:
ssh server "rm -rf /path"or:
rsync -av --delete source/ server:/important/path/Before destructive remote operations, run inspection commands.
ssh server "pwd; hostname; ls -lah /important/path"For rsync, run:
rsync -av --delete --dry-run source/ server:/important/path/For remote deletion, list first:
ssh server "find /path -type f -name '*.tmp' -mtime +30 -print"Then delete only if correct:
ssh server "find /path -type f -name '*.tmp' -mtime +30 -delete"A remote command should be treated with more caution than a local command because recovery may be harder.
Least privilege in practice
Least privilege means giving only the access required, not more.
Bad habit:
sudo chmod -R 777 /var/www/siteBetter:
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;Then grant write access only where needed.
sudo chown -R www-data:www-data /var/www/site/uploadsBad habit:
sudo chown -R www-data:www-data /var/www/siteThis may make the web server own everything, including code files. If the application is compromised, an attacker may be able to modify application code more easily.
A better model may be:
| Area | Owner | Permission idea |
|---|---|---|
| Application code | deploy:www-data | Web server can read, deploy user can write |
| Uploads | www-data:www-data or controlled group | Web server can write |
| Cache | www-data:www-data | Web server can write |
| Configuration secrets | restricted owner and group | Only required users can read |
| Scripts | deploy or root | Executable only where needed |
There is no universal permission model for every application, but the principle is universal: do not give write access where read access is enough.
Dangerous commands and why they are dangerous
Some commands are not bad, but they are dangerous when used without context.
| Command pattern | Risk |
|---|---|
rm -rf path | Deletes recursively without confirmation |
chmod -R 777 path | Gives everyone full access |
chown -R user:group path | Changes ownership for many files |
find / -exec rm {} \; | Can delete across the system |
rsync --delete | Removes destination files not in source |
dd if=... of=... | Can overwrite disks |
mkfs | Creates filesystem and destroys existing data |
kill -9 | Forces process termination without cleanup |
sudo command | Runs with elevated privileges |
> file | Overwrites file content |
truncate -s 0 file | Empties file content |
Before running dangerous commands, use a safety checklist.
hostname
whoami
pwd
ls -lahFor file deletion:
find /target/path -type f -name "*.tmp" -printThen:
find /target/path -type f -name "*.tmp" -deleteFor rsync --delete:
rsync -av --delete --dry-run source/ destination/Then:
rsync -av --delete source/ destination/For recursive ownership changes:
ls -ld /target/path
find /target/path -maxdepth 2 -exec ls -ld {} \; | headThen apply only if correct.
A dangerous command should never be the first command in a workflow.
Operational discipline: one change at a time
When fixing a Linux problem, make one meaningful change at a time.
Bad workflow:
sudo chmod -R 777 /var/www/site
sudo chown -R www-data:www-data /var/www/site
sudo systemctl restart nginx
sudo apt upgradeAfter this, if the problem is fixed or worse, you do not know which change mattered.
Better workflow:
systemctl status nginx
journalctl -u nginx -n 100
namei -l /var/www/site/index.htmlThen one change:
sudo chmod 755 /var/www/siteVerify:
curl -I http://localhostThen another change only if needed.
One change at a time makes troubleshooting learnable and reversible.
Operational discipline: save the exact command
When a command solves a problem, save it.
Create notes:
mkdir -p ~/ops/notes
nano ~/ops/notes/fixes.mdAdd:
Problem:
Nginx failed because port 80 was already used.
Diagnosis:
sudo ss -tulpn | grep ':80'
sudo lsof -i :80
Fix:
sudo systemctl stop old-service
sudo systemctl restart nginx
Verification:
curl -I http://localhost
systemctl status nginxThis creates institutional memory, even if you are the only administrator.
Search later:
grep -i "port 80" ~/ops/notes/fixes.mdLinux knowledge grows faster when commands are documented in context.
Operational discipline: build command templates
Many Linux tasks repeat with small changes. Templates reduce mistakes.
Find large files template:
sudo find /PATH -type f -size +SIZE -exec ls -lh {} \;Example:
sudo find /var/log -type f -size +100M -exec ls -lh {} \;Rsync preview template:
rsync -avz --delete --dry-run SOURCE/ USER@HOST:DESTINATION/Service diagnosis template:
systemctl status SERVICE
journalctl -u SERVICE -n 100
sudo ss -tulpn | grep PORTPermission diagnosis template:
whoami
id
ls -l FILE
namei -l FILEBackup template:
tar -czf NAME-$(date +%Y-%m-%d-%H-%M).tar.gz SOURCE
tar -tzf NAME-$(date +%Y-%m-%d-%H-%M).tar.gz | headTemplates are safer than rewriting complex commands from memory every time.
Navigation and inspection commands
Navigation and inspection commands answer the first operational questions: where am I, what is here and what does it look like?
pwd: know your current location
The command pwd means print working directory.
pwdExample output:
/home/pat/projectsThis tells you that your terminal is currently working inside /home/pat/projects.
The current directory matters because many commands operate relative to it.
If you type:
rm test.txtLinux looks for test.txt in the current directory.
If you type:
cp report.txt backup/Linux looks for report.txt and backup/ relative to where you are now.
This is why pwd should become automatic before file operations.
Use pwd before:
| Operation | Why |
|---|---|
| Removing files | To avoid deleting from the wrong directory |
| Moving files | To understand source and target |
| Copying files | To avoid copying wrong data |
| Running scripts | To know where relative paths resolve |
| Editing configuration | To avoid editing the wrong file |
| Creating archives | To avoid archiving the wrong folder |
Example:
pwd
ls -lah
rm -i old-notes.txtThis is much safer than running rm blindly.
cd: move through the filesystem
The command cd means change directory.
Go to a specific absolute path:
cd /home/pat/DocumentsGo to a directory relative to your current location:
cd DocumentsGo to your home directory:
cdor:
cd ~Go to the root directory:
cd /Go one level up:
cd ..Go two levels up:
cd ../..Return to the previous directory:
cd -This command is useful when switching between two directories.
Example:
cd /var/log
pwd
cd /etc
pwd
cd -
pwdThe last cd - returns you to /var/log.
Understanding cd .. is essential. If you are in:
/home/pat/projects/linux/scriptsand run:
cd ..you move to:
/home/pat/projects/linuxRun it again:
cd ..Now you are in:
/home/pat/projectsLinux path navigation is logical once you think in levels.
Common cd mistakes
The cd command is simple, but beginners often run into predictable problems.
| Mistake | Example | Why it fails | Correct approach |
|---|---|---|---|
| Wrong letter case | cd downloads | The folder is Downloads | cd Downloads |
| Spaces not quoted | cd My Folder | Shell sees two arguments | cd "My Folder" |
| Trying to enter a file | cd notes.txt | Files are not directories | Use cat, nano or less |
| Wrong current directory | cd project | project is not inside current directory | Use absolute path |
| Missing permissions | cd /root | Normal user cannot enter | Use appropriate privileges only when justified |
Handling spaces:
cd "My Folder"or:
cd My\ FolderThe backslash tells the shell that the space belongs to the folder name.
However, for command-line work, names without spaces are usually easier.
Better:
my-folder
my_folder
project-backupLess convenient:
My Folder
Project Backup Final
New Documents CopySpaces are allowed, but they require more care.
ls: list files and directories
The command ls lists directory contents.
Basic use:
lsList a specific directory:
ls /home/pat/DocumentsLong format:
ls -lShow hidden files:
ls -aHuman-readable sizes:
ls -hCommon combined form:
ls -lahThis is one of the most useful commands in Linux.
A typical ls -lah output may look like this:
drwxr-xr-x 5 pat pat 4.0K May 14 10:20 .
drwxr-xr-x 25 pat pat 4.0K May 14 09:55 ..
-rw-r--r-- 1 pat pat 1.2K May 14 10:18 notes.txt
-rwxr-xr-x 1 pat pat 842 May 14 10:19 backup.sh
drwxr-xr-x 2 pat pat 4.0K May 14 10:20 logsThis output contains a lot of information.
| Column | Example | Meaning |
|---|---|---|
| Type and permissions | -rw-r--r-- | File type and access rights |
| Link count | 1 | Number of hard links |
| Owner | pat | User owner |
| Group | pat | Group owner |
| Size | 1.2K | File size |
| Date and time | May 14 10:18 | Last modification time |
| Name | notes.txt | File or directory name |
The first character tells you the type.
| First character | Meaning |
|---|---|
- | Regular file |
d | Directory |
l | Symbolic link |
b | Block device |
c | Character device |
s | Socket |
p | Named pipe |
This means:
-rw-r--r--is a regular file.
drwxr-xr-xis a directory.
lrwxrwxrwxis a symbolic link.
Understanding the first column of ls -l is the beginning of understanding Linux permissions.
Hidden files in Linux
In Linux, hidden files are not hidden through a special property. They are hidden because their names begin with a dot.
Examples:
.bashrc
.profile
.ssh
.config
.cacheThe normal ls command does not show them.
lsTo show hidden files, use:
ls -aTo show hidden files with details and readable sizes:
ls -lahHidden files often store user configuration.
| Hidden file or directory | Common purpose |
|---|---|
.bashrc | Bash shell configuration |
.profile | Login shell environment settings |
.ssh | SSH keys and configuration |
.config | User application configuration |
.cache | User cache files |
.local | User-local applications and data |
Do not delete hidden files casually. Some of them are important for login, shell behavior, SSH access or application settings.
Practical ls patterns
The ls command becomes more useful when you learn common patterns.
| Command | Purpose |
|---|---|
ls | Simple list |
ls -l | Detailed list |
ls -a | Include hidden files |
ls -h | Human-readable sizes |
ls -lah | Detailed list with hidden files and readable sizes |
ls -lt | Sort by modification time, newest first |
ls -ltr | Sort by modification time, oldest first |
ls -R | List recursively |
ls *.txt | Show only files ending in .txt |
ls /var/log | List another directory |
ls -ld folder | Show information about the folder itself, not its contents |
The command:
ls -ld /var/logshows information about the directory /var/log itself.
The command:
ls -l /var/logshows what is inside /var/log.
This difference matters when checking permissions on a directory.
tree: see the structure clearly
The command tree displays a directory structure visually.
treeExample output:
.
├── documents
│ ├── report.txt
│ └── notes.txt
├── scripts
│ └── backup.sh
└── logs
└── app.logThis is much easier to understand than a flat list when working with projects.
Show a specific directory:
tree /home/pat/projectsLimit depth:
tree -L 2Show only directories:
tree -dShow hidden files too:
tree -aShow human-readable sizes:
tree -hCombine options:
tree -a -h -L 2If tree is not installed:
sudo apt update
sudo apt install treeA useful alternative using find:
find . -maxdepth 2This lists files and directories up to two levels below the current directory.
Creating, reading, copying, moving and deleting files
Most real Linux work begins with files. Creating, reading, editing, copying, moving, linking and deleting data should be done with enough context to avoid accidental damage.
mkdir: create directories
The command mkdir creates directories.
mkdir testThis creates a directory named test in the current location.
Create a directory using an absolute path:
mkdir /home/pat/testCreate multiple directories:
mkdir documents scripts backupsCreate nested directories:
mkdir -p projects/linux/scriptsThe -p option means create parent directories if they do not exist.
Without -p, this can fail:
mkdir projects/linux/scriptsif projects or projects/linux does not already exist.
With -p, Linux creates the full structure.
mkdir -p projects/linux/scriptsThis is especially useful in scripts because it avoids errors when parent directories are missing.
Common mkdir examples:
| Command | Meaning |
|---|---|
mkdir logs | Create logs in current directory |
mkdir ~/backups | Create backups in home directory |
mkdir -p ~/projects/app/logs | Create nested directory structure |
mkdir dir1 dir2 dir3 | Create three directories |
mkdir -m 755 public | Create directory with specific permissions |
The command mkdir -m sets permissions during creation.
mkdir -m 700 privateThis creates a directory accessible only to the owner.
touch: create empty files and update timestamps
The command touch creates an empty file if the file does not exist.
touch notes.txtIf the file already exists, touch updates its modification timestamp.
Create multiple files:
touch file1.txt file2.txt file3.txtCreate a file in another directory:
touch /home/pat/Documents/notes.txtWhy use touch?
| Use case | Explanation |
|---|---|
| Create an empty placeholder file | Useful before adding content |
| Test write permissions | If touch fails, you may not have write access |
| Update modification time | Useful in scripts and build systems |
| Prepare a log file | A script can later append to it |
| Create files quickly | Faster than opening an editor |
Example permission test:
touch /var/log/test-fileAs a normal user, this will likely fail.
touch: cannot touch '/var/log/test-file': Permission deniedThat tells you the directory is protected.
Inside your home directory, this should work:
touch ~/test-filenano: edit text files from the terminal
The command nano opens a simple terminal text editor.
nano notes.txtIf notes.txt exists, nano opens it. If it does not exist, nano creates it when you save.
Open a file in another directory:
nano /home/pat/Documents/notes.txtEdit a system file with administrative privileges:
sudo nano /etc/hostsImportant nano shortcuts:
| Shortcut | Meaning |
|---|---|
Ctrl + O | Save file |
Enter | Confirm filename after saving |
Ctrl + X | Exit |
Ctrl + W | Search |
Ctrl + K | Cut current line |
Ctrl + U | Paste |
Ctrl + C | Show cursor position |
Ctrl + G | Open help |
A safe configuration editing workflow:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sudo nano /etc/ssh/sshd_configThis creates a backup before editing.
That habit is extremely important. Many Linux problems are caused by editing configuration files without keeping a known-good copy.
A practical rule: before editing an important file under /etc, make a backup first.
cat: display and concatenate files
The command cat displays file content.
cat notes.txtIt can also concatenate multiple files.
cat part1.txt part2.txt part3.txtSave combined output to a new file:
cat part1.txt part2.txt part3.txt > complete.txtDisplay all text files matching a pattern:
cat *.txtThe * character is a wildcard. It means any characters.
| Pattern | Meaning |
|---|---|
*.txt | All files ending with .txt |
log-* | All files beginning with log- |
file?.txt | Files like file1.txt, file2.txt, but not file10.txt |
Use cat for short files. Do not use it blindly on huge logs because it can flood your terminal.
Good uses of cat:
| Situation | Example |
|---|---|
| Display a short note | cat notes.txt |
| Check a small config file | cat app.conf |
| Combine files | cat a.txt b.txt > c.txt |
| Send content into a pipeline | `cat file.txt |
Although this works:
cat app.log | grep errorthis is usually simpler:
grep error app.logStill, cat remains useful because it is easy to understand and often appears in examples and scripts.
more and less: read long files safely
The command more displays long files page by page.
more long-file.txtBasic controls:
| Key | Action |
|---|---|
| Space | Next page |
| Enter | Next line |
q | Quit |
A more powerful alternative is less.
less long-file.txtDespite the name, less is more capable than more.
Useful controls inside less:
| Key | Action |
|---|---|
| Space | Next page |
b | Previous page |
| Up arrow | Scroll up |
| Down arrow | Scroll down |
/word | Search for word |
n | Next search match |
N | Previous search match |
g | Go to beginning |
G | Go to end |
q | Quit |
Use less for logs, configuration files and command output that is too long for one screen.
Example:
less /var/log/syslogSearch inside it:
/errorThen press n to jump to the next match.
head: inspect the beginning of a file
The command head shows the first lines of a file.
head file.txtBy default, it shows the first 10 lines.
Show the first 20 lines:
head -n 20 file.txtor:
head -20 file.txthead is useful when you need to understand the structure of a file quickly.
Examples:
head /etc/passwdhead -n 5 data.csvhead -n 30 app.logCommon uses:
| File type | Why head helps |
|---|---|
| CSV files | See column names and format |
| Logs | See initial format and source |
| Scripts | Read interpreter line and comments |
| Configuration files | Read introductory comments |
| Data exports | Confirm structure before processing |
For example, before processing a CSV file with awk or cut, use:
head -n 3 data.csvThis helps you avoid wrong assumptions about delimiters and columns.
tail: inspect the end of a file
The command tail shows the last lines of a file.
tail file.txtBy default, it shows the last 10 lines.
Show the last 50 lines:
tail -n 50 file.txtFollow a file in real time:
tail -f file.txtThis is one of the most important commands for Linux troubleshooting.
For example:
sudo tail -f /var/log/syslogThis shows new system log entries as they are written.
Another example:
sudo tail -f /var/log/auth.logThis can show authentication-related events on many Debian-based systems.
A practical service debugging workflow:
sudo systemctl restart apache2
sudo tail -f /var/log/apache2/error.logThis restarts Apache and then watches the error log.
Stop tail -f with:
Ctrl + CUseful tail patterns:
| Command | Meaning |
|---|---|
tail file.txt | Show last 10 lines |
tail -n 100 file.txt | Show last 100 lines |
tail -f file.txt | Follow new lines live |
tail -n 50 -f file.txt | Show last 50 lines and continue following |
cp: copy files and directories
The command cp copies files.
Basic copy:
cp source.txt target.txtCopy a file into a directory:
cp report.txt /home/pat/Documents/Copy and rename at the same time:
cp report.txt /home/pat/Documents/final-report.txtCopy multiple files into a directory:
cp file1.txt file2.txt file3.txt /home/pat/Documents/Copy a directory recursively:
cp -r project project-backupThe -r option means recursive. It copies a directory and everything inside it.
Important cp options:
| Option | Meaning |
|---|---|
-r | Copy directories recursively |
-i | Ask before overwriting |
-v | Show what is being copied |
-u | Copy only when the source is newer |
-a | Archive mode, preserving structure and metadata |
Archive mode is often better for backups.
cp -a website website-backupThis preserves permissions, timestamps and symbolic links more carefully than a simple recursive copy.
A safe copy command:
cp -av project project-backupThis copies in archive mode and shows what is happening.
mv: move and rename files
The command mv moves or renames files and directories.
Rename a file:
mv oldname.txt newname.txtMove a file:
mv report.txt /home/pat/Documents/Move and rename at the same time:
mv report.txt /home/pat/Documents/final-report.txtRename a directory:
mv old-project new-projectMove a directory:
mv project /home/pat/backups/Important options:
| Option | Meaning |
|---|---|
-i | Ask before overwriting |
-v | Show what is being moved |
-n | Do not overwrite existing files |
Safer move:
mv -iv draft.txt final.txtUnlike cp, mv does not require -r for directories.
A practical difference:
| Command | What happens |
|---|---|
cp file backup/ | Original file remains |
mv file backup/ | Original file is moved |
mv old new | File or directory is renamed |
Be careful when moving files across filesystems. Moving inside the same filesystem is usually very fast because Linux changes directory references. Moving across disks may require copying data and deleting the original.
rm: remove files and directories
The command rm removes files.
rm file.txtRemove multiple files:
rm file1.txt file2.txt file3.txtAsk before removing:
rm -i file.txtRemove a directory recursively:
rm -r folderForce deletion:
rm -f file.txtRecursive forced deletion:
rm -rf folderThis is powerful and dangerous.
| Option | Meaning |
|---|---|
-r | Recursive, remove directories and their contents |
-f | Force, do not ask |
-i | Ask before every removal |
-I | Ask once before removing many files |
A safer beginner command is:
rm -ri folderThis removes recursively but asks for confirmation.
Before deleting a directory, inspect it:
pwd
ls -lah
du -sh folder
tree -L 2 folderThen remove:
rm -ri folderDo not run destructive commands copied from the internet unless you understand every part.
Especially dangerous patterns include:
rm -rf /
rm -rf *
rm -rf ./*
sudo rm -rf /path
find / -name "*.log" -exec rm {} \;The command rm -rf * removes everything matching * in the current directory. If you are in the wrong place, the result can be disastrous.
A safer habit is to use ls first with the same pattern.
ls *.log
rm -i *.logrmdir: remove empty directories
The command rmdir removes empty directories.
rmdir empty-folderIf the directory contains files, rmdir refuses to remove it.
rmdir: failed to remove 'folder': Directory not emptyThis makes rmdir safer than rm -r, but less flexible.
Use rmdir when you specifically expect a directory to be empty. Use rm -r only when you intend to remove contents too.
Practical workflow: create, inspect, copy, move and remove files
The following workflow demonstrates basic file operations safely.
Create a practice directory:
mkdir -p ~/linux-practice
cd ~/linux-practice
pwdCreate files:
touch notes.txt tasks.txt log.txt
ls -lahWrite text into a file using redirection:
echo "Linux practice notes" > notes.txt
echo "First task" > tasks.txt
echo "Application started" > log.txtRead files:
cat notes.txt
cat tasks.txt
cat log.txtCopy a file:
cp notes.txt notes-copy.txt
ls -lahRename a file:
mv notes-copy.txt archived-notes.txt
ls -lahCreate a subdirectory:
mkdir archiveMove the archived file:
mv archived-notes.txt archive/
treeSearch text:
grep "Linux" notes.txtRemove safely:
rm -i tasks.txt
rm -ri archiveThis small exercise covers mkdir, cd, pwd, touch, ls, echo, redirection, cat, cp, mv, tree, grep and rm.
It is simple, but it mirrors real Linux work.
Practical workflow: safely clean a temporary directory
Temporary directories can grow. But cleaning them blindly can break running programs.
Inspect first:
du -sh /tmp
sudo find /tmp -type f -mtime +7 -exec ls -lh {} \;If the list looks safe, remove files older than 7 days:
sudo find /tmp -type f -mtime +7 -deleteRemove empty directories:
sudo find /tmp -type d -empty -deleteThis is more controlled than:
sudo rm -rf /tmp/*The second command may remove files currently needed by applications.
A precise cleanup is better than a dramatic cleanup.
Practical workflow: use diff to compare files
The command diff compares files.
diff old.conf new.confUnified format:
diff -u old.conf new.confThis is useful after configuration changes.
Workflow:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.before
sudo nano /etc/nginx/nginx.conf
diff -u /etc/nginx/nginx.conf.before /etc/nginx/nginx.confThe output shows what changed.
This is a professional habit because it prevents accidental edits from going unnoticed.
File timestamps help reconstruct what happened
Linux files have timestamps. The most commonly visible one in ls -l is modification time.
ls -l file.txtShow detailed timestamps with stat:
stat file.txtExample fields:
| Field | Meaning |
|---|---|
| Access | Last time file was read |
| Modify | Last time file content changed |
| Change | Last time metadata changed |
| Birth | Creation time, if supported by filesystem |
Modification time changes when file content changes.
Change time changes when metadata changes, such as permissions or ownership.
This is useful in investigations.
Find recently modified files:
find /var/www/site -type f -mtime -1Find files modified in the last 30 minutes:
find /var/www/site -type f -mmin -30Sort files by modification time:
ls -ltOldest last or reversed:
ls -ltrThis helps answer: what changed recently?
stat gives exact file metadata
The command stat displays detailed file information.
stat file.txtExample output includes size, blocks, permissions, UID, GID and timestamps.
Use it when ls -l is not enough.
Example:
stat /var/www/site/index.phpThis can help compare files before and after deployment.
You can format output:
stat -c "%n %U %G %a %s" file.txtThis prints:
| Format | Meaning |
|---|---|
%n | File name |
%U | Owner username |
%G | Group name |
%a | Permissions in numeric form |
%s | Size in bytes |
Example output:
file.txt pat pat 644 1200This can be useful in scripts.
ln and symbolic links
A symbolic link is a file that points to another file or directory.
Create symbolic link:
ln -s /real/path/file.txt link-nameExample:
ln -s /var/www/releases/2026-05-14 /var/www/currentNow /var/www/current points to the release directory.
Show link:
ls -l /var/www/currentOutput may look like:
current -> /var/www/releases/2026-05-14Symbolic links are common in deployments, configuration management and shared paths.
Important distinction:
| Link type | Command | Meaning |
|---|---|---|
| Symbolic link | ln -s target link | Points to a path |
| Hard link | ln target link | Points to the same inode |
Symbolic links are easier to understand and more common for directories and deployments.
If a symbolic link breaks, it points to a target that no longer exists.
Check target:
readlink link-nameResolve full path:
readlink -f link-nameFind symbolic links:
find . -type lFind broken symbolic links:
find . -xtype lPractical workflow: generate files from scripts
Create a simple configuration file:
cat > app.conf << 'EOF'
APP_ENV=production
APP_DEBUG=false
APP_PORT=8080
EOFCreate a protected file with sudo:
sudo tee /etc/myapp.conf > /dev/null << 'EOF'
APP_ENV=production
APP_DEBUG=false
APP_PORT=8080
EOFAppend a block:
cat >> notes.txt << 'EOF'
New section
More notes
EOFThis is useful for automation, but be careful. > overwrites. >> appends.
| Operator | Effect |
|---|---|
> | Create or overwrite |
>> | Append |
< | Read from file |
<< EOF | Here-document input |
Before overwriting an important file, back it up.
Safe deletion with trash alternatives
The terminal rm command usually does not move files to a graphical trash bin. It deletes directory entries directly.
For safer interactive work, use:
rm -i file.txtFor recursive interactive deletion:
rm -ri directoryFor a safer manual approach, move files to a temporary review directory first.
mkdir -p ~/delete-review
mv old-files ~/delete-review/Inspect later:
ls -lah ~/delete-reviewDelete only after review:
rm -ri ~/delete-review/old-filesThis is useful when you are not completely sure.
For servers, deletion safety comes from backups, review commands and precise paths, not from a trash feature.
Practical workflow: cleanup with review directory
Suppose you want to remove old generated files from a project.
Create review folder:
mkdir -p ~/cleanup-reviewFind files:
find /var/www/site/cache -type f -mtime +30 -exec ls -lh {} \;Move instead of delete:
find /var/www/site/cache -type f -mtime +30 -exec mv {} ~/cleanup-review/ \;Check application behavior. Check disk:
df -h
du -sh ~/cleanup-reviewIf everything is fine, delete later:
rm -ri ~/cleanup-reviewThis approach is slower but safer when you are uncertain.
For huge numbers of files, moving all into one directory may cause name collisions or performance issues. In that case, use a structured cleanup or direct deletion after proper backups. But the review-directory concept is useful for small and medium cleanup tasks.
Understanding diff and patch
The command diff shows differences between files.
diff -u old.conf new.confThe -u option produces unified diff format, widely used in development and administration.
Save patch:
diff -u old.conf new.conf > change.patchApply patch:
patch old.conf < change.patchIn system administration, you may not use patch every day, but diff -u is extremely useful for reviewing configuration changes.
Example:
sudo cp /etc/ssh/sshd_config /tmp/sshd_config.before
sudo nano /etc/ssh/sshd_config
diff -u /tmp/sshd_config.before /etc/ssh/sshd_configThis shows exactly what changed before you reload SSH.
Practical workflow: review configuration changes before reload
Backup:
sudo cp /etc/nginx/nginx.conf /tmp/nginx.conf.beforeEdit:
sudo nano /etc/nginx/nginx.confReview:
diff -u /tmp/nginx.conf.before /etc/nginx/nginx.confTest:
sudo nginx -tReload:
sudo systemctl reload nginxVerify:
systemctl status nginx
journalctl -u nginx -n 50This workflow is simple and professional.
Searching files, filtering text and processing output
Searching and text processing turn the terminal into an analytical tool. These commands find files, filter logs and extract meaning from structured text.
grep: search text with precision
The command grep searches for text patterns in files or command output.
Basic search:
grep "error" app.logThis displays every line in app.log that contains error.
Case-insensitive search:
grep -i "error" app.logShow line numbers:
grep -n "error" app.logSearch recursively in a directory:
grep -r "database" /var/www/htmlIgnore case recursively and show line numbers:
grep -rin "database" /var/www/htmlInvert the search:
grep -v "Notice" app.logThis shows lines that do not contain Notice.
Count matching lines:
grep -c "error" app.logShow filenames containing matches:
grep -l "error" *.logShow context around matches:
grep -A 2 -B 4 "Fatal error" app.logThis shows two lines after and four lines before each matching line.
| Option | Meaning |
|---|---|
-i | Ignore case |
-n | Show line numbers |
-r | Recursive search |
-v | Invert match |
-c | Count matching lines |
-l | Show only filenames with matches |
-A | Show lines after match |
-B | Show lines before match |
-C | Show lines before and after match |
A powerful log example:
grep -i "failed password" /var/log/auth.logCount failed password entries:
grep -i "failed password" /var/log/auth.log | wc -lShow context:
grep -i -C 3 "failed password" /var/log/auth.logThis is how grep becomes a diagnostic tool.
Finding files is not only searching by name
One of the most important differences between a casual Linux user and a confident Linux user is the ability to find things precisely. On a graphical desktop, searching often means typing part of a filename into a search box. In the Linux command line, searching can be much more powerful. You can search by name, extension, type, size, modification time, ownership, permissions and even execute another command on every result.
The main command for this task is find.
find <path> <conditions> <actions>A simple example:
find /home/pat -iname "report.txt"This searches inside /home/pat for a file or directory named report.txt, ignoring uppercase and lowercase differences.
The structure is logical.
| Part | Meaning |
|---|---|
find | Run the search tool |
/home/pat | Start searching inside this path |
-iname | Match name case-insensitively |
"report.txt" | The name pattern to search for |
The command find is not only useful when you have lost a file. It is also useful when auditing systems, cleaning old logs, locating large files, finding broken project structures, checking modified files after deployment and preparing backups.
A graphical search tool usually answers the question: “Where is this file?”
The find command can answer deeper questions:
| Question | Example command |
|---|---|
| Where is this file? | find /home -iname "report.txt" |
| Where are all PHP files? | find /var/www -type f -iname "*.php" |
| Which files are larger than 100 MB? | find /home -type f -size +100M |
| Which files changed in the last two days? | find /home -type f -mtime -2 |
| Which empty directories exist? | find . -type d -empty |
| Which log files can be compressed? | find /var/log -type f -iname "*.log" -size +10M |
| Which files belong to a specific user? | find /var/www -user www-data |
The real value of find is not just that it searches. The real value is that it can search with conditions that matter to administrators.
Searching by filename with find
The most basic use of find is searching by filename.
find /home/pat -name "test.txt"This searches inside /home/pat for an exact name match.
The -name option is case-sensitive. That means test.txt, Test.txt and TEST.txt are different.
To ignore case, use -iname.
find /home/pat -iname "test.txt"This can match:
test.txt
Test.txt
TEST.txtFor beginners, -iname is often safer because real files are not always named consistently.
Search for all text files:
find /home/pat -iname "*.txt"Search for all PHP files:
find /var/www -iname "*.php"Search for all configuration files:
find /etc -iname "*.conf"The * symbol is a wildcard. It means any characters.
| Pattern | Meaning |
|---|---|
"*.txt" | Anything ending in .txt |
"log-*" | Anything starting with log- |
"*.conf" | Anything ending in .conf |
"backup*.tar.gz" | Anything starting with backup and ending in .tar.gz |
Always quote patterns like "*.txt" when using find.
This is better:
find . -iname "*.txt"This can behave unexpectedly:
find . -iname *.txtThe reason is that the shell may expand *.txt before find receives it. Quoting keeps the pattern intact.
Searching by file type
Linux directories can contain many kinds of filesystem objects: regular files, directories, symbolic links, devices, sockets and named pipes. When searching, you often want only one type.
Find only regular files:
find /home/pat -type fFind only directories:
find /home/pat -type dFind only symbolic links:
find /home/pat -type lCombine type and name:
find /var/www -type f -iname "*.php"This means: find only regular files under /var/www whose names end in .php.
Find directories named cache:
find /var/www -type d -iname "cache"Find symbolic links:
find /usr/local/bin -type lThe -type filter is important because names alone can be misleading. A directory can be named backup.txt. A file can be named logs. Search by type when the distinction matters.
| Type option | Meaning |
|---|---|
-type f | Regular file |
-type d | Directory |
-type l | Symbolic link |
-type b | Block device |
-type c | Character device |
-type s | Socket |
-type p | Named pipe |
For most everyday work, f, d and l are the most common.
Searching by size
Disk space problems are common on Linux servers. A log grows unexpectedly. A backup script creates repeated archives. A user uploads large files. A database dump remains forgotten in a project directory. The find command helps locate files by size.
Find files larger than 100 MB:
find /home -type f -size +100MFind files smaller than 1 KB:
find /home -type f -size -1kFind files exactly 10 MB in size is less common, but possible:
find /home -type f -size 10MThe symbols matter.
| Size expression | Meaning |
|---|---|
+100M | Larger than 100 MB |
-100M | Smaller than 100 MB |
100M | Exactly 100 MB unit match |
+1G | Larger than 1 GB |
+500k | Larger than 500 KB |
A practical command for finding large files:
find /home -type f -size +500M -exec ls -lh {} \;This finds files larger than 500 MB and displays them with readable sizes.
Explanation:
| Part | Meaning |
|---|---|
find /home | Search inside /home |
-type f | Only regular files |
-size +500M | Larger than 500 MB |
-exec ls -lh {} \; | Run ls -lh on each result |
A more advanced version:
find /home -type f -size +500M -printf "%s %p\n" | sort -nr | head -20This prints file sizes and paths, sorts them by size and shows the largest 20.
The lesson is important: when disk space is low, do not delete randomly. First locate the largest files.
Searching by modification time
Time-based searching is essential for administration. You may need to know which files changed recently, which logs are old, which backups should be removed or which files were modified after a deployment.
Find files modified in the last two days:
find /home -type f -mtime -2Find files modified more than 30 days ago:
find /var/log -type f -mtime +30Find files modified exactly around 7 days ago:
find /home -type f -mtime 7The signs matter.
| Expression | Meaning |
|---|---|
-mtime -2 | Modified less than 2 days ago |
-mtime +30 | Modified more than 30 days ago |
-mtime 7 | Modified 7 days ago according to find’s day rounding |
For minute-level precision, use -mmin.
Find files modified in the last 60 minutes:
find /home/pat -type f -mmin -60Find files modified more than 120 minutes ago:
find /tmp -type f -mmin +120This is very useful when troubleshooting.
Example: after updating a web application, find recently changed files:
find /var/www/html -type f -mmin -30Example: find old compressed logs:
find /var/log -type f -iname "*.gz" -mtime +30A safe cleanup workflow:
find /var/log -type f -iname "*.gz" -mtime +30Review the output first. Then, only when you are sure:
sudo find /var/log -type f -iname "*.gz" -mtime +30 -deleteThe order matters. Inspect first. Delete only after verification.
Running commands on find results
The find command becomes extremely powerful when combined with -exec. This allows you to run another command on every result.
Basic structure:
find <path> <conditions> -exec <command> {} \;The {} placeholder represents each found item.
Example:
find /var/log -iname "*.log.gz" -exec ls -lh {} \;This lists each matching compressed log file.
Remove matching files:
find /var/log -iname "*.log.gz" -exec rm {} \;This is powerful but dangerous.
A safer method is to print first:
find /var/log -iname "*.log.gz"Then list with details:
find /var/log -iname "*.log.gz" -exec ls -lh {} \;Then remove only if the result is correct:
sudo find /var/log -iname "*.log.gz" -exec rm {} \;Another useful example: change permissions on all .sh scripts.
find ~/scripts -type f -iname "*.sh" -exec chmod +x {} \;Compress old logs:
find /var/log/myapp -type f -iname "*.log" -mtime +7 -exec gzip {} \;Copy all PDF files into one folder:
find ~/Documents -type f -iname "*.pdf" -exec cp {} ~/pdf-collection/ \;The -exec feature can save hours, but it must be used carefully.
| Symbol | Meaning |
|---|---|
{} | Current found item |
\; | End of the command executed by find |
-exec | Run a command on each result |
A professional habit is to replace the action with echo first.
find /var/log -iname "*.gz" -mtime +30 -exec echo rm {} \;This prints what would be removed without actually removing it.
find with maxdepth and mindepth
By default, find searches recursively through all subdirectories. Sometimes this is too much.
Limit search to the current directory only:
find . -maxdepth 1 -type fSearch two levels deep:
find . -maxdepth 2 -type fIgnore the current directory itself and search below it:
find . -mindepth 1 -type dCombine both:
find . -mindepth 1 -maxdepth 2 -type dThis finds directories at depth one or two, but not the current directory itself.
This is useful when working with large projects or system folders where a full recursive search would produce too much output.
Example:
find /var/www -maxdepth 2 -type dThis shows the upper directory structure of web projects without descending into every subfolder.
Practical workflow: collect all files of one type
Suppose you want to collect all .pdf files from a large directory tree.
Create destination:
mkdir -p ~/pdf-collectionPreview files:
find ~/Documents -type f -iname "*.pdf"Copy files:
find ~/Documents -type f -iname "*.pdf" -exec cp {} ~/pdf-collection/ \;Check result:
ls -lah ~/pdf-collectionCount copied files:
find ~/pdf-collection -type f -iname "*.pdf" | wc -lThis workflow uses mkdir, find, cp, ls and wc together.
That is real command-line productivity.
Key takeaways
At this point, the command line becomes more than navigation and file reading. You now have the tools to search deeply, archive safely, analyze disk usage, mount filesystems and control permissions.
The most important commands in this section are:
| Command | Essential role |
|---|---|
find | Search by name, type, size, time and execute actions |
tar | Create and extract Linux archives |
zip | Create ZIP archives |
unzip | Extract ZIP archives |
df | Show free and used filesystem space |
du | Show file and directory disk usage |
mount | Attach a filesystem to the directory tree |
umount | Detach a mounted filesystem |
chmod | Change permissions |
chown | Change ownership |
date | Display and format date and time |
uptime | Show system runtime and load average |
The deeper lesson is this: Linux administration is a sequence of careful questions.
Where is the file? Use find.
How large is it? Use du or ls -lh.
Which filesystem is full? Use df.
Who owns it? Use ls -l.
Who can access it? Read the permissions.
Can it be executed? Check x.
Should it be archived? Use tar.
Should it be deleted? Inspect first, then act.
The more carefully you ask, the safer and more powerful your command line work becomes.
wc: counting lines, words, characters and bytes
The command wc means word count, but it can count more than words.
Basic use:
wc file.txtExample output:
20 150 900 file.txtThe columns usually mean:
| Column | Meaning |
|---|---|
| First | Lines |
| Second | Words |
| Third | Bytes |
| Last | Filename |
Count lines only:
wc -l file.txtCount words only:
wc -w file.txtCount characters:
wc -m file.txtCount bytes:
wc -c file.txtwc is especially useful after pipes.
Count matching lines:
grep -i "error" app.log | wc -lCount files found by find:
find /var/www -type f | wc -lCount logged-in users:
who | wc -lCount installed packages:
dpkg -l | wc -lThe command wc -l is one of the simplest ways to turn text output into a number.
cut: extracting columns from structured text
The command cut extracts parts of each line. It is useful when text is separated by delimiters such as colons, commas, tabs or spaces.
A classic example uses /etc/passwd.
cut -d : -f 1 /etc/passwdThis prints the first field of each line, using : as the delimiter.
Explanation:
| Part | Meaning |
|---|---|
cut | Run field extraction |
-d : | Use colon as delimiter |
-f 1 | Print first field |
/etc/passwd | Input file |
The file /etc/passwd contains user account records. The first field is the username.
Extract usernames and home directories:
cut -d : -f 1,6 /etc/passwdExtract only characters 1 to 10:
cut -c 1-10 file.txtExtract the second column from a CSV file:
cut -d ',' -f 2 data.csvCommon options:
| Option | Meaning |
|---|---|
-d | Delimiter |
-f | Field number |
-c | Character positions |
--complement | Select everything except specified fields |
Example:
cut -d ',' -f 1,3 data.csvThis prints fields 1 and 3 from a comma-separated file.
cut is simple and fast, but it is not a complete CSV parser. Real CSV files can contain quoted commas, line breaks inside fields and escaping rules. For simple delimiter-based text, cut is excellent. For complex CSV, use more suitable tools.
awk: a complete text processing language
The command awk is more powerful than cut. It can select fields, apply conditions, calculate values, format output and behave like a small programming language.
Basic structure:
awk 'pattern { action }' filePrint the first field:
awk '{print $1}' file.txtBy default, awk splits input on whitespace.
Use a colon as field separator:
awk -F ':' '{print $1}' /etc/passwdThis prints usernames, similar to:
cut -d : -f 1 /etc/passwdPrint username and home directory:
awk -F ':' '{print $1, $6}' /etc/passwdPrint with custom formatting:
awk -F ':' '{print "User:", $1, "Home:", $6}' /etc/passwdPrint lines where the third field is greater than or equal to 1000:
awk -F ':' '$3 >= 1000 {print $1, $3}' /etc/passwdThis is already more powerful than cut.
Important awk field variables:
| Variable | Meaning |
|---|---|
$0 | Entire line |
$1 | First field |
$2 | Second field |
$NF | Last field |
NF | Number of fields |
NR | Current line number |
Print line numbers:
awk '{print NR, $0}' file.txtPrint last field:
awk '{print $NF}' file.txtPrint only lines with more than 5 fields:
awk 'NF > 5 {print $0}' file.txtawk for log analysis
Suppose a web access log has lines where the first field is the client IP address. You can count requests by IP.
awk '{print $1}' access.log | sort | uniq -c | sort -nr | headThis means:
| Stage | Purpose |
|---|---|
awk '{print $1}' access.log | Extract IP addresses |
sort | Group identical IPs |
uniq -c | Count repeated IPs |
sort -nr | Sort by count descending |
head | Show top results |
Print the first and ninth fields from a web log:
awk '{print $1, $9}' access.logIf the ninth field is HTTP status code, this can show which IP produced which status.
Count status codes:
awk '{print $9}' access.log | sort | uniq -c | sort -nrFind lines with status 500:
awk '$9 == 500 {print $0}' access.logFind IPs causing 404 errors:
awk '$9 == 404 {print $1}' access.log | sort | uniq -c | sort -nr | headThese are real server investigation patterns. They show why text processing is central to Linux administration.
awk for calculations
awk can also calculate.
Suppose sizes.txt contains numbers:
10
25
30Sum them:
awk '{sum += $1} END {print sum}' sizes.txtOutput:
65Average:
awk '{sum += $1; count++} END {print sum / count}' sizes.txtPrint total size from a command:
du -sk * | awk '{sum += $1} END {print sum " KB"}'This is where awk becomes more than a column extractor. It becomes a compact data processing tool.
sed: editing text streams
The command sed is a stream editor. It reads text, applies editing rules and prints the result.
Basic substitution:
sed 's/old/new/' file.txtThis replaces the first occurrence of old with new on each line in the output. It does not modify the file unless you use in-place editing.
Replace all occurrences on each line:
sed 's/old/new/g' file.txtThe g means global within each line.
Delete lines beginning with #:
sed '/^#/d' config.confThis removes comment lines from the output.
Delete empty lines:
sed '/^$/d' file.txtPrint only a range of lines:
sed -n '10,20p' file.txtThis prints lines 10 through 20.
In-place editing:
sed -i 's/old/new/g' file.txtIn-place editing with backup:
sed -i.backup 's/old/new/g' file.txtThis creates file.txt.backup.
The backup form is much safer when learning.
sed examples for configuration cleanup
Show a configuration file without comments and empty lines:
sed '/^#/d; /^$/d' config.confThis applies two commands:
| sed rule | Meaning |
|---|---|
/^#/d | Delete lines beginning with # |
/^$/d | Delete empty lines |
This is useful when reading long configuration files.
Example:
sed '/^#/d; /^$/d' /etc/ssh/sshd_configBe careful: this prints a cleaned view. It does not show inline comments and it does not explain inherited defaults. Still, it can make large configuration files easier to inspect.
Replace a domain in a file:
sed -i.backup 's/old.example.com/new.example.com/g' config.confThen compare:
diff config.conf.backup config.confIf wrong, restore:
mv config.conf.backup config.confA professional habit with sed -i is to make a backup first or use the built-in backup extension.
sort and uniq: organizing and counting text
The command sort sorts lines.
sort file.txtReverse sort:
sort -r file.txtNumeric sort:
sort -n numbers.txtHuman-readable size sort:
sort -h sizes.txtSort by a specific column:
sort -k 2 file.txtThe command uniq removes adjacent duplicate lines.
uniq file.txtImportant detail: uniq only removes duplicates that are next to each other. This is why it is often used after sort.
sort file.txt | uniqCount duplicates:
sort file.txt | uniq -cSort counts descending:
sort file.txt | uniq -c | sort -nrThis pattern is extremely useful.
Count repeated errors:
grep -i "error" app.log | sort | uniq -c | sort -nrCount top IP addresses:
awk '{print $1}' access.log | sort | uniq -c | sort -nr | headCount status codes:
awk '{print $9}' access.log | sort | uniq -c | sort -nrThe combination sort | uniq -c | sort -nr is one of the most practical Linux text-analysis patterns.
head and tail in pipelines
The commands head and tail are not only for reading files. They also limit pipeline output.
Show top 10 largest files from a du pipeline:
du -ak | sort -nr | headShow top 20:
du -ak | sort -nr | head -20Show the last 20 commands from history:
history | tail -n 20Show the latest 50 matching log lines:
grep -i "error" app.log | tail -n 50Show the first 5 users from /etc/passwd:
cut -d : -f 1 /etc/passwd | head -5In real work, many commands produce too much output. head and tail help you inspect a manageable slice.
Practical workflow: analyze a large log file
Suppose you have a large application log named app.log.
Start by checking size:
ls -lh app.logInspect beginning:
head -n 20 app.logInspect end:
tail -n 50 app.logCount total lines:
wc -l app.logCount error lines:
grep -i "error" app.log | wc -lShow most common error lines:
grep -i "error" app.log | sort | uniq -c | sort -nr | head -20Save error summary:
grep -i "error" app.log | sort | uniq -c | sort -nr > error-summary.txtRead summary:
less error-summary.txtFollow new errors live:
tail -f app.log | grep -i "error"This workflow turns a large log into useful information.
Practical workflow: count web requests by IP address
Assume access.log is a web access log where the first field is the client IP.
Top IP addresses:
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -20Count total requests:
wc -l access.logCount unique IP addresses:
awk '{print $1}' access.log | sort -u | wc -lSave top IP report:
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -50 > top-ips.txtThis is useful for traffic analysis, abuse detection, debugging and understanding server load.
Practical workflow: count HTTP status codes
If the ninth field in an access log is the HTTP status code, count status codes:
awk '{print $9}' access.log | sort | uniq -c | sort -nrFind 500 errors:
awk '$9 == 500 {print $0}' access.logFind 404 errors:
awk '$9 == 404 {print $0}' access.logTop IPs causing 404 errors:
awk '$9 == 404 {print $1}' access.log | sort | uniq -c | sort -nr | head -20Save 500 errors:
awk '$9 == 500 {print $0}' access.log > http-500-errors.logA few commands can answer questions that would be tedious in a graphical editor.
Practical workflow: prepare a clean configuration view
Many configuration files contain comments and blank lines. To read active lines more easily:
sed '/^#/d; /^$/d' config.confFor SSH configuration:
sudo sed '/^#/d; /^$/d' /etc/ssh/sshd_configFor Apache configuration:
sed '/^#/d; /^$/d' /etc/apache2/apache2.confThis does not edit the file. It prints a cleaned view.
Save cleaned view:
sed '/^#/d; /^$/d' config.conf > active-config-view.txtThis is useful for review, documentation or troubleshooting.
Practical workflow: batch rename with shell tools
Suppose you have files ending in .jpeg and want to rename them to .jpg.
Preview:
ls *.jpegA simple loop:
for file in *.jpeg; do
mv "$file" "${file%.jpeg}.jpg"
doneExplanation:
| Part | Meaning |
|---|---|
for file in *.jpeg | Loop through .jpeg files |
"${file%.jpeg}" | Remove .jpeg from the end |
.jpg | Add new extension |
mv | Rename file |
Safer preview:
for file in *.jpeg; do
echo mv "$file" "${file%.jpeg}.jpg"
doneThis prints the planned commands without running them.
Only after review, run the real loop.
This preview-first method is important for batch operations.
Practical workflow: find and process files safely
Find all .log files:
find . -type f -name "*.log"Show sizes:
find . -type f -name "*.log" -exec ls -lh {} \;Count them:
find . -type f -name "*.log" | wc -lCompress logs older than 7 days:
find . -type f -name "*.log" -mtime +7 -exec gzip {} \;Preview deletion of compressed logs older than 30 days:
find . -type f -name "*.gz" -mtime +30 -exec echo rm {} \;Delete after verifying:
find . -type f -name "*.gz" -mtime +30 -deleteThe important pattern is: find, inspect, count, preview, act.
Practical workflow: use grep, awk and cut together
Extract usernames from /etc/passwd:
cut -d : -f 1 /etc/passwdShow users with UID 1000 or higher:
awk -F ':' '$3 >= 1000 {print $1, $3, $6}' /etc/passwdSearch for a specific shell:
grep "/bin/bash" /etc/passwdExtract usernames using Bash shell:
grep "/bin/bash" /etc/passwd | cut -d : -f 1This demonstrates tool selection:
| Tool | Best use |
|---|---|
grep | Find lines |
cut | Extract simple fields |
awk | Apply conditions and format output |
sed | Edit or transform lines |
wc | Count results |
The tools overlap, but each has a natural strength.
Practical workflow: create a top error report from a web log
Assume access.log contains web requests.
Create report:
{
echo "Top client IPs"
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -20
echo
echo "HTTP status code counts"
awk '{print $9}' access.log | sort | uniq -c | sort -nr
echo
echo "Top 404 sources"
awk '$9 == 404 {print $1}' access.log | sort | uniq -c | sort -nr | head -20
echo
echo "Recent 500 errors"
awk '$9 == 500 {print $0}' access.log | tail -20
} > web-log-report.txtRead it:
less web-log-report.txtThis is a compact reporting workflow using grouping, awk, sort, uniq, head, tail and redirection.
Time based log investigation
When investigating an incident, time matters.
Show logs since a specific time:
journalctl --since "2026-05-14 09:00"Until a specific time:
journalctl --since "2026-05-14 09:00" --until "2026-05-14 10:00"For a service:
journalctl -u nginx --since "2026-05-14 09:00" --until "2026-05-14 10:00"For plain log files, use grep if timestamps are textual.
grep "2026-05-14 09:" app.logShow files modified during the period:
find /var/www/site -type f -newermt "2026-05-14 09:00" ! -newermt "2026-05-14 10:00" -exec ls -lh {} \;This command finds files modified between 09:00 and 10:00.
Time-based investigation is essential when a problem begins after a deployment, update, reboot or scheduled job.
Using find with newermt
The find command can search by human-readable modification time using -newermt.
Find files modified after a date:
find /var/www/site -type f -newermt "2026-05-14" -exec ls -lh {} \;Find files modified after a specific time:
find /var/www/site -type f -newermt "2026-05-14 10:00" -exec ls -lh {} \;Find files modified in a range:
find /var/www/site -type f -newermt "2026-05-14 10:00" ! -newermt "2026-05-14 11:00" -exec ls -lh {} \;This is extremely useful after deployment incidents.
Example workflow:
find /etc -type f -newermt "2026-05-14 09:00" -exec ls -lh {} \;
find /var/www -type f -newermt "2026-05-14 09:00" -exec ls -lh {} \;
journalctl --since "2026-05-14 09:00"This connects file changes and logs in the same time window.
Pipes, redirection and command composition
Pipes, redirection and shell interpretation explain why small commands can become complete workflows.
Understanding pipes
The pipe symbol | sends the output of one command into another command.
Example:
ls -lah | grep ".log"This means:
| Step | Action |
|---|---|
ls -lah | List files with details |
| ` | ` |
grep ".log" | Show only lines containing .log |
Another example:
history | tail -n 20This shows only the last 20 commands from your command history.
A more advanced example:
du -ak | sort -nr | head -20This shows large files or directories.
| Command part | Purpose |
|---|---|
du -ak | Show disk usage in kilobytes |
sort -nr | Sort numerically in reverse order |
head -20 | Show only first 20 lines |
Pipes are one of the most important concepts in Linux. They allow simple tools to become powerful workflows.
A command pipeline should be read from left to right. Each command transforms the previous output.
Output redirection
Redirection sends command output into files instead of showing it only on the screen.
Overwrite or create a file:
command > file.txtAppend to a file:
command >> file.txtExample:
grep "error" app.log > errors.txtThis creates errors.txt with all matching lines. If the file already exists, it is overwritten.
Append instead:
grep "error" app.log >> errors.txtThis adds matching lines to the end of the file.
| Symbol | Meaning | Risk |
|---|---|---|
> | Write output and overwrite file | Can erase previous content |
>> | Append output to file | Safer for accumulating logs |
2> | Redirect error output | Useful for separating errors |
&> | Redirect normal output and errors | Useful for full command logs |
Example separating successful output from errors:
find /etc -name "*.conf" > found.txt 2> denied.txtThis saves found files into found.txt and permission errors into denied.txt.
Example saving both output and errors:
find /etc -name "*.conf" &> result.txtRedirection is essential for logging, scripting and automation.
The shell is more than a place where commands are typed
The Linux shell is not only a command launcher. It is an environment that can remember commands, expand patterns, redirect output, connect programs, store variables, repeat work, run scripts and automate system behavior.
This is where Linux begins to feel different from a normal graphical operating system. In a graphical interface, you usually perform one action at a time. In the shell, you can describe a whole workflow as text. That workflow can be repeated, saved, scheduled, shared, inspected and improved.
A command such as this is useful:
grep -i "error" app.logBut the shell becomes much more powerful when commands are combined:
grep -i "error" app.log | sort | uniq -c | sort -nr > error-summary.txtThis single line searches for errors, sorts them, counts repeated lines, sorts by frequency and saves the result into a file.
That is not just command usage. That is text-based automation.
The shell gives you several major powers.
| Shell capability | What it means | Example |
|---|---|---|
| Command history | Reuse previous commands | history, !!, !123 |
| Expansion | Convert patterns into filenames or values | *.log, {a,b}, $HOME |
| Pipes | Send output from one command to another | `ps aux |
| Redirection | Save output or errors to files | command > output.txt |
| Variables | Store values temporarily | NAME="backup" |
| Substitution | Use output of one command inside another | $(date +%Y-%m-%d) |
| Job control | Move work between foreground and background | jobs, fg, bg |
| Scripts | Save commands into executable files | backup.sh |
| Scheduling | Run commands automatically later | crontab |
A beginner sees the shell as a blank screen. An experienced Linux user sees it as a programmable control surface.
Shell expansion explains many surprising behaviors
The shell often modifies your command before the command itself runs. This process is called expansion.
For example:
ls *.txtThe ls command may never receive the literal text *.txt. The shell first expands *.txt into matching filenames.
If the directory contains:
notes.txt
report.txt
tasks.txtthen the command becomes:
ls notes.txt report.txt tasks.txtThis is why wildcard behavior can surprise beginners.
Common shell patterns:
| Pattern | Meaning |
|---|---|
* | Any number of characters |
? | Exactly one character |
[abc] | One character from the set |
[0-9] | One digit |
*.log | Files ending in .log |
file?.txt | file1.txt, fileA.txt, but not file10.txt |
backup-* | Names beginning with backup- |
Examples:
ls *.logls file?.txtls backup-*Before removing files with a wildcard, always preview.
Do not start with:
rm *.logStart with:
ls *.logThen use safer deletion:
rm -i *.logWildcards are powerful, but they expand before the command runs. That means a mistake in the pattern can affect many files.
Quoting changes how the shell interprets text
Quotes control expansion and word splitting.
Without quotes:
echo Hello WorldThe shell sees two separate words: Hello and World.
With quotes:
echo "Hello World"The shell treats the text as one argument.
This matters with filenames that contain spaces.
Wrong:
cd My FolderThe shell sees two arguments: My and Folder.
Correct:
cd "My Folder"or:
cd My\ FolderDouble quotes allow variable expansion.
NAME="Linux"
echo "Hello $NAME"Output:
Hello LinuxSingle quotes prevent variable expansion.
NAME="Linux"
echo 'Hello $NAME'Output:
Hello $NAMEComparison:
| Form | Behavior |
|---|---|
"text $VAR" | Keeps text together and expands variables |
'text $VAR' | Keeps text together and does not expand variables |
text\ with\ spaces | Escapes individual spaces |
| Unquoted text | Subject to splitting and expansion |
A strong shell habit is to quote variables.
Better:
cp "$SOURCE" "$DESTINATION"Riskier:
cp $SOURCE $DESTINATIONIf a variable contains spaces, the unquoted version can break.
Variables make commands reusable
A shell variable stores a value.
PROJECT="website"Use it with $.
echo "$PROJECT"Output:
websiteA practical example:
SOURCE="/var/www/site"
DESTINATION="/home/pat/backups"
DATE=$(date +%Y-%m-%d)Now create a backup:
tar -czf "$DESTINATION/site-$DATE.tar.gz" "$SOURCE"This is easier to read and safer to modify than a long command with repeated paths.
Variables are especially useful in scripts.
#!/bin/bash
SOURCE="/var/www/site"
DESTINATION="/home/pat/backups"
DATE=$(date +%Y-%m-%d)
ARCHIVE="$DESTINATION/site-$DATE.tar.gz"
mkdir -p "$DESTINATION"
tar -czf "$ARCHIVE" "$SOURCE"
echo "Backup created: $ARCHIVE"This script is simple, but it already shows real automation.
Important syntax detail: do not place spaces around = when assigning variables.
Correct:
NAME="Linux"Wrong:
NAME = "Linux"The second form does not assign a variable. The shell interprets it differently.
Command substitution uses command output inside another command
Command substitution runs a command and inserts its output into another command.
Modern syntax:
$(command)Example:
echo "Today is $(date +%Y-%m-%d)"Output:
Today is 2026-05-14Create a dated backup:
tar -czf "project-$(date +%Y-%m-%d).tar.gz" project/Create a log line with timestamp:
echo "$(date +"%Y-%m-%d %H:%M:%S") backup started" >> backup.logStore command output in a variable:
DATE=$(date +%Y-%m-%d)
HOST=$(hostname)Use them:
echo "Running on $HOST at $DATE"Command substitution is one of the bridges between one-time commands and shell scripts.
Pipes turn commands into workflows
A pipe sends the output of one command into the input of another command.
command1 | command2Example:
ps aux | grep nginxThis runs ps aux, then filters the output through grep nginx.
Another example:
cat app.log | grep -i errorThis works, but the shorter form is usually better:
grep -i error app.logPipes become valuable when several tools are connected.
grep -i "error" app.log | sort | uniq -c | sort -nrThis means:
| Stage | Command | Purpose |
|---|---|---|
| 1 | grep -i "error" app.log | Find error lines |
| 2 | sort | Sort identical lines together |
| 3 | uniq -c | Count repeated lines |
| 4 | sort -nr | Sort counts from highest to lowest |
A disk usage pipeline:
du -ak | sort -nr | head -20This means:
| Stage | Command | Purpose |
|---|---|---|
| 1 | du -ak | Show disk usage in kilobytes |
| 2 | sort -nr | Sort largest first |
| 3 | head -20 | Show top 20 results |
A process pipeline:
ps aux | sort -nrk 3 | head -10This sorts processes by CPU usage and shows the top 10.
The pipe is central to Linux because it lets each command remain simple while allowing the workflow to be complex.
Redirection controls where output goes
Most commands have output. By default, normal output appears on the terminal. Errors also appear on the terminal, but technically they use a different stream.
Linux commonly uses three standard streams.
| Stream | Number | Meaning |
|---|---|---|
| Standard input | 0 | Input into a command |
| Standard output | 1 | Normal output |
| Standard error | 2 | Error output |
Redirect standard output into a file:
command > output.txtAppend standard output to a file:
command >> output.txtRedirect errors:
command 2> errors.txtRedirect output and errors to separate files:
command > output.txt 2> errors.txtRedirect both output and errors to one file:
command > all.txt 2>&1Modern shorthand in many shells:
command &> all.txtExample:
find /etc -name "*.conf" > found.txt 2> denied.txtThis stores successful results in found.txt and permission errors in denied.txt.
Append errors to a log file:
command 2>> errors.logDiscard output:
command > /dev/nullDiscard errors:
command 2> /dev/nullDiscard everything:
command > /dev/null 2>&1The special file /dev/null is a data sink. Anything written to it disappears.
Use /dev/null carefully. It is useful in scripts, but hiding errors can make troubleshooting harder.
Redirection and sudo can be confusing
A common beginner problem appears when writing to protected files.
This often fails:
sudo echo "test" > /etc/example.confWhy? Because sudo applies to echo, but the redirection > is handled by your current shell before sudo writes the file. The shell itself may not have permission to write into /etc.
Use tee instead:
echo "test" | sudo tee /etc/example.confAppend with tee -a:
echo "another line" | sudo tee -a /etc/example.confHide tee output if needed:
echo "test" | sudo tee /etc/example.conf > /dev/nullThis pattern is important for writing into protected files from command pipelines.
Examples:
echo "127.0.0.1 localtest" | sudo tee -a /etc/hostsprintf "Hello\nWorld\n" | sudo tee /opt/example.txtThe core lesson: sudo does not automatically apply to shell redirection.
xargs converts input into command arguments
The command xargs takes input lines and turns them into arguments for another command.
A simple example:
find . -name "*.tmp" | xargs rmThis finds .tmp files and passes them to rm.
However, this can break when filenames contain spaces or unusual characters. A safer version uses null-separated output.
find . -name "*.tmp" -print0 | xargs -0 rmExplanation:
| Part | Meaning |
|---|---|
find . -name "*.tmp" | Find matching files |
-print0 | Separate results with null characters |
xargs -0 | Read null-separated input safely |
rm | Remove files |
Preview before deleting:
find . -name "*.tmp" -print0 | xargs -0 ls -lhThen remove:
find . -name "*.tmp" -print0 | xargs -0 rmAnother example: count lines in all .log files.
find . -name "*.log" -print0 | xargs -0 wc -lThe command xargs is useful, but it should be handled carefully. For many tasks, find -exec is easier to read.
Compare:
find . -name "*.tmp" -exec rm {} \;and:
find . -name "*.tmp" -print0 | xargs -0 rmBoth can work. xargs may be more efficient for large numbers of files because it can pass many files to one command invocation.
tee: display output and save it at the same time
The command tee reads input and writes it both to the screen and to a file.
command | tee output.txtExample:
ls -lah | tee listing.txtThis displays the listing and saves it to listing.txt.
Append instead of overwrite:
command | tee -a output.txtUse with protected files:
echo "example" | sudo tee /etc/example.confUse in logs:
sudo apt update | tee apt-update.logSave both output and errors:
command 2>&1 | tee command.logExample:
sudo apt upgrade 2>&1 | tee upgrade.logThis is useful during maintenance because you can watch output and keep a record.
Understanding standard input in practical commands
Standard input is the data a command reads. Many commands can read from files or from standard input.
Example:
cat file.txtThe file is an argument.
But this uses standard input:
cat < file.txtPipes provide standard input:
cat file.txt | grep errorHere, grep reads from standard input.
Equivalent:
grep error file.txtSome commands are commonly used with standard input.
sort < names.txtor:
cat names.txt | sortA here-document sends standard input:
cat << 'EOF'
Line one
Line two
EOFUnderstanding input makes pipes and scripts easier to reason about.
Understanding shell globbing versus find
Globbing is expansion performed by the shell.
ls *.logThis matches files in the current directory.
find searches recursively and with conditions.
find . -type f -name "*.log"Comparison:
| Feature | Shell glob | find |
|---|---|---|
| Current directory matching | Excellent | Yes |
| Recursive search | Limited unless using shell options | Excellent |
| Search by size | No | Yes |
| Search by time | No | Yes |
| Search by owner | No | Yes |
| Execute action on results | Limited | Excellent |
| Handles huge trees | Shell may hit argument limits | Better |
Use globbing for simple current-directory tasks.
ls *.txt
rm -i *.tmpUse find for deeper controlled searches.
find /var/log -type f -name "*.gz" -mtime +30 -exec ls -lh {} \;Argument list too long
Sometimes shell expansion creates too many arguments.
Example:
rm *.logIf there are too many .log files, you may see:
Argument list too longThis happens because the shell expands *.log into a huge list before running rm.
Use find instead:
find . -type f -name "*.log" -deleteOr interactive:
find . -type f -name "*.log" -exec rm -i {} \;Or with xargs safely:
find . -type f -name "*.log" -print0 | xargs -0 rmThis is another reason find is essential for serious file operations.
Archives, compression and backup basics
Archives, compression and backups connect file operations with operational safety. A useful archive is not only created, but also inspected, moved, verified and restorable.
Archiving and compression in Linux
Linux systems often use archives for backups, transfers, deployments and log storage. The most important command is tar.
The name originally meant tape archive, but today it is used for packaging files and directories into archive files.
There is an important distinction:
| Concept | Meaning |
|---|---|
| Archiving | Combining multiple files into one file |
| Compression | Reducing the size of data |
| Archive file | One file containing many files |
| Compressed archive | An archive that is also compressed |
The command tar can create an archive and optionally compress it.
Common archive extensions:
| Extension | Meaning |
|---|---|
.tar | Tar archive, usually not compressed |
.tar.gz | Tar archive compressed with gzip |
.tgz | Short form of .tar.gz |
.tar.bz2 | Tar archive compressed with bzip2 |
.tar.xz | Tar archive compressed with xz |
.zip | ZIP archive, common on Windows |
A .tar.gz file is extremely common in Linux.
Creating tar archives
Create a gzip-compressed tar archive:
tar -czvf archive.tar.gz /home/pat/projectThis command creates archive.tar.gz from /home/pat/project.
The options can be understood like this:
| Option | Meaning |
|---|---|
-c | Create an archive |
-z | Compress with gzip |
-v | Verbose output |
-f | Use the following filename |
The -f option must be followed by the archive filename.
This is correct:
tar -czvf project-backup.tar.gz project/This is wrong or confusing:
tar -czv project-backup.tar.gz project/Without -f, tar does not know that project-backup.tar.gz is the archive file name in the intended way.
A quieter version without verbose output:
tar -czf project-backup.tar.gz project/For scripts, less verbose output is often preferred.
Create an uncompressed tar archive:
tar -cf archive.tar project/Create a bzip2-compressed archive:
tar -cjvf archive.tar.bz2 project/Create an xz-compressed archive:
tar -cJvf archive.tar.xz project/| Compression | Option | Common extension | General note |
|---|---|---|---|
| gzip | -z | .tar.gz | Fast and widely used |
| bzip2 | -j | .tar.bz2 | Often smaller than gzip, slower |
| xz | -J | .tar.xz | Strong compression, often slower |
| none | no compression option | .tar | Archive only |
For most practical use, .tar.gz is a safe default.
Extracting tar archives
Extract a .tar.gz archive:
tar -xzvf archive.tar.gzOptions:
| Option | Meaning |
|---|---|
-x | Extract |
-z | Use gzip |
-v | Verbose output |
-f | Archive filename follows |
A quieter version:
tar -xzf archive.tar.gzExtract into a specific directory:
tar -xzf archive.tar.gz -C /home/pat/extractedThe destination directory must already exist.
mkdir -p /home/pat/extracted
tar -xzf archive.tar.gz -C /home/pat/extractedList archive contents without extracting:
tar -tzf archive.tar.gzThis is a very important safety command.
Before extracting an archive from an unknown source, inspect its structure:
tar -tzf archive.tar.gz | headWhy this matters:
Some archives contain a clean folder like this:
project/
project/index.php
project/assets/style.cssOthers may contain files directly:
index.php
style.css
config.phpIf you extract the second type in the wrong directory, it can scatter files everywhere.
A safe extraction workflow:
mkdir -p ~/extract-test
tar -tzf archive.tar.gz | head -50
tar -xzf archive.tar.gz -C ~/extract-testZIP and unzip
ZIP files are common when exchanging files with Windows users, clients, designers, office workers or web platforms.
Create a ZIP archive:
zip archive.zip file1.txt file2.txtCreate a ZIP archive from a directory:
zip -r archive.zip project/The -r option means recursive.
Extract a ZIP archive:
unzip archive.zipExtract into a specific directory:
unzip archive.zip -d extracted/List ZIP contents without extracting:
unzip -l archive.zipThis is the ZIP equivalent of inspecting before extracting.
Common ZIP workflow:
unzip -l website-files.zip
mkdir -p website-files
unzip website-files.zip -d website-filesComparison between tar.gz and ZIP:
| Feature | .tar.gz | .zip |
|---|---|---|
| Common on Linux | Very common | Common |
| Common on Windows | Less native historically | Very common |
| Preserves Unix permissions well | Yes | Less consistently |
| Good for Linux backups | Yes | Sometimes |
| Good for sharing with non-Linux users | Sometimes | Yes |
| Compression included | Yes with gzip | Yes |
For Linux system backups, prefer tar.gz. For general file sharing, ZIP is often more convenient.
Practical archive workflow for backups
A good backup archive should have a clear name, include the correct directory and be easy to inspect.
Create a timestamped backup:
tar -czf project-$(date +%Y-%m-%d).tar.gz project/If today is 2026-05-14, this creates:
project-2026-05-14.tar.gzThe date command inside $() runs first and inserts the result into the filename.
A more detailed timestamp:
tar -czf project-$(date +%Y-%m-%d-%H-%M).tar.gz project/This may create:
project-2026-05-14-10-30.tar.gzBackup and verify:
tar -czf project-backup.tar.gz project/
tar -tzf project-backup.tar.gz | head -20
ls -lh project-backup.tar.gzThis creates the archive, lists the first 20 archive entries and shows the archive size.
A safer backup script pattern:
#!/bin/bash
SOURCE="/home/pat/project"
DESTINATION="/home/pat/backups"
DATE=$(date +%Y-%m-%d-%H-%M)
ARCHIVE="$DESTINATION/project-$DATE.tar.gz"
mkdir -p "$DESTINATION"
tar -czf "$ARCHIVE" "$SOURCE"
tar -tzf "$ARCHIVE" > /dev/null
echo "Backup created: $ARCHIVE"This simple script introduces several important Linux ideas: variables, quoting, directory creation, archive creation and verification.
Practical workflow: prepare a clean project backup
Suppose you want to back up a project directory.
Go to the parent directory:
cd /home/pat/projects
pwd
ls -lahInspect the project:
du -sh myproject
tree -L 2 myprojectCreate a backup directory:
mkdir -p /home/pat/backupsCreate a dated archive:
tar -czf /home/pat/backups/myproject-$(date +%Y-%m-%d).tar.gz myproject/Verify archive exists:
ls -lh /home/pat/backupsInspect archive contents:
tar -tzf /home/pat/backups/myproject-$(date +%Y-%m-%d).tar.gz | head -30This is much better than simply compressing files blindly.
A backup is not complete until you know it can be read.
Practical workflow: securely copy a backup from a server
Suppose you want to download a backup from a server.
First connect and inspect:
ssh pat@server
hostname
ls -lh /home/pat/backupsExit remote session:
exitDownload with scp:
scp pat@server:/home/pat/backups/project-backup.tar.gz .Verify local file:
ls -lh project-backup.tar.gz
tar -tzf project-backup.tar.gz | head -30A better method for large or repeated backups:
rsync -avz --progress pat@server:/home/pat/backups/ ./backups/This preserves metadata and is efficient if run repeatedly.
Downloading and verifying archives
When downloading files with wget, do not immediately extract them into important directories.
Use a controlled directory:
mkdir -p ~/downloads
cd ~/downloadsDownload:
wget https://example.com/archive.tar.gzCheck size:
ls -lh archive.tar.gzInspect archive:
tar -tzf archive.tar.gz | head -50Create extraction directory:
mkdir -p extractedExtract there:
tar -xzf archive.tar.gz -C extractedInspect:
tree -L 2 extractedThis prevents messy extraction into the wrong location.
For ZIP:
unzip -l archive.zip
mkdir -p extracted
unzip archive.zip -d extractedThe principle is the same: inspect before extracting.
Practical cron workflow for a daily backup
Create directories:
mkdir -p /home/pat/scripts
mkdir -p /home/pat/backups
mkdir -p /home/pat/logsCreate script:
nano /home/pat/scripts/site-backup.shScript content:
#!/bin/bash
SOURCE="/var/www/site"
DESTINATION="/home/pat/backups"
LOG="/home/pat/logs/site-backup.log"
DATE=$(date +%Y-%m-%d-%H-%M)
ARCHIVE="$DESTINATION/site-$DATE.tar.gz"
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup started" >> "$LOG"
mkdir -p "$DESTINATION"
tar -czf "$ARCHIVE" "$SOURCE" >> "$LOG" 2>&1
if tar -tzf "$ARCHIVE" > /dev/null 2>&1; then
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup verified: $ARCHIVE" >> "$LOG"
else
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup verification failed: $ARCHIVE" >> "$LOG"
exit 1
fi
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup finished" >> "$LOG"Make executable:
chmod +x /home/pat/scripts/site-backup.shRun manually:
/home/pat/scripts/site-backup.shCheck log:
tail -n 50 /home/pat/logs/site-backup.logEdit crontab:
crontab -eAdd daily schedule:
0 2 * * * /home/pat/scripts/site-backup.shVerify:
crontab -lThis is a complete beginner-friendly automation workflow.
Practical workflow: create a compressed report bundle
Suppose support asks you for logs and system state.
Create report directory:
mkdir -p ~/support-reportCollect system information:
hostname > ~/support-report/hostname.txt
uptime > ~/support-report/uptime.txt
df -h > ~/support-report/disk.txt
ip a > ~/support-report/ip-addresses.txt
ip route > ~/support-report/routes.txt
systemctl --failed > ~/support-report/failed-services.txtCollect recent logs:
journalctl -n 300 > ~/support-report/journal-recent.txtCreate archive:
tar -czf support-report-$(date +%Y-%m-%d-%H-%M).tar.gz support-report/Inspect archive:
tar -tzf support-report-$(date +%Y-%m-%d-%H-%M).tar.gz | head -50This creates a clean diagnostic package without manually copying terminal output.
Be careful not to include secrets, private keys, tokens or sensitive customer data in support bundles.
Practical workflow: investigate a broken backup
A backup file exists, but is it usable?
Check size:
ls -lh backup.tar.gzTest archive listing:
tar -tzf backup.tar.gz > /dev/nullCheck exit status:
echo $?Show first entries:
tar -tzf backup.tar.gz | head -30Extract to a test directory:
mkdir -p /tmp/restore-test
tar -xzf backup.tar.gz -C /tmp/restore-test
tree -L 2 /tmp/restore-testA backup is not proven by existing. It is proven by successful reading and preferably test restoration.
A better backup script verifies after creation:
if tar -tzf "$ARCHIVE" > /dev/null 2>&1; then
echo "Backup verified"
else
echo "Backup failed verification"
exit 1
fiWorking with compressed logs
Linux logs are often compressed after rotation.
You may see files like:
syslog.1
syslog.2.gz
auth.log.1
auth.log.2.gzTo read a .gz file without extracting it, use zcat.
zcat /var/log/syslog.2.gz | lessSearch compressed logs with zgrep.
zgrep -i "error" /var/log/syslog.2.gzCount matches:
zgrep -i "failed password" /var/log/auth.log.*.gz | wc -lList compressed log contents:
gzip -l /var/log/syslog.2.gzThese tools help investigate older logs without manually decompressing files.
Practical workflow: create a pre change snapshot using tar
Before making risky changes to a directory, create a local archive.
sudo tar -czf /root/site-before-change-$(date +%Y-%m-%d-%H-%M).tar.gz /var/www/siteVerify:
sudo tar -tzf /root/site-before-change-$(date +%Y-%m-%d-%H-%M).tar.gz | headFor configuration:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%Y-%m-%d-%H-%M).backupFor whole config directory:
sudo tar -czf /root/nginx-config-before-change-$(date +%Y-%m-%d-%H-%M).tar.gz /etc/nginxThis provides rollback material before experiments.
A rollback archive is not a full backup strategy, but it is very useful before local changes.
Understanding file integrity checks
Sometimes you need to verify whether a file changed or whether two files are identical.
Use checksums.
sha256sum file.txtExample output:
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 file.txtCompare two files:
sha256sum file1.txt file2.txtIf the hash is the same, the content is the same with extremely high practical confidence.
Create checksum file:
sha256sum backup.tar.gz > backup.tar.gz.sha256Verify later:
sha256sum -c backup.tar.gz.sha256This is useful for backups, downloads and file transfer verification.
For a directory archive:
tar -czf site.tar.gz /var/www/site
sha256sum site.tar.gz > site.tar.gz.sha256After transfer:
sha256sum -c site.tar.gz.sha256This verifies that the archive did not change during transfer.
Practical workflow: verify transferred backup
On source server:
tar -czf site-backup.tar.gz /var/www/site
sha256sum site-backup.tar.gz > site-backup.tar.gz.sha256Transfer:
scp site-backup.tar.gz site-backup.tar.gz.sha256 backup@backup-server:/backups/On backup server:
cd /backups
sha256sum -c site-backup.tar.gz.sha256Expected output:
site-backup.tar.gz: OKThen test archive:
tar -tzf site-backup.tar.gz | headThis workflow verifies both transfer integrity and archive readability.
Understanding compression choices
Different compression tools have different tradeoffs.
| Format | Command | General characteristics |
|---|---|---|
| gzip | gzip, tar -z | Fast, common, good default |
| bzip2 | bzip2, tar -j | Better compression than gzip in some cases, slower |
| xz | xz, tar -J | Strong compression, often slower |
| zip | zip | Common for sharing with Windows users |
| plain tar | tar -cf | Archive without compression |
Create gzip tar:
tar -czf archive.tar.gz directory/Create xz tar:
tar -cJf archive.tar.xz directory/Create uncompressed tar:
tar -cf archive.tar directory/If speed matters, gzip is often a good default. If smallest size matters and time is acceptable, xz may be useful.
For system backups, compatibility and restore speed often matter more than maximum compression.
Practical workflow: choose backup compression
Fast backup:
tar -czf site-fast-backup.tar.gz /var/www/siteSmaller backup:
tar -cJf site-small-backup.tar.xz /var/www/siteNo compression, useful when storage is fast and compression is unnecessary:
tar -cf site-backup.tar /var/www/siteCheck sizes:
ls -lh site-fast-backup.tar.gz site-small-backup.tar.xz site-backup.tarTest readability:
tar -tf site-backup.tar | head
tar -tzf site-fast-backup.tar.gz | head
tar -tJf site-small-backup.tar.xz | headUse the compression method that fits the operational need.
Practical workflow: restore configuration safely
Suppose you backed up /etc/nginx.
Archive:
nginx-config-backup.tar.gzInspect:
tar -tzf nginx-config-backup.tar.gz | head -50Extract into test directory:
mkdir -p /tmp/nginx-config-restore
tar -xzf nginx-config-backup.tar.gz -C /tmp/nginx-config-restore
tree -L 3 /tmp/nginx-config-restoreCompare current and backup:
diff -ru /etc/nginx /tmp/nginx-config-restore/etc/nginxRestore one file if needed:
sudo cp /tmp/nginx-config-restore/etc/nginx/nginx.conf /etc/nginx/nginx.confTest:
sudo nginx -tReload:
sudo systemctl reload nginxThis avoids blindly overwriting the whole configuration directory.
Practical workflow: compare cron and systemd timer for backups
Cron version:
0 2 * * * /home/pat/ops/scripts/site-backup >> /home/pat/ops/logs/site-backup-cron.log 2>&1Systemd timer version:
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=trueComparison:
| Feature | Cron | systemd timer |
|---|---|---|
| Simple syntax | Yes | More files required |
| Integrated logs | Manual redirection | journalctl |
| Missed run handling | Not by default | Persistent=true |
| Dependency handling | Limited | Strong |
| Service status | Limited | systemctl status |
| User familiarity | Very common | Modern systemd systems |
Use cron for simple user tasks. Use systemd timers when you want service-style visibility, missed-run handling and better integration.
Permissions, ownership, users and groups
Permissions and ownership explain many Linux problems that look mysterious at first. Users, groups, modes, ACLs and sudo policy determine who can access what and under which conditions.
Understanding Linux permissions
Linux permissions control who can read, write and execute files. This is one of the most important security concepts in the system.
Run:
ls -lExample:
-rw-r--r-- 1 pat pat 1200 May 14 10:18 notes.txt
-rwxr-xr-x 1 pat pat 842 May 14 10:19 backup.sh
drwxr-xr-x 2 pat pat 4096 May 14 10:20 scriptsThe first column contains type and permissions.
Take this example:
-rwxr-xr--Break it down:
| Part | Meaning |
|---|---|
- | Regular file |
rwx | Owner permissions |
r-x | Group permissions |
r-- | Others permissions |
The permission letters mean:
| Letter | Meaning for files | Meaning for directories |
|---|---|---|
r | Read file content | List directory contents |
w | Modify file content | Create, delete or rename entries inside directory |
x | Execute file as a program | Enter or traverse directory |
The meaning of execute permission is different for files and directories.
For a file, execute means you can run it as a program or script.
For a directory, execute means you can enter it and access items inside it if you know their names.
This is why directory permissions can be confusing. A directory needs execute permission to be usable.
Permission groups: owner, group and others
Every file has three permission categories.
| Category | Meaning |
|---|---|
| Owner | The user who owns the file |
| Group | The group assigned to the file |
| Others | Everyone else |
Example:
-rw-r--r-- 1 pat editors 1200 May 14 10:18 article.txtThis means:
| Field | Value |
|---|---|
| Owner | pat |
| Group | editors |
| Owner permissions | rw- |
| Group permissions | r-- |
| Others permissions | r-- |
So pat can read and write. Members of editors can read. Everyone else can read.
A more restrictive example:
-rw------- 1 pat pat 1200 May 14 10:18 private.txtOnly pat can read and write this file.
A script example:
-rwxr-xr-x 1 pat pat 842 May 14 10:19 backup.shThe owner can read, write and execute. Group and others can read and execute.
chmod with symbolic permissions
The command chmod changes permissions.
Make a script executable:
chmod +x backup.shThis adds execute permission.
Remove execute permission:
chmod -x backup.shGive the owner execute permission only:
chmod u+x backup.shRemove write permission from others:
chmod o-w file.txtAdd read permission for group:
chmod g+r file.txtThe symbolic categories are:
| Symbol | Meaning |
|---|---|
u | User owner |
g | Group |
o | Others |
a | All |
The operations are:
| Symbol | Meaning |
|---|---|
+ | Add permission |
- | Remove permission |
= | Set exact permission |
The permissions are:
| Symbol | Meaning |
|---|---|
r | Read |
w | Write |
x | Execute |
Examples:
chmod u+rw file.txt
chmod g-w file.txt
chmod o-r file.txt
chmod a+r public.txt
chmod u=rwx,g=rx,o= script.shSymbolic permissions are readable and good for learning.
chmod with numeric permissions
Numeric permissions are compact and common in administration.
Each permission has a number:
| Permission | Number |
|---|---|
| Read | 4 |
| Write | 2 |
| Execute | 1 |
Add the numbers together.
| Permission set | Calculation | Value |
|---|---|---|
r-- | 4 | 4 |
rw- | 4 + 2 | 6 |
r-x | 4 + 1 | 5 |
rwx | 4 + 2 + 1 | 7 |
--- | 0 | 0 |
A numeric permission has three digits.
| Digit position | Applies to |
|---|---|
| First digit | Owner |
| Second digit | Group |
| Third digit | Others |
Examples:
| Permission | Meaning |
|---|---|
644 | Owner read/write, group read, others read |
600 | Owner read/write, nobody else has access |
755 | Owner full access, group and others read/execute |
700 | Owner full access, nobody else has access |
775 | Owner and group full access, others read/execute |
777 | Everyone can read, write and execute |
Set file to 644:
chmod 644 file.txtSet script to 755:
chmod 755 script.shSet private key to 600:
chmod 600 ~/.ssh/id_rsaSet private directory to 700:
chmod 700 ~/.sshImportant warning: do not use chmod 777 as a universal fix. It gives everyone full access and can create serious security problems.
chmod on directories
Directory permissions deserve special attention.
For directories:
| Permission | Meaning |
|---|---|
| Read | Can list directory contents |
| Write | Can create, delete or rename entries |
| Execute | Can enter or traverse the directory |
A common directory permission is 755.
chmod 755 public-directoryThis means:
| Category | Permission | Meaning |
|---|---|---|
| Owner | rwx | Can enter, list and modify |
| Group | r-x | Can enter and list |
| Others | r-x | Can enter and list |
A private directory often uses 700.
chmod 700 private-directoryThis means only the owner can access it.
For web directories, permissions must be chosen carefully. Giving write access to the wrong user can create security risks. Giving too little access can break uploads, cache generation or application updates.
A common safe starting pattern for many web projects is:
find /var/www/site -type d -exec chmod 755 {} \;
find /var/www/site -type f -exec chmod 644 {} \;This sets directories to 755 and files to 644.
Do not apply this blindly to every application. Some applications need writable cache or upload directories.
chown: change owner and group
The command chown changes file owner and group.
Change owner:
sudo chown pat file.txtChange owner and group:
sudo chown pat:www-data file.txtChange ownership recursively:
sudo chown -R pat:www-data /var/www/siteThe -R option means recursive. It changes ownership for the directory and everything inside it.
This is powerful and must be used carefully.
Common ownership examples:
| Command | Meaning |
|---|---|
sudo chown pat file.txt | Make pat the owner |
sudo chown pat:editors file.txt | Owner pat, group editors |
sudo chown :www-data file.txt | Change group only |
sudo chown -R pat:pat folder | Recursively change owner and group |
Check ownership:
ls -l file.txtCheck directory ownership:
ls -ld folderThe difference matters:
ls -l foldershows the contents of the folder.
ls -ld foldershows the folder itself.
Permission and ownership troubleshooting
Permission problems are common. The error messages usually look like this:
Permission deniedor:
Operation not permittedA good troubleshooting workflow:
whoami
pwd
ls -ld .
ls -l target-fileThis tells you who you are, where you are, the permissions of the current directory and the permissions of the target file.
If a script will not run:
ls -l script.sh
chmod +x script.sh
./script.shIf a directory cannot be entered:
ls -ld directoryCheck whether execute permission exists.
If a web application cannot write to a cache directory:
ls -ld cache
ls -l cacheYou may need to adjust owner, group or permissions depending on which user the web server runs as.
For example:
sudo chown -R www-data:www-data cacheor:
sudo chmod -R 775 cacheBut do not guess. First identify the correct service user.
ps aux | grep apache
ps aux | grep nginxPermission fixes should be precise, not random.
The danger of recursive chmod and chown
Recursive permission changes can affect thousands of files.
This command:
sudo chmod -R 777 /var/www/sitemay appear to “fix” a permission problem, but it creates a major security problem.
This command:
sudo chown -R pat:pat /would be catastrophic because it would attempt to change ownership across the entire filesystem.
Before recursive changes, always verify the target.
pwd
ls -ld /var/www/siteThen use precise commands.
A safer web project example:
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;Writable upload directory only:
sudo chown -R www-data:www-data /var/www/site/uploads
sudo chmod -R 775 /var/www/site/uploadsThis is better than making the entire website writable.
The principle is simple: change the smallest necessary area with the least necessary permission.
whoami, id and groups
Before changing permissions or debugging access, know who you are.
whoamiThis prints the current username.
Show user and group IDs:
idExample:
uid=1000(pat) gid=1000(pat) groups=1000(pat),27(sudo),33(www-data)This tells you:
| Field | Meaning |
|---|---|
uid | User ID |
gid | Primary group ID |
groups | Supplementary groups |
Show groups:
groupsShow groups for another user:
groups usernameThis matters because Linux permissions depend not only on the username but also on group membership.
If a file is owned by group www-data and has group write permission, a user must be in the www-data group to use that permission.
Example:
ls -l file.txtOutput:
-rw-rw-r-- 1 root www-data 1200 May 14 10:20 file.txtA user in group www-data may write to this file if directory permissions also allow access.
Check:
idIf www-data is not listed, group permission will not apply to you.
su and sudo are different
The command sudo runs a command with elevated privileges, usually as root.
sudo apt updateThe command su switches user.
su -This tries to switch to the root user and start a login shell.
Switch to another user:
su - usernameRun a command as another user with sudo:
sudo -u www-data whoamiExample:
sudo -u www-data php script.phpThis runs the PHP script as the www-data user.
This is useful when debugging web application permissions. A file may be readable by your user but not by the web server user.
Test access as www-data:
sudo -u www-data ls -lah /var/www/siteTest writing:
sudo -u www-data touch /var/www/site/cache/test-fileIf that fails, the web server may also fail to write cache files.
Use sudo -u as a diagnostic tool, not only as an administration shortcut.
Understanding users and groups in file access
Linux file access is decided by user, group and others permissions.
Example:
-rw-r----- 1 root www-data 1200 May 14 10:20 config.phpThis means:
| Category | Permission |
|---|---|
Owner root | Read and write |
Group www-data | Read |
| Others | No access |
If the web server runs as www-data, it can read the file. Other users cannot.
Now consider:
drwxr-x--- 2 root www-data 4096 May 14 10:20 privateThis directory allows:
| Category | Access |
|---|---|
Owner root | Enter, list, modify |
Group www-data | Enter and list |
| Others | No access |
If a user is not root and not in www-data, they cannot enter.
To troubleshoot path access, use:
namei -l /var/www/site/private/config.phpThis shows permissions for every path component.
Example output may show:
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-x--- root www-data site
-rw-r----- root www-data config.phpThis helps reveal where access is blocked.
A file can have correct permissions, but if a parent directory blocks execute access, the file is still inaccessible.
passwd and changing passwords
The command passwd changes a user password.
Change your own password:
passwdChange another user’s password as administrator:
sudo passwd usernameLock a password for an account:
sudo passwd -l usernameUnlock:
sudo passwd -u usernameOn servers using SSH keys, password login may be disabled, but account passwords can still matter for local login, sudo policies or recovery workflows depending on configuration.
Password changes should be handled carefully, especially on production systems. Always know whether the account is used by humans, services or automation.
adduser, usermod and group membership
Create a user on Debian-based systems:
sudo adduser newuserAdd a user to a group:
sudo usermod -aG groupname usernameExample: add user to sudo group:
sudo usermod -aG sudo patExample: add user to www-data group:
sudo usermod -aG www-data patThe -aG combination is important.
| Option | Meaning |
|---|---|
-a | Append to supplementary groups |
-G | Specify supplementary groups |
Without -a, you may replace the user’s supplementary groups instead of adding to them. That can remove important access.
After group changes, the user usually needs to log out and log back in for the new group membership to appear.
Check:
id pator:
groups patPractical workflow: investigate permission denied
Suppose a user cannot read:
/var/www/site/uploads/image.jpgStart with identity:
whoami
idCheck the file:
ls -l /var/www/site/uploads/image.jpgCheck the parent directory:
ls -ld /var/www/site/uploadsCheck the whole path:
namei -l /var/www/site/uploads/image.jpgTest as the service user if relevant:
sudo -u www-data ls -l /var/www/site/uploads/image.jpgIf the issue is missing directory execute permission, changing only the file will not help. One of the parent directories must allow traversal.
Example fix for a web upload directory:
sudo chown -R www-data:www-data /var/www/site/uploads
sudo find /var/www/site/uploads -type d -exec chmod 755 {} \;
sudo find /var/www/site/uploads -type f -exec chmod 644 {} \;But this is only correct if www-data should own those uploads. Do not apply ownership changes blindly.
Process ownership and service users
Every process runs as a user. That user controls what the process can read and write.
Show processes:
ps auxFind web server processes:
ps aux | grep nginx
ps aux | grep apache
ps aux | grep phpThe first column is the process owner.
Example:
www-data 842 0.1 1.5 300000 65000 ? S 10:20 0:01 nginxThis means the process runs as www-data.
If the process needs to write to:
/var/www/site/cachethen test:
sudo -u www-data touch /var/www/site/cache/test-fileIf it fails, the application will likely fail too.
This is the connection between processes and permissions.
Advanced permissions explain many problems that basic chmod does not solve
Basic Linux permissions are built around three actions and three identity classes.
The actions are read, write and execute. The identity classes are owner, group and others. This model explains many everyday problems, but real Linux systems often need more detail. Web servers, shared directories, deployment users, backup users, scripts, SSH keys and service accounts all depend on permissions behaving predictably.
A beginner may know that chmod 755 gives executable access and chmod 644 is common for files. A professional user asks deeper questions.
Who owns the file?
Which group owns it?
Which user runs the process that needs access?
Does every parent directory allow traversal?
Is the execute bit missing on a directory?
Is the file controlled by ACL rules?
Is a special permission bit active?
Is the default creation mask causing new files to have unexpected permissions?
Is a service writing files as a different user than expected?
Linux permission problems often look simple from the outside, but the cause may be one level deeper than the file itself.
Review of standard permission logic
Run:
ls -l file.txtExample output:
-rw-r----- 1 pat www-data 1200 May 14 10:20 file.txtThis line tells you the file type, permissions, owner, group, size, time and name.
The permission part is:
-rw-r-----Break it down:
| Part | Meaning |
|---|---|
- | Regular file |
rw- | Owner can read and write |
r-- | Group can read |
--- | Others have no access |
The owner is:
patThe group is:
www-dataThis means user pat can read and write. Members of group www-data can read. Everyone else has no access.
For directories, execute permission has a special meaning. It means the user can enter or traverse the directory.
Example:
ls -ld /var/www/siteOutput:
drwxr-x--- 5 deploy www-data 4096 May 14 10:20 /var/www/siteThis means:
| Category | Permission | Practical effect |
|---|---|---|
Owner deploy | rwx | Can enter, list and modify |
Group www-data | r-x | Can enter and list |
| Others | --- | Cannot access |
If a web server runs as www-data, it can read files inside this directory only if the files and all parent directories allow it.
This is why the command below is extremely useful:
namei -l /var/www/site/index.htmlIt shows permissions for each path component, not only the final file.
umask controls default permissions for new files
When a user or process creates a new file, Linux does not usually create it with full open permissions. The final permissions are influenced by a value called umask.
Show current umask:
umaskExample output:
0022The umask removes permissions from the default creation mode.
Typical defaults are:
| Object type | Base permission before umask |
|---|---|
| New file | 666 |
| New directory | 777 |
If the umask is 022, then:
| Object | Base | Minus umask | Result |
|---|---|---|---|
| File | 666 | 022 | 644 |
| Directory | 777 | 022 | 755 |
That is why new files are often created as 644 and new directories as 755.
If the umask is 002, then:
| Object | Base | Minus umask | Result |
|---|---|---|---|
| File | 666 | 002 | 664 |
| Directory | 777 | 002 | 775 |
This is common in collaborative group environments where group members should be able to write.
Set umask temporarily:
umask 002Create a file and directory:
touch test-file
mkdir test-directory
ls -l
ls -ld test-directoryThe permissions will reflect the active umask.
Why umask matters:
| Situation | Why umask matters |
|---|---|
| Deployment user creates files | New files may not be group-writable |
| Web app creates cache files | Runtime permissions may differ from expected |
| Shared folders | Group collaboration may fail |
| Backup scripts | Restored or generated files may have different access |
| Cron jobs | Non-interactive environment may use different umask |
You can set umask inside a script.
#!/bin/bash
umask 002
mkdir -p /var/www/site/cache
touch /var/www/site/cache/example.cacheThis ensures new files created by the script follow the intended permission pattern.
setgid directories keep group ownership consistent
A common problem in shared directories is inconsistent group ownership.
Suppose a project directory is owned by group www-data.
ls -ld /var/www/siteOutput:
drwxrwxr-x 5 deploy www-data 4096 May 14 10:20 /var/www/siteIf user deploy creates a new file, the file may be owned by the user’s primary group, not necessarily www-data.
The setgid bit on a directory helps solve this. When setgid is active on a directory, new files and subdirectories created inside it inherit the directory’s group.
Set setgid on a directory:
sudo chmod g+s /var/www/siteCheck:
ls -ld /var/www/siteOutput may show:
drwxrwsr-x 5 deploy www-data 4096 May 14 10:20 /var/www/siteThe s in the group execute position indicates setgid.
Apply to directories recursively:
sudo find /var/www/site -type d -exec chmod g+s {} \;A practical collaborative web directory setup may look like this:
sudo chown -R deploy:www-data /var/www/site
sudo find /var/www/site -type d -exec chmod 2775 {} \;
sudo find /var/www/site -type f -exec chmod 664 {} \;The leading 2 in 2775 means setgid.
| Permission | Meaning |
|---|---|
775 | Owner and group can write, others can read and enter |
2775 | Same as 775, plus setgid on directory |
Use this carefully. It is useful for shared project directories, but it should not be applied blindly across the system.
Sticky bit protects shared directories
The sticky bit is commonly used on shared writable directories such as /tmp.
Check /tmp:
ls -ld /tmpTypical output:
drwxrwxrwt 10 root root 4096 May 14 10:20 /tmpThe t at the end means sticky bit.
A directory with sticky bit allows multiple users to create files, but users cannot delete files owned by other users unless they have appropriate privileges.
This is important for shared temporary directories.
Set sticky bit:
sudo chmod +t shared-directoryNumeric form:
sudo chmod 1777 shared-directoryA permission of 1777 means:
| Part | Meaning |
|---|---|
1 | Sticky bit |
777 | Everyone can read, write and enter |
This is appropriate for directories like /tmp, not for ordinary web application directories.
Do not use 1777 as a lazy fix for permission problems. It is for specific shared temporary use cases.
setuid is powerful and risky
The setuid bit allows an executable file to run with the permissions of the file owner, not the user who launched it.
A classic example is passwd.
ls -l /usr/bin/passwdOutput may look like:
-rwsr-xr-x 1 root root 68208 May 14 10:20 /usr/bin/passwdThe s in the owner execute position means setuid.
Why does passwd need this? A normal user must be able to change their password, but password data is stored in protected system files. The command needs controlled elevated privileges.
Set setuid:
sudo chmod u+s executable-fileNumeric form:
sudo chmod 4755 executable-fileThe leading 4 means setuid.
Setuid is dangerous when applied incorrectly. A vulnerable setuid root program can become a serious security problem.
Find setuid files:
sudo find / -perm -4000 -type f -exec ls -lh {} \; 2> /dev/nullThis command searches for files with setuid bit set.
You do not need to modify setuid files in normal administration unless you know exactly why.
ACLs provide permissions beyond owner group others
Standard permissions allow one owner, one group and others. Sometimes this is not flexible enough. Linux ACLs, access control lists, allow more detailed permission rules.
Check ACLs:
getfacl file.txtSet ACL for a user:
setfacl -m u:alice:rw file.txtThis gives user alice read and write access to file.txt.
Set ACL for a group:
setfacl -m g:editors:rw file.txtRemove ACL for a user:
setfacl -x u:alice file.txtSet default ACL on a directory:
setfacl -m d:g:editors:rwx shared-directoryDefault ACLs apply to newly created files and directories inside that directory.
Show ACLs:
getfacl shared-directoryIf ls -l shows a plus sign after permissions, ACLs exist.
Example:
-rw-r-----+ 1 pat www-data 1200 May 14 10:20 file.txtThe + means extended ACLs are present.
ACLs are powerful, but they can also make permission troubleshooting more complex. If normal chmod and chown do not explain access behavior, check ACLs.
getfacl /path/to/filesudo is controlled by policy
The sudo command does not magically give everyone root access. It checks policy. That policy is usually configured through the sudoers system.
The safest way to edit sudoers is:
sudo visudoDo not edit the sudoers file with a normal editor unless you know exactly what you are doing. visudo checks syntax before saving, reducing the risk of locking out administrative access.
A common sudoers rule looks like:
%sudo ALL=(ALL:ALL) ALLThis means members of group sudo can run commands as any user and group on any host, after authentication.
Check your groups:
idIf your user is in the sudo group, sudo access may be granted depending on configuration.
Add user to sudo group:
sudo usermod -aG sudo usernameThe user usually needs to log out and back in for group membership to apply.
Test sudo access:
sudo -vRun a command:
sudo whoamiExpected output:
rootList allowed sudo commands:
sudo -lThis shows what the current user is allowed to run through sudo.
The sudoers.d directory
Many systems support additional sudo rules under:
/etc/sudoers.d/List files:
sudo ls -lah /etc/sudoers.d/Create a controlled rule with visudo:
sudo visudo -f /etc/sudoers.d/deployExample rule:
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl reload nginxThis allows user deploy to reload Nginx without a password, but only that command.
This is much safer than giving full passwordless root access.
Bad rule:
deploy ALL=(ALL) NOPASSWD: ALLThis allows everything without a password. It may be too broad for many situations.
Better rules are specific.
| User | Allowed command | Reason |
|---|---|---|
deploy | Reload web server | Needed after deployment |
backup | Run backup script | Needed for scheduled backups |
monitor | Read status commands | Needed for monitoring |
Least privilege applies to sudo too.
Understanding file ownership after extraction
When extracting archives, ownership can matter.
Extract as normal user:
tar -xzf archive.tar.gzFiles may be owned by the extracting user depending on archive and options.
Extract as root:
sudo tar -xzf archive.tar.gz -C /This may preserve original numeric ownerships, which can be desired for system restores but dangerous if the archive is untrusted.
Inspect archive first:
tar -tzvf archive.tar.gz | head -30The -v option shows details such as permissions and ownership.
Safer extraction into test directory:
mkdir -p /tmp/extract-test
tar -xzf archive.tar.gz -C /tmp/extract-test
ls -lah /tmp/extract-testNever extract an unknown archive directly into /, /etc, /usr or a production directory without inspection.
Package management and software installation
Package management is the safest normal way to install, update, remove and investigate software on Debian-based Linux systems.
Package management is the safest way to install software on Linux
Installing software on Linux should usually be done through a package manager. A package manager is not only a download tool. It is a controlled system for finding software, installing it, updating it, verifying dependencies, removing it and keeping the operating system consistent.
On Debian-based systems such as Debian, Ubuntu, Linux Mint, Raspberry Pi OS and many server distributions derived from Debian, the most common package tool is apt.
When you install a program through apt, Linux does more than place one executable file somewhere on the disk. It checks repositories, reads package metadata, resolves dependencies, downloads the correct package versions, installs required libraries, registers the package in the local package database and often configures service files, documentation and default settings.
This matters because Linux software rarely exists as one isolated file. A web server, programming language, database system or desktop application may depend on many libraries and supporting packages.
For example, when you install a package such as apache2, the system may also install required dependencies. When you remove it later, the package manager knows what was installed and can manage it cleanly.
| Concept | Meaning |
|---|---|
| Package | A software unit managed by the operating system |
| Dependency | Another package required by a package |
| Repository | A trusted server or source containing packages |
| Package list | Local index of available packages |
| Package database | Local record of installed packages |
| Upgrade | Installing newer versions of installed packages |
| Remove | Uninstalling a package |
| Purge | Removing a package and its configuration files |
| Autoremove | Removing dependencies no longer needed |
A beginner may think installing software means downloading a random file from a website. On Linux, that should not be the first habit. The first habit should be: search the package manager.
apt search package-nameThen install using:
sudo apt install package-nameThis approach is safer, easier to update and easier to undo.
Understanding repositories
A repository is a software source. It contains packages prepared for your distribution and version. Your system does not automatically know every software package in the world. It knows the repositories configured on that machine.
Repository configuration is commonly stored in files under:
/etc/apt/sources.listand:
/etc/apt/sources.list.d/You do not need to edit these files for normal package installation, but it is useful to know they exist.
When you run:
sudo apt updateyour system contacts configured repositories and downloads fresh package lists. It does not upgrade your software yet. It only refreshes the local knowledge of what is available.
When you run:
sudo apt upgradeyour system installs newer versions of packages that are already installed, if upgrades are available.
This distinction is essential.
| Command | What it does | What it does not do |
|---|---|---|
sudo apt update | Refreshes package lists | Does not install upgrades |
sudo apt upgrade | Upgrades installed packages | Does not refresh package lists by itself |
sudo apt install package | Installs a package | Does not automatically know newest lists unless update was run |
sudo apt remove package | Removes a package | May leave configuration files |
sudo apt purge package | Removes package and configuration | Does not remove all user data |
sudo apt autoremove | Removes unused dependencies | Does not remove manually installed needed packages |
A correct update workflow usually looks like this:
sudo apt update
sudo apt upgradeThis means: first refresh the list of available software versions, then upgrade installed packages.
apt update explained deeply
The command:
sudo apt updateupdates the local package index. It asks the repositories: what packages are available, what versions exist and what dependencies are defined?
It does not install new package versions by itself.
This is why the name can be confusing for beginners. In everyday language, “update” often means making software newer. In apt, update means updating the package information database.
A typical workflow before installing software is:
sudo apt update
sudo apt install htopThis ensures that the package manager uses current repository metadata.
If you skip apt update, installation may still work, but your system may use outdated package information. On a server that has not been updated in a long time, this can lead to errors such as package not found, version conflicts or outdated dependency information.
A good habit:
sudo apt updatebefore:
sudo apt install package-nameand before:
sudo apt upgradeCommon output may include repository lines, package list downloads and information about packages that can be upgraded.
After apt update, the system may say that packages can be upgraded. That means you should decide whether to run:
sudo apt upgradeor a more complete upgrade command depending on the system policy.
For a beginner or general server maintenance workflow, this is the standard safe starting point:
sudo apt update
sudo apt upgradeapt upgrade explained deeply
The command:
sudo apt upgradeupgrades installed packages to newer available versions from configured repositories.
It uses the package lists refreshed by apt update.
The command may show a list of packages to be upgraded and ask for confirmation. You can review the packages before accepting.
Automatic yes:
sudo apt upgrade -yThe -y option answers yes automatically. This can be useful in scripts, but it should not be used blindly on important production systems unless you understand the update policy.
Manual confirmation is safer when learning.
sudo apt upgradeA good maintenance workflow:
sudo apt update
apt list --upgradable
sudo apt upgradeThe command:
apt list --upgradableshows which packages can be upgraded before actually upgrading them.
This gives you more control.
| Command | Purpose |
|---|---|
sudo apt update | Refresh available package information |
apt list --upgradable | Show packages with available upgrades |
sudo apt upgrade | Install available upgrades |
sudo apt autoremove | Clean dependencies no longer needed |
A careful server maintenance sequence:
sudo apt update
apt list --upgradable
sudo apt upgrade
sudo apt autoremoveAfter kernel or critical system updates, a reboot may be required.
You can check whether a reboot is suggested on many Debian-based systems by looking for:
/var/run/reboot-requiredExample:
ls /var/run/reboot-requiredIf the file exists, the system indicates that a reboot is recommended.
apt install explained deeply
The command:
sudo apt install package-nameinstalls a package.
Example:
sudo apt install htopThis installs htop, an interactive process viewer.
Install multiple packages:
sudo apt install htop tree curl wget unzipThis installs several packages in one command.
Before installing, you can search:
apt search htopYou can show package information:
apt show htopThe apt show command may display package version, description, dependencies, installed size, source repository and maintainer information.
This is useful when you do not know exactly what a package is.
| Command | Purpose |
|---|---|
apt search htop | Search package names and descriptions |
apt show htop | Show detailed package information |
sudo apt install htop | Install the package |
| `dpkg -l | grep htop` |
Install without recommended packages:
sudo apt install --no-install-recommends package-nameThis can reduce installed dependencies, especially on minimal servers or containers. However, it may also omit useful supporting packages. Use it only when you understand the package.
Reinstall a package:
sudo apt install --reinstall package-nameThis can help if files belonging to a package were accidentally removed or damaged.
apt search and finding the correct package name
Package names are not always obvious. The program name and package name may differ. This is why apt search is important.
Search for OpenJDK packages:
apt search openjdkFilter search results:
apt search openjdk | grep jreSearch for Apache-related packages:
apt search apache2Search for PHP packages:
apt search phpSearch can produce many results. Filtering with grep makes it more useful.
apt search php | grep mysqlThis searches packages related to PHP and filters lines containing mysql.
A practical pattern:
apt search keyword | grep second-keywordExamples:
apt search python | grep venv
apt search nginx | grep module
apt search php | grep curl
apt search openjdk | grep jdkThis shows how package management connects to text filtering. apt search produces output. grep narrows it.
apt remove and apt purge
The command:
sudo apt remove package-nameremoves a package, but usually leaves configuration files behind.
Example:
sudo apt remove vimThis removes the vim package.
If you want to remove the package and system-level configuration files, use:
sudo apt purge vimThe difference matters.
| Command | Removes program files | Removes system configuration |
|---|---|---|
apt remove | Yes | Usually no |
apt purge | Yes | Yes |
After removing packages, unused dependencies may remain. Clean them with:
sudo apt autoremoveA common cleanup workflow:
sudo apt remove package-name
sudo apt autoremoveA more complete removal workflow:
sudo apt purge package-name
sudo apt autoremoveImportant detail: user-specific configuration files in home directories are usually not removed by apt purge.
For example, if an application created settings under:
/home/pat/.config/those may remain because they belong to the user, not the system package database.
This is usually good because removing a package should not casually delete personal user data.
dpkg and manual package installation
The command dpkg is a lower-level Debian package tool. While apt manages repositories and dependencies, dpkg directly installs, removes and queries .deb packages.
Install a local .deb file:
sudo dpkg -i package.debIf dependencies are missing, fix them with:
sudo apt -f installThe -f means fix broken dependencies.
A common manual installation workflow:
wget https://example.com/software.deb
sudo dpkg -i software.deb
sudo apt -f installHowever, be careful with random .deb files. Installing packages outside official repositories can introduce compatibility, security and update problems.
Use manual .deb installation when:
| Situation | Reason |
|---|---|
The software vendor provides an official .deb package | Common for some commercial tools |
| The package is not available in your repository | Manual installation may be necessary |
| You understand the source and trust it | Security matters |
| You know how updates will be handled | Manual packages may not update automatically unless they add a repository |
List installed packages:
dpkg -lFilter installed packages:
dpkg -l | grep phpShow files installed by a package:
dpkg -L package-nameFind which package owns a file:
dpkg -S /path/to/fileExample:
dpkg -S /usr/bin/lsThis can tell you which package provides the ls command.
The package manager is also a system investigation tool.
Practical package workflow for a new server
When starting with a fresh Debian-based server, a common first workflow is:
sudo apt update
sudo apt upgrade
sudo apt install htop tree curl wget unzip zip rsyncThis updates the system and installs useful administration tools.
A slightly more careful version:
sudo apt update
apt list --upgradable
sudo apt upgrade
sudo apt install htop tree curl wget unzip zip rsyncThen verify installed tools:
command -v htop
command -v tree
command -v curl
command -v wget
command -v rsyncThis shows where the commands are located.
Example output:
/usr/bin/htop
/usr/bin/tree
/usr/bin/curl
/usr/bin/wget
/usr/bin/rsyncA good package maintenance routine:
sudo apt update
apt list --upgradable
sudo apt upgrade
sudo apt autoremoveThis keeps the package database fresh, reviews upgrades, applies them and removes unused dependencies.
Practical workflow: install and start a web server
Install Apache:
sudo apt update
sudo apt install apache2Start Apache:
sudo systemctl start apache2Enable Apache at boot:
sudo systemctl enable apache2Check status:
systemctl status apache2Check listening ports:
sudo ss -tulpn | grep ':80'Create a test file:
echo "Apache is working" | sudo tee /var/www/html/test.htmlThe command tee writes input to a file. With sudo, it can write to protected locations.
Why not simply use this?
sudo echo "Apache is working" > /var/www/html/test.htmlBecause redirection is handled by the shell before sudo applies to echo. The shell may not have permission to write into /var/www/html.
This works:
echo "Apache is working" | sudo tee /var/www/html/test.htmlThis is an important Linux lesson: sudo applies to the command, not automatically to shell redirection.
Practical workflow: install, inspect and remove a package
Install a tool:
sudo apt update
sudo apt install treeVerify command path:
command -v treeUse it:
tree -L 2Check package status:
dpkg -l | grep treeShow package information:
apt show treeShow installed files:
dpkg -L treeRemove it:
sudo apt remove treeRemove unused dependencies:
sudo apt autoremoveThis workflow teaches the full lifecycle: search, install, verify, inspect, use and remove.
Practical workflow: safe system update on a small server
A small server maintenance sequence can look like this:
who
uptime
df -h
systemctl --failed
sudo apt update
apt list --upgradable
sudo apt upgrade
sudo apt autoremove
systemctl --failedExplanation:
| Command | Reason |
|---|---|
who | See logged-in users |
uptime | Check load and runtime |
df -h | Ensure enough disk space |
systemctl --failed | See current service problems |
apt update | Refresh package lists |
apt list --upgradable | Review available upgrades |
apt upgrade | Apply upgrades |
apt autoremove | Remove unused dependencies |
systemctl --failed | Confirm no new failed services |
If a reboot is needed:
sudo rebootAfter reconnecting:
uptime
systemctl --failedThis is controlled maintenance, not random updating.
Practical workflow: inspect a package and its files
Check whether a package is installed:
dpkg -l | grep nginxShow package details:
apt show nginxShow installed files:
dpkg -L nginxFind which package owns a file:
dpkg -S /usr/sbin/nginxCheck service:
systemctl status nginxThis connects package management and service management.
If a configuration file is missing, dpkg -L package can show where it should be. If a binary exists but you do not know where it came from, dpkg -S can identify its package.
apt troubleshooting
Package management can fail for several reasons: network problems, repository issues, locks, broken dependencies, interrupted installations or insufficient disk space.
Start with disk space:
df -hUpdate package lists:
sudo apt updateIf update fails, read the error. It may be DNS, repository, GPG key, TLS, proxy or network related.
Check internet:
ping -c 4 8.8.8.8
getent hosts deb.debian.orgFix broken dependencies:
sudo apt -f installReconfigure interrupted packages:
sudo dpkg --configure -aClean package cache:
sudo apt cleanRemove unused dependencies:
sudo apt autoremoveCheck apt logs:
ls -lah /var/log/apt
cat /var/log/apt/history.logA package lock error may look like another apt process is running. Check processes:
ps aux | grep apt
ps aux | grep dpkgDo not delete lock files casually. First check whether a real package process is active. Killing or interrupting package operations can damage package state.
A safe recovery sequence after interrupted package work:
sudo dpkg --configure -a
sudo apt -f install
sudo apt update
sudo apt upgradeThis attempts to finish configuration, repair dependencies, refresh package lists and continue upgrades.
Understanding apt remove purge autoremove and clean
These apt commands remove different things.
| Command | Purpose |
|---|---|
sudo apt remove package | Remove installed package files |
sudo apt purge package | Remove package and system configuration files |
sudo apt autoremove | Remove unused dependency packages |
sudo apt clean | Clear downloaded package cache |
sudo apt autoclean | Remove obsolete package files from cache |
Example:
sudo apt remove nginxThis removes Nginx package files, but may leave configuration.
sudo apt purge nginxThis removes Nginx and its system configuration files.
sudo apt autoremoveThis removes packages installed as dependencies that are no longer needed.
sudo apt cleanThis frees space used by cached .deb files.
Check package cache size:
sudo du -sh /var/cache/aptClean it:
sudo apt cleanCheck again:
sudo du -sh /var/cache/aptThis can help on small systems with limited disk space.
dpkg database investigation
List installed packages:
dpkg -lSearch installed packages:
dpkg -l | grep nginxShow files installed by a package:
dpkg -L nginxFind which package owns a file:
dpkg -S /usr/sbin/nginxPackage file lists help answer questions like:
Where did this binary come from?
Which package installed this configuration file?
Which files belong to this package?
Is this command installed by the package I think it is?
Example:
command -v nginx
dpkg -S "$(command -v nginx)"This finds the executable path and then asks which package owns it.
Processes, services and system control
Processes, services and system control commands show what is running, what failed, which programs own resources and how long-running system components are managed.
date: working with system time in commands
The command date displays the current date and time.
dateCustom format:
date +%Y-%m-%dExample output:
2026-05-14Useful date formats:
| Command | Example output |
|---|---|
date +%Y-%m-%d | 2026-05-14 |
date +%H:%M:%S | 10:30:45 |
date +%Y-%m-%d-%H-%M | 2026-05-14-10-30 |
date +"%Y-%m-%d %H:%M:%S" | 2026-05-14 10:30:45 |
The date command becomes powerful when used in filenames.
tar -czf backup-$(date +%Y-%m-%d).tar.gz project/This creates a backup with the date in its name.
For logs:
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup started" >> backup.logThis appends a timestamped line into backup.log.
uptime: checking how long the system has been running
The command uptime shows how long the system has been running and gives load average information.
uptimeExample output:
10:42:11 up 5 days, 3:21, 2 users, load average: 0.18, 0.22, 0.20This output contains:
| Part | Meaning |
|---|---|
10:42:11 | Current system time |
up 5 days, 3:21 | System has been running for 5 days and 3 hours 21 minutes |
2 users | Number of logged-in users |
load average | System load over 1, 5 and 15 minutes |
Show boot time:
uptime -sExample:
2026-05-09 07:21:14Uptime is useful because some problems appear after restarts, while others appear after long runtime.
If a server rebooted unexpectedly, uptime -s can help confirm when.
System management begins with understanding services
A Linux system runs many background programs. These programs may provide networking, logging, scheduling, databases, web servers, SSH access, printing, monitoring or other functionality.
A background program of this kind is often called a daemon. The managed unit used to start, stop, restart or inspect it is commonly called a service.
Examples of services:
| Service | Typical purpose |
|---|---|
ssh | Remote shell access |
apache2 | Apache web server |
nginx | Nginx web server |
mysql | MySQL or MariaDB database service |
cron | Scheduled task service |
rsyslog | System logging service |
docker | Container engine |
php-fpm | PHP FastCGI process manager |
On many modern Linux distributions, services are managed by systemd using the systemctl command. Older or compatibility commands may use service.
Both forms are still seen in documentation.
Traditional style:
sudo service apache2 restartModern systemd style:
sudo systemctl restart apache2Both may work on many systems, but systemctl gives more detailed control and status information on systemd-based systems.
service command
The service command is a traditional way to manage services.
Start a service:
sudo service apache2 startStop a service:
sudo service apache2 stopRestart a service:
sudo service apache2 restartReload configuration:
sudo service apache2 reloadCheck status:
sudo service apache2 statusThe difference between restart and reload matters.
| Action | Meaning |
|---|---|
| Start | Start a stopped service |
| Stop | Stop a running service |
| Restart | Stop and start again |
| Reload | Reload configuration without full restart, if supported |
| Status | Show current service state |
Reload is usually less disruptive than restart, but not every service supports reload in the same way.
For example, after changing a web server configuration, you may try:
sudo service apache2 reloadIf a full restart is needed:
sudo service apache2 restartA safe configuration workflow is:
sudo apache2ctl configtest
sudo service apache2 reloadThe first command tests Apache configuration before applying changes.
systemctl command
The systemctl command is the primary management tool for systemd services.
Start a service:
sudo systemctl start apache2Stop a service:
sudo systemctl stop apache2Restart a service:
sudo systemctl restart apache2Reload configuration:
sudo systemctl reload apache2Check status:
systemctl status apache2Enable service at boot:
sudo systemctl enable apache2Disable service at boot:
sudo systemctl disable apache2Check whether service is enabled:
systemctl is-enabled apache2Check whether service is active:
systemctl is-active apache2Show failed services:
systemctl --failedThis is extremely useful after boot problems or service failures.
| Command | Purpose |
|---|---|
systemctl status service | Show service state and recent logs |
systemctl start service | Start now |
systemctl stop service | Stop now |
systemctl restart service | Restart now |
systemctl reload service | Reload configuration |
systemctl enable service | Start automatically at boot |
systemctl disable service | Do not start automatically at boot |
systemctl --failed | Show failed units |
The word enable does not mean start immediately. It means configure the service to start automatically during boot.
The word start means start now.
This distinction matters.
sudo systemctl enable nginxmakes Nginx start at boot.
sudo systemctl start nginxstarts Nginx immediately.
Often you want both:
sudo systemctl enable nginx
sudo systemctl start nginxor:
sudo systemctl enable --now nginxThe --now option enables and starts in one command.
update-rc.d and service startup on older systems
The original cheat sheet includes update-rc.d, which is used on older Debian-style init systems and compatibility layers.
Enable SSH at boot:
sudo update-rc.d ssh enableDisable SSH at boot:
sudo update-rc.d -f ssh removeOn modern systemd systems, the equivalent is usually:
sudo systemctl enable sshand:
sudo systemctl disable sshUnderstanding this helps when reading older tutorials. Many Linux guides remain useful, but commands evolve.
| Older command | Modern equivalent |
|---|---|
sudo service apache2 start | sudo systemctl start apache2 |
sudo service apache2 stop | sudo systemctl stop apache2 |
sudo service apache2 restart | sudo systemctl restart apache2 |
sudo service apache2 reload | sudo systemctl reload apache2 |
sudo update-rc.d ssh enable | sudo systemctl enable ssh |
sudo update-rc.d -f ssh remove | sudo systemctl disable ssh |
A serious Linux user should recognize both styles.
Checking service status properly
When a service is not working, do not restart it blindly forever. First inspect its status.
systemctl status apache2This may show:
| Information | Meaning |
|---|---|
| Loaded | Whether the service unit file exists and is loaded |
| Active | Whether the service is running, stopped or failed |
| Main PID | Main process ID |
| Tasks | Number of related processes |
| Memory | Memory used by the service |
| Logs | Recent service log messages |
If status shows failed, look at the log lines at the bottom. They often contain the real reason.
For deeper logs, use:
journalctl -u apache2Follow logs live:
journalctl -u apache2 -fShow recent logs:
journalctl -u apache2 -n 50Although journalctl was not one of the central commands in the PDF cheat sheet, it is essential on modern systemd systems because it reads the systemd journal.
A professional service troubleshooting workflow:
systemctl status apache2
journalctl -u apache2 -n 50
sudo apache2ctl configtest
sudo systemctl restart apache2
systemctl status apache2This reads status, checks logs, tests configuration, restarts and verifies.
Rebooting Linux safely
The command:
sudo rebootrestarts the system.
It is simple, but it should not be used casually on production machines. Rebooting disconnects users, stops services and interrupts running tasks.
Before rebooting a server, consider checking:
who
uptime
systemctl --failed
df -hThese commands show logged-in users, system runtime, failed services and disk space.
A more careful reboot workflow:
who
uptime
sudo rebootIf you are connected over SSH, your session will disconnect. Wait for the server to come back, then reconnect.
ssh user@serverAfter reconnecting:
uptime
systemctl --failedThis confirms the system booted and shows whether any services failed.
shutdown command
The command shutdown stops or restarts the system at a specific time.
Shut down immediately:
sudo shutdown -h nowShut down at 20:00:
sudo shutdown -h 20:00Reboot after one minute:
sudo shutdown -r +1Cancel a scheduled shutdown:
sudo shutdown -cOptions:
| Option | Meaning |
|---|---|
-h | Halt or power off |
-r | Reboot |
now | Immediately |
+10 | In 10 minutes |
20:00 | At 20:00 system time |
-c | Cancel scheduled shutdown |
A useful maintenance message:
sudo shutdown -r +10 "System will reboot for maintenance in 10 minutes"This warns logged-in users.
On personal machines, immediate reboot is usually fine. On servers, scheduled shutdown is more professional.
Process management: every running program has a process
A process is a running instance of a program. When you start a shell, open an editor, run a web server or execute a script, Linux represents that activity as one or more processes.
Processes have process IDs, owners, states, memory usage, CPU usage and parent-child relationships.
The main commands for basic process management are:
| Command | Purpose |
|---|---|
ps | Show process list |
ps aux | Show detailed process list |
top | Interactive real-time process viewer |
htop | More user-friendly process viewer |
kill | Send a signal to a process by PID |
killall | Send a signal to processes by name |
pgrep | Find process IDs by name |
pkill | Kill processes by name or pattern |
The PDF cheat sheet includes ps, kill, killall and htop. These are core commands for understanding what is running on a Linux system.
ps aux explained
The command:
ps auxshows running processes.
A simplified output may look like this:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 167000 12000 ? Ss May14 0:03 /sbin/init
www-data 842 0.1 1.5 300000 65000 ? S May14 1:20 apache2
pat 2510 0.0 0.1 12000 5000 pts/0 Ss 10:20 0:00 bash
pat 2601 0.0 0.1 15000 4000 pts/0 R+ 10:30 0:00 ps auxImportant columns:
| Column | Meaning |
|---|---|
USER | User owning the process |
PID | Process ID |
%CPU | CPU usage percentage |
%MEM | Memory usage percentage |
VSZ | Virtual memory size |
RSS | Physical memory currently used |
TTY | Terminal associated with process |
STAT | Process state |
START | Start time |
TIME | CPU time used |
COMMAND | Command that started the process |
Find processes related to Apache:
ps aux | grep apacheFind PHP processes:
ps aux | grep phpFind Nginx processes:
ps aux | grep nginxA common beginner question is why grep itself appears in results.
Example:
pat 3001 0.0 0.0 grep apacheThis happens because the grep apache command itself contains the word apache.
A common workaround:
ps aux | grep '[a]pache'This searches for apache without matching the grep command line in the same way.
Another modern tool:
pgrep -a apacheThis shows process IDs and command lines matching apache.
Process states
The STAT column in ps aux tells you the process state.
Common process states:
| State | Meaning |
|---|---|
R | Running or runnable |
S | Sleeping, waiting for an event |
D | Uninterruptible sleep, often waiting on I/O |
T | Stopped |
Z | Zombie process |
s | Session leader |
+ | Foreground process group |
A sleeping process is not necessarily a problem. Many services spend most of their time waiting for requests.
A zombie process is a process that has finished but still has an entry in the process table because its parent has not collected its exit status. Occasional zombies may appear briefly. Persistent zombies may indicate a parent process problem.
A process in uninterruptible sleep can be more serious, especially if related to disk or network filesystem issues.
For everyday administration, you do not need to memorize every process state immediately, but you should know that ps gives more than a list. It gives clues.
top and htop
The command top shows real-time system activity.
topIt displays CPU usage, memory usage, load average and active processes.
The command htop is a more user-friendly alternative.
htopIf it is not installed:
sudo apt update
sudo apt install htopWhy many administrators prefer htop:
| Feature | Benefit |
|---|---|
| Color display | Easier to read |
| Interactive scrolling | Easier navigation |
| Process tree view | Better understanding of relationships |
| Search function | Quickly locate processes |
| Kill from interface | Stop processes interactively |
| CPU and memory bars | Fast visual overview |
Inside htop, common keys include:
| Key | Action |
|---|---|
| Arrow keys | Move through process list |
F3 | Search |
F4 | Filter |
F5 | Tree view |
F6 | Sort |
F9 | Kill selected process |
F10 | Quit |
You should not kill processes randomly from htop. First identify what they are and why they are running.
A good process investigation workflow:
uptime
htop
ps aux | grep process-name
systemctl status service-nameThis connects system load, process view, specific process search and service status.
kill: stopping a process by PID
The command kill sends a signal to a process. Despite the name, it does not always mean immediate destruction. By default, it sends a termination signal asking the process to stop.
Basic syntax:
kill PIDExample:
kill 12345This sends the default TERM signal.
Force kill:
kill -9 12345The -9 signal is SIGKILL. It forces the kernel to stop the process. The process cannot clean up, save state or close files gracefully.
Use kill -9 only when normal termination fails.
A better sequence:
kill 12345
sleep 2
ps -p 12345
kill -9 12345This first asks the process to stop normally, checks whether it is still running and only then forces termination if needed.
Common signals:
| Signal | Number | Meaning |
|---|---|---|
SIGTERM | 15 | Ask process to terminate gracefully |
SIGKILL | 9 | Force process to stop immediately |
SIGHUP | 1 | Hangup, often used to reload configuration |
SIGINT | 2 | Interrupt, similar to Ctrl + C |
Send a named signal:
kill -TERM 12345Force kill:
kill -KILL 12345Reload-like signal for some processes:
kill -HUP 12345Whether HUP reloads configuration depends on the program.
killall: stopping processes by name
The command killall sends a signal to processes by name.
Example:
killall phpThis sends the default termination signal to processes named php.
Force kill:
killall -9 phpUse this carefully. It may stop more processes than intended.
A safer approach is to inspect first:
pgrep -a phpThen decide whether to use:
killall phpor kill a specific PID:
kill 12345Comparison:
| Command | Target |
|---|---|
kill 12345 | One process by PID |
killall php | All processes with the name php |
pkill pattern | Processes matching a pattern |
A common beginner mistake is using killall without realizing how broad it can be.
On a shared or production server, prefer precise process IDs unless you are certain.
Process control with Ctrl + C, Ctrl + Z, jobs, fg and bg
Not every process needs to be killed with kill. Some are controlled directly from the terminal.
Stop a running foreground command:
Ctrl + CThis sends an interrupt signal.
Suspend a foreground command:
Ctrl + ZThis pauses it and returns you to the shell.
Show background and stopped jobs:
jobsResume a stopped job in the foreground:
fgResume a stopped job in the background:
bgExample:
ping 8.8.8.8Press:
Ctrl + ZThen:
jobs
bgThe command continues in the background.
Bring it back:
fgStop it:
Ctrl + CThis is useful, but for long-running remote work, tools like screen or tmux are safer because they survive SSH disconnections.
Understanding logs in service management
Services often fail for reasons visible in logs. Linux logs may be stored in different places depending on the service and distribution.
Common locations:
| Location | Purpose |
|---|---|
/var/log/syslog | General system log on many Debian-based systems |
/var/log/auth.log | Authentication logs |
/var/log/apache2/ | Apache logs |
/var/log/nginx/ | Nginx logs |
/var/log/mysql/ | MySQL or MariaDB logs on some systems |
journalctl | Systemd journal logs |
Read recent system logs:
tail -n 100 /var/log/syslogFollow system logs:
sudo tail -f /var/log/syslogSearch errors:
grep -i "error" /var/log/syslogRead service logs with journalctl:
journalctl -u service-name -n 100Follow service logs live:
journalctl -u service-name -fA practical restart and watch workflow:
sudo systemctl restart nginx
journalctl -u nginx -fStop following with:
Ctrl + CLogs are often the difference between guessing and knowing.
The relationship between packages, services and processes
A strong Linux user understands that packages, services and processes are related but different.
| Layer | Example | Meaning |
|---|---|---|
| Package | apache2 package | Installed software files managed by package manager |
| Service | apache2.service | Managed background unit |
| Process | /usr/sbin/apache2 process | Currently running program instance |
| Port | :80 or :443 | Network endpoint used by service |
| Log | /var/log/apache2/error.log | Record of events and errors |
Installing a package does not always mean the service is running.
A service being enabled does not always mean it is currently active.
A process running does not always mean it is listening on the expected port.
A port listening does not always mean the application is healthy.
You need commands for each layer.
| Question | Command |
|---|---|
| Is the package installed? | `dpkg -l |
| Is the service active? | systemctl is-active apache2 |
| Is the service enabled at boot? | systemctl is-enabled apache2 |
| What processes are running? | `ps aux |
| Is the port listening? | `sudo ss -tulpn |
| What do logs say? | journalctl -u apache2 -n 50 |
This multi-layer thinking is essential for real troubleshooting.
Understanding foreground and background commands
When you run a command normally, it occupies the terminal until it finishes.
Example:
sleep 60The terminal waits for 60 seconds.
Run it in the background:
sleep 60 &The & symbol starts the command in the background.
Show jobs:
jobsBring a job to foreground:
fgBackground execution is useful for simple tasks, but it is not enough for important long-running remote tasks. If your SSH connection closes, background jobs may still end depending on how they were started.
For reliable long sessions, use:
screenor:
tmuxThe command screen is included in the original command set, and it remains useful for long-running administration work.
nohup for simple persistent commands
Another tool for keeping commands running after logout is nohup.
Example:
nohup ./long-script.sh > script.log 2>&1 &This runs the script in the background and redirects output into script.log.
Breakdown:
| Part | Meaning |
|---|---|
nohup | Ignore hangup signal after logout |
./long-script.sh | Command to run |
> | Redirect standard output |
script.log | Output file |
2>&1 | Redirect errors to the same place as output |
& | Run in background |
For beginners, screen is often easier because you can reattach and see the session interactively.
For scripts and automation, nohup can be useful.
watch: repeat a command automatically
The command watch repeatedly runs another command and displays the output.
Basic usage:
watch dateThis refreshes every 2 seconds by default.
Run every 10 seconds:
watch -n 10 dateWatch disk space:
watch -n 5 df -hWatch memory:
watch -n 5 free -hWatch a directory:
watch -n 2 ls -lahWatch listening ports:
watch -n 2 "ss -tulpn"Use quotes when the watched command contains spaces, pipes or complex syntax.
Example:
watch -n 2 "ps aux | grep apache"This repeats the full pipeline.
The watch command is useful during deployments, file transfers, log growth, service restarts and disk cleanup.
lsof: list open files
The command lsof means list open files.
lsofThis can produce a lot of output because Linux treats many things as files: regular files, directories, sockets, devices and network connections.
Find processes using a file:
lsof /path/to/fileFind what uses a port:
sudo lsof -i :80Find what uses port 443:
sudo lsof -i :443Find open deleted files:
sudo lsof | grep deletedThis is useful when disk space remains used after deleting large files.
Example workflow:
df -h
sudo lsof | grep deletedIf a large deleted file is still open by a process, restarting that process may release disk space.
Find network connections:
sudo lsof -iThe command lsof is advanced, but extremely valuable in troubleshooting.
netstat and ss
The command netstat appears in many older guides and cheat sheets. It shows network connections, routing tables and listening ports.
Show listening ports:
netstat -lShow listening ports with process information:
sudo netstat -lpShow continuously:
netstat -lpcOn many modern systems, ss is preferred.
Show listening TCP and UDP ports with process names:
sudo ss -tulpnBreakdown:
| Option | Meaning |
|---|---|
-t | TCP |
-u | UDP |
-l | Listening sockets |
-p | Show process |
-n | Numeric addresses and ports |
Check whether port 80 is listening:
sudo ss -tulpn | grep ':80'Check SSH port:
sudo ss -tulpn | grep ':22'netstat is still useful to recognize, but ss is usually the modern default.
dmesg: kernel messages
The command dmesg displays kernel ring buffer messages.
dmesgIt is useful for hardware, drivers, boot messages, disks, USB devices and kernel-level errors.
Search for errors:
dmesg | grep -i errorSearch for USB events:
dmesg | grep -i usbFollow kernel messages live:
sudo dmesg -wA practical use case: plug in a USB drive, then run:
dmesg | tail -n 30This may show which device name was assigned, such as /dev/sdb.
Another use case: disk problems.
dmesg | grep -i "I/O error"Kernel messages are not everyday reading for beginners, but they are essential when diagnosing hardware or low-level system problems.
Key takeaways
At this point, the guide moves from file-level work into system-level administration. The command families now cover software installation, system updates, services, processes, long-running sessions and active server diagnosis.
The key commands in this section are:
| Command | Essential role |
|---|---|
apt update | Refresh package lists |
apt upgrade | Upgrade installed packages |
apt install | Install packages |
apt remove | Remove packages |
apt purge | Remove packages and configuration |
apt search | Find packages |
apt show | Display package details |
dpkg -i | Install local .deb packages |
dpkg -l | List installed packages |
dpkg -L | Show files installed by a package |
dpkg -S | Find which package owns a file |
service | Traditional service management |
systemctl | Modern service management |
reboot | Restart the system |
shutdown | Shut down or schedule restart |
ps aux | List running processes |
htop | Interactive process monitoring |
kill | Stop process by PID |
killall | Stop processes by name |
screen | Keep terminal sessions alive |
history | Show command history |
watch | Repeat commands automatically |
lsof | List open files and ports |
netstat | Older network status tool |
ss | Modern socket and port inspection |
dmesg | Kernel and hardware messages |
The deeper lesson is that Linux system administration is layered.
A package installs software. A service manages background behavior. A process is the running instance. A port exposes network access. A log explains what happened. A permission controls access. A filesystem stores the data. A command lets you inspect each layer.
Once you see those layers clearly, troubleshooting becomes much more precise. You no longer ask only “Why does it not work?” You ask better questions.
Is the package installed?
Is the service enabled?
Is it active?
Did it fail?
What do the logs say?
Is the process running?
Is the port listening?
Is the disk full?
Are permissions correct?
Was a configuration file changed recently?
Every one of those questions has a command. That is the real power of Linux.
Practical workflow: monitor a command repeatedly
Use watch to repeat commands.
Watch disk usage:
watch -n 5 df -hWatch a directory during file transfer:
watch -n 2 "ls -lh /home/pat/downloads"Watch memory:
watch -n 5 free -hWatch top error count:
watch -n 10 "grep -i error app.log | wc -l"Watch listening ports:
watch -n 2 "ss -tulpn"The command must be quoted when it contains pipes or multiple words.
Without quotes, the shell may interpret only part of the command as the argument to watch.
Correct:
watch -n 10 "ps aux | grep nginx"Risky or wrong:
watch -n 10 ps aux | grep nginxThe second form pipes the output of watch, not the intended repeated command.
Practical workflow: use lsof to solve file and port mysteries
Find what is using port 80:
sudo lsof -i :80Find what is using port 443:
sudo lsof -i :443Find processes using a mounted directory before unmounting:
sudo lsof +D /mnt/usbThis can help when umount says the target is busy.
Find deleted files still open:
sudo lsof | grep deletedIf disk space is not freed after deleting a large log, this may reveal the process still holding the file open.
Example workflow:
df -h
sudo lsof | grep deleted
sudo systemctl restart service-name
df -hThis is a real-world troubleshooting pattern.
Practical workflow: use dmesg for hardware events
View kernel messages:
dmesgShow recent messages:
dmesg | tail -n 50Search USB events:
dmesg | grep -i usbSearch disk errors:
dmesg | grep -i "error"Follow live kernel messages:
sudo dmesg -wPractical USB workflow:
sudo dmesg -wThen plug in a USB drive and observe messages. Stop with:
Ctrl + CThen check block devices:
lsblkThis helps identify whether the USB device appeared as /dev/sdb, /dev/sdc or another name.
Practical workflow: combine dmesg, lsblk and mount
Plug in a USB drive. Then run:
dmesg | tail -n 30
lsblkCreate mount point:
sudo mkdir -p /mnt/usbMount the partition:
sudo mount /dev/sdb1 /mnt/usbCheck:
df -h /mnt/usb
ls -lah /mnt/usbUnmount safely:
sudo umount /mnt/usbIf unmount fails because the device is busy:
sudo lsof +D /mnt/usbClose processes using it, change out of the directory if your shell is inside it, then try again.
cd ~
sudo umount /mnt/usbThis is a complete hardware-to-filesystem workflow.
Practical workflow: inspect a systemd service file
Show service file:
systemctl cat nginxShow properties:
systemctl show nginxShow selected property:
systemctl show nginx -p ExecStartShow dependencies:
systemctl list-dependencies nginxThis helps explain how a service starts and what it depends on.
If you edit or add systemd unit files, reload systemd:
sudo systemctl daemon-reloadThen restart the service:
sudo systemctl restart service-nameDo not forget daemon-reload after changing unit files. Otherwise, systemd may continue using old unit information.
The difference between reload and restart
Many services support both reload and restart.
Reload usually means: read configuration again without fully stopping the service.
Restart means: stop and start the service.
| Action | Effect | Risk |
|---|---|---|
| Reload | Apply configuration with less interruption | May not apply all changes |
| Restart | Fully restarts service | More disruptive |
| Stop | Stops service | Causes downtime |
| Start | Starts stopped service | Used after stop or failure |
Examples:
sudo systemctl reload nginxsudo systemctl restart nginxBefore either one, test configuration if possible.
sudo nginx -tFor Apache:
sudo apache2ctl configtestFor SSH:
sudo sshd -tPrefer reload when supported and appropriate. Use restart when the service needs a full restart or reload is not enough.
Understanding system load
The uptime command shows load averages.
uptimeExample:
10:42:11 up 5 days, 3:21, 2 users, load average: 0.18, 0.22, 0.20The three load numbers represent 1-minute, 5-minute and 15-minute averages.
Load is not exactly CPU percentage. It represents processes running or waiting for CPU and sometimes uninterruptible I/O.
Interpreting load depends on CPU core count.
Check CPU count:
nprocIf a system has 1 CPU core, a load of 1.00 may mean it is fully loaded. If it has 8 cores, a load of 1.00 is usually light.
Basic interpretation:
| Load compared with CPU cores | Meaning |
|---|---|
| Much lower than core count | Usually fine |
| Around core count | Busy but possibly normal |
| Much higher than core count | Potential overload |
| High load with low CPU | Possible I/O wait problem |
Use htop or top to see more.
htopIf load is high, check CPU consumers:
ps aux --sort=-%cpu | head -15Check memory:
free -hCheck disk I/O symptoms through logs:
dmesg | grep -i errorLoad is a clue, not a conclusion.
Memory and swap basics
Check memory:
free -hExample output:
total used free shared buff/cache available
Mem: 7.7Gi 2.1Gi 1.0Gi 120Mi 4.6Gi 5.2Gi
Swap: 2.0Gi 0.0Gi 2.0GiImportant fields:
| Field | Meaning |
|---|---|
total | Total memory |
used | Used memory |
free | Completely unused memory |
buff/cache | Memory used for buffers and cache |
available | Estimated memory available for applications |
Swap | Disk-based memory extension |
Linux uses free memory for cache. This is normal. Do not panic just because free memory looks low. The available column is usually more useful.
Check swap devices:
swapon --showHigh swap usage may indicate memory pressure, but some swap usage is not always a problem.
Find memory-heavy processes:
ps aux --sort=-%mem | head -15Use htop for interactive inspection.
htopMemory troubleshooting should ask whether usage is expected. Databases, search engines, Java applications and caches may use large amounts of memory by design.
journalctl is the modern log reader for systemd systems
Many modern Linux systems use systemd. The logs managed by systemd are read with journalctl.
Show recent logs:
journalctl -n 100Follow logs live:
journalctl -fShow logs for one service:
journalctl -u nginxShow recent service logs:
journalctl -u nginx -n 100Follow service logs:
journalctl -u nginx -fShow logs since today:
journalctl --since todayShow logs since a specific time:
journalctl --since "2026-05-14 10:00"Show logs in a time range:
journalctl --since "2026-05-14 10:00" --until "2026-05-14 11:00"Show only errors:
journalctl -p err -n 100Priority levels include:
| Priority | Meaning |
|---|---|
emerg | System is unusable |
alert | Immediate action required |
crit | Critical condition |
err | Error |
warning | Warning |
notice | Normal but significant |
info | Informational |
debug | Debug messages |
For troubleshooting, -p err and -p warning are very useful.
journalctl -p warning -n 100This shows warning-level and more severe messages.
journalctl for boot analysis
Show logs from the current boot:
journalctl -bShow logs from the previous boot:
journalctl -b -1List boots:
journalctl --list-bootsThis is useful after unexpected reboots or startup failures.
Example workflow after a server rebooted unexpectedly:
uptime
journalctl --list-boots
journalctl -b -1 -p warningThis checks current uptime, lists boot history and shows warnings or errors from the previous boot.
If a service failed during boot:
systemctl --failed
journalctl -b -u service-nameThis reads logs for that service from the current boot.
journal disk usage
The systemd journal can consume disk space.
Check journal size:
journalctl --disk-usageVacuum old journal logs by time:
sudo journalctl --vacuum-time=14dVacuum by size:
sudo journalctl --vacuum-size=1GUse carefully. Logs are evidence. Do not remove them during an active investigation unless necessary.
Persistent journal configuration is often controlled under:
/etc/systemd/journald.confAfter changing journald configuration:
sudo systemctl restart systemd-journaldA practical disk cleanup workflow may include:
journalctl --disk-usage
sudo journalctl --vacuum-time=14d
journalctl --disk-usageThis checks size before and after cleanup.
systemctl is more than start and stop
Many users learn only these commands:
sudo systemctl start service
sudo systemctl stop service
sudo systemctl restart serviceBut systemctl can do much more.
Show status:
systemctl status nginxShow whether active:
systemctl is-active nginxShow whether enabled:
systemctl is-enabled nginxEnable and start immediately:
sudo systemctl enable --now nginxDisable and stop immediately:
sudo systemctl disable --now nginxShow failed units:
systemctl --failedReload systemd after unit changes:
sudo systemctl daemon-reloadShow a service unit file:
systemctl cat nginxShow service properties:
systemctl show nginxShow dependencies:
systemctl list-dependencies nginxList running services:
systemctl list-units --type=service --state=runningList all service unit files:
systemctl list-unit-files --type=serviceThe difference between a unit being active and enabled is important.
| State | Meaning |
|---|---|
| Active | Running now |
| Enabled | Configured to start at boot |
| Disabled | Not configured to start at boot |
| Failed | Tried to run but failed |
| Masked | Prevented from being started |
Mask a service:
sudo systemctl mask service-nameUnmask:
sudo systemctl unmask service-nameMasking is stronger than disabling. A masked service cannot be started until unmasked.
Creating a simple systemd service
Cron is useful for scheduled jobs. But for long-running background programs, a systemd service is often better.
Suppose you have a script:
/home/pat/scripts/worker.shMake it executable:
chmod +x /home/pat/scripts/worker.shCreate a service file:
sudo nano /etc/systemd/system/myworker.serviceContent:
[Unit]
Description=My custom worker
After=network.target
[Service]
Type=simple
User=pat
WorkingDirectory=/home/pat/scripts
ExecStart=/home/pat/scripts/worker.sh
Restart=on-failure
[Install]
WantedBy=multi-user.targetReload systemd:
sudo systemctl daemon-reloadStart service:
sudo systemctl start myworkerCheck status:
systemctl status myworkerEnable at boot:
sudo systemctl enable myworkerView logs:
journalctl -u myworker -fThis converts a script into a managed background service.
Important service fields:
| Field | Meaning |
|---|---|
Description | Human-readable service description |
After | Start ordering |
User | User account running the process |
WorkingDirectory | Directory where process starts |
ExecStart | Command to run |
Restart | Restart policy |
WantedBy | Boot target for enabling |
Systemd services are more appropriate than cron for programs that should stay running continuously.
systemd targets explain system states
Systemd uses targets to group system states.
List targets:
systemctl list-units --type=targetCommon targets:
| Target | Meaning |
|---|---|
multi-user.target | Normal non-graphical multi-user mode |
graphical.target | Graphical mode |
rescue.target | Single-user rescue mode |
emergency.target | Minimal emergency shell |
network.target | Basic network target |
timers.target | Timer units active |
Check default target:
systemctl get-defaultSet default target to multi-user:
sudo systemctl set-default multi-user.targetSet default target to graphical:
sudo systemctl set-default graphical.targetSwitch target temporarily:
sudo systemctl isolate multi-user.targetBe careful with isolate. It can stop services not part of the target.
For normal administration, you mostly need to know that targets define boot states and service grouping.
uname and kernel information
The command uname shows system and kernel information.
unameShow all information:
uname -aShow kernel release:
uname -rShow machine architecture:
uname -mExamples:
| Command | Purpose |
|---|---|
uname | Kernel name |
uname -a | All available uname information |
uname -r | Kernel release |
uname -m | Machine architecture |
Kernel version matters for drivers, modules, containers, security updates and compatibility.
After kernel updates, a reboot may be needed before the new kernel is active.
Check running kernel:
uname -rCheck whether reboot is recommended on many Debian-based systems:
ls /var/run/reboot-requiredIf the file exists, plan a reboot.
lsmod and kernel modules
Kernel modules are pieces of code loaded into the kernel, often for drivers or filesystems.
List loaded modules:
lsmodSearch for a module:
lsmod | grep module-nameShow module information:
modinfo module-nameLoad a module:
sudo modprobe module-nameUnload a module:
sudo modprobe -r module-nameThese commands are more advanced and should be used carefully. Removing the wrong module can affect hardware or network functionality.
Practical example: checking whether a virtualization or filesystem module is loaded.
lsmod | grep overlayThis may matter for container systems.
Most users do not manually manage modules often, but knowing how to inspect them helps with hardware and container troubleshooting.
sysctl and kernel parameters
The sysctl command reads and changes kernel parameters at runtime.
Show all parameters:
sysctl -aShow one parameter:
sysctl net.ipv4.ip_forwardEnable IP forwarding temporarily:
sudo sysctl -w net.ipv4.ip_forward=1This runtime change may not survive reboot.
Persistent sysctl settings are usually placed under:
/etc/sysctl.conf
/etc/sysctl.d/Example:
sudo nano /etc/sysctl.d/99-custom.confContent:
net.ipv4.ip_forward=1Apply:
sudo sysctl --systemBe careful. Kernel parameters can affect networking, memory, security and performance.
A safe workflow:
sysctl parameter.name
sudo sysctl -w parameter.name=value
sysctl parameter.nameFor persistent changes, document why the parameter is needed.
Environment files for services
Systemd services can use environment files. These files define variables used by the service.
Show a service file:
systemctl cat service-nameLook for lines like:
EnvironmentFile=/etc/default/service-nameor:
Environment="NAME=value"Inspect environment file:
cat /etc/default/service-nameEdit carefully:
sudo cp /etc/default/service-name /etc/default/service-name.$(date +%Y-%m-%d-%H-%M).backup
sudo nano /etc/default/service-nameRestart service:
sudo systemctl restart service-nameCheck logs:
journalctl -u service-name -n 100Environment variables are often used to configure applications without changing code. They may include ports, paths, runtime modes or feature flags.
Be careful with secrets. Environment variables can sometimes be visible through process inspection depending on permissions and system configuration.
Working directory matters in services and scripts
A script may work when you run it from one directory and fail from another.
Example script:
#!/bin/bash
cat config.txtIf you run it from the directory containing config.txt, it works. If cron or systemd runs it from another directory, it fails.
Better:
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cat "$SCRIPT_DIR/config.txt"Or use absolute paths:
cat /home/pat/myapp/config.txtIn systemd service files, set:
WorkingDirectory=/home/pat/myappThen relative paths inside the application start from that directory.
In cron, avoid relative paths unless the script explicitly changes directory.
cd /home/pat/myapp || exit 1Then run commands.
The current directory is an invisible dependency. Make it explicit.
Understanding signals beyond kill -9
The kill command sends signals. The default signal is usually SIGTERM, which asks a process to terminate gracefully.
Show available signals:
kill -lCommon signals:
| Signal | Number | Meaning |
|---|---|---|
SIGHUP | 1 | Hangup, often reload configuration |
SIGINT | 2 | Interrupt, like Ctrl + C |
SIGTERM | 15 | Graceful termination request |
SIGKILL | 9 | Immediate forced termination |
SIGSTOP | 19 or system-dependent | Stop process |
SIGCONT | 18 or system-dependent | Continue stopped process |
Graceful stop:
kill PIDExplicit graceful stop:
kill -TERM PIDForce stop:
kill -KILL PIDor:
kill -9 PIDDo not use kill -9 as the first option. It prevents cleanup. Files may remain incomplete. Locks may remain. Temporary state may not be cleaned.
For managed services, prefer:
sudo systemctl stop service-nameor:
sudo systemctl restart service-nameSystemd understands the service better than a random PID kill.
Process trees explain parent and child relationships
Processes can start other processes. This creates a process tree.
Show process tree with ps:
ps auxfInstall and use pstree if available:
sudo apt install psmisc
pstree -pThe -p option shows PIDs.
A web server may have a master process and worker processes. A shell may start scripts. A service manager may start a daemon.
Why process trees matter:
| Situation | Why tree helps |
|---|---|
| Killing parent may stop children | Useful or dangerous depending on context |
| Worker processes belong to master | Restart service instead of killing workers |
| Script started many child commands | Need to understand full activity |
| Zombie process exists | Parent relationship matters |
| High CPU child process | Need to know what started it |
Example:
ps auxf | grep nginxThis may show a master Nginx process and worker processes.
Do not kill worker processes randomly if the service manager should handle them.
Nice and process priority
Linux processes have scheduling priority. The nice value influences CPU scheduling.
Show nice values:
ps -eo pid,ni,comm | headRun a command with lower priority:
nice -n 10 tar -czf backup.tar.gz /large/sourceHigher nice value means lower priority.
Renice a running process:
sudo renice 10 -p PIDThis can be useful for CPU-heavy tasks such as compression or batch processing.
Do not use priority changes as a substitute for proper capacity planning, but they can reduce impact of background jobs.
Example backup with lower CPU priority:
nice -n 10 tar -czf backup.tar.gz /var/www/siteCombine with I/O priority if ionice is available:
ionice -c2 -n7 nice -n 10 tar -czf backup.tar.gz /var/www/siteThis tries to make the backup less disruptive.
Understanding PID files
A PID file contains a process ID. Services sometimes use PID files to record which process is running.
Common locations:
/run/service-name.pid
/var/run/service-name.pid/var/run is often a link to /run.
Read PID file:
cat /run/service-name.pidCheck process:
ps -p "$(cat /run/service-name.pid)" -fIf the PID file points to a process that no longer exists, it may be stale.
Systemd often manages processes without requiring traditional PID files, but many services and scripts still use them.
Storage, disks, mounts and filesystem usage
Storage troubleshooting starts with free space, directory usage, mounts, persistent mount configuration and temporary data.
Disk space with df
The command df shows free and used space on mounted filesystems.
Basic command:
dfHuman-readable output:
df -hThe -h option means human-readable. It shows sizes in KB, MB, GB or TB instead of raw blocks.
Example output:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 31G 17G 65% /
/dev/sdb1 200G 120G 80G 60% /mnt/dataImportant columns:
| Column | Meaning |
|---|---|
Filesystem | Device or filesystem source |
Size | Total size |
Used | Used space |
Avail | Available space |
Use% | Percentage used |
Mounted on | Where it appears in the filesystem |
Check a specific path:
df -h /varCheck the filesystem containing your current directory:
df -h .The command df answers this question: how full is the filesystem?
It does not tell you which folder is responsible. For that, use du.
Disk usage with du
The command du shows how much space files and directories use.
Basic command:
duThis can produce a lot of output.
Human-readable summary of current directory:
du -sh .Human-readable summary of a specific directory:
du -sh /home/patShow sizes of immediate items inside a directory:
du -h --max-depth=1 /home/patSort by size:
du -h --max-depth=1 /home/pat | sort -hShow largest items at the bottom:
du -h --max-depth=1 /home/pat | sort -hShow largest items at the top with numeric kilobyte sorting:
du -ak /home/pat | sort -nr | head -20The command from the cheat sheet style is especially useful:
du -ak | sort -nr | head -20This finds the largest items under the current directory.
| Command part | Purpose |
|---|---|
du -ak | Show disk usage in kilobytes for all items |
sort -nr | Sort numerically from largest to smallest |
head -20 | Show only the first 20 lines |
A common disk investigation workflow:
df -h
du -sh /*
du -h --max-depth=1 /var | sort -h
du -h --max-depth=1 /var/log | sort -hThis starts broad and narrows down.
The difference between df and du is important.
| Command | Answers |
|---|---|
df -h | Which filesystem is full? |
du -sh folder | How much space does this folder use? |
du -h --max-depth=1 | Which subfolder is large? |
Use df first to identify the full filesystem. Use du next to find what is using space.
Why df and du may disagree
Sometimes df says a filesystem is full, but du does not show enough files to explain the usage. This can happen when a deleted file is still held open by a running process.
Linux allows a process to keep writing to a file even after the file name has been removed from the directory. The file disappears from normal listings, but the disk space is not released until the process closes it.
You can investigate this with lsof.
sudo lsof | grep deletedIf a huge deleted log file is still open, restarting the related service may release the space.
Example workflow:
df -h
sudo lsof | grep deleted
sudo systemctl restart service-name
df -hThis is an advanced but very real Linux troubleshooting situation.
Mounting and unmounting filesystems
Linux does not use drive letters like C: or D:. A disk or partition becomes accessible by being mounted somewhere in the filesystem tree.
The command mount attaches a filesystem to a directory.
The command umount detaches it.
Mount a partition:
sudo mount /dev/sda1 /mnt/usbUnmount it:
sudo umount /mnt/usbThe command is umount, not unmount.
The directory /mnt/usb is called a mount point. It must exist before mounting.
sudo mkdir -p /mnt/usb
sudo mount /dev/sda1 /mnt/usbCheck mounted filesystems:
mountMore readable disk overview:
lsblkExample output:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 50G 0 disk
└─sda1 8:1 0 50G 0 part /
sdb 8:16 1 32G 0 disk
└─sdb1 8:17 1 32G 0 part /mnt/usbThe command lsblk is very useful before mounting because it shows available block devices.
A safe USB mounting workflow:
lsblk
sudo mkdir -p /mnt/usb
sudo mount /dev/sdb1 /mnt/usb
df -h /mnt/usb
ls -lah /mnt/usbBefore unplugging or removing a mounted device:
sudo umount /mnt/usbUnmounting ensures pending writes are completed and the filesystem is detached safely.
Practical workflow: investigate a full disk
A real Linux administrator often starts with a warning: disk usage is high. The goal is not to delete something quickly. The goal is to find the cause safely.
Start with filesystems:
df -hFind which filesystem is full.
If / is full, inspect top-level directories:
sudo du -h --max-depth=1 / | sort -hIf /var is large:
sudo du -h --max-depth=1 /var | sort -hIf /var/log is large:
sudo du -h --max-depth=1 /var/log | sort -hFind large log files:
sudo find /var/log -type f -size +100M -exec ls -lh {} \;Check compressed old logs:
sudo find /var/log -type f -iname "*.gz" -mtime +30 -exec ls -lh {} \;Only after review, remove old compressed logs:
sudo find /var/log -type f -iname "*.gz" -mtime +30 -deleteCheck space again:
df -hIf space is still not freed, check deleted open files:
sudo lsof | grep deletedThis workflow shows professional Linux thinking: identify, narrow down, verify, act, verify again.
Disk usage and inode usage
Disk space has two important dimensions: blocks and inodes.
Check block usage:
df -hCheck inode usage:
df -iA filesystem can have free space but no free inodes. This happens when there are too many small files.
If inodes are full, commands may fail with:
No space left on deviceeven if df -h shows available space.
Find directories with many files:
sudo find /var -xdev -type f | cut -d / -f 2,3 | sort | uniq -c | sort -nr | head -20This estimates where many files are located under /var.
Find many small session files, cache files or temporary files.
sudo find /var -type f -size -4k | wc -lDo not delete randomly. Identify the application creating excessive files, then fix retention, cache cleanup or application behavior.
Practical workflow: inspect mounted filesystems deeply
Show block devices:
lsblkShow filesystems:
lsblk -fShow mounted filesystems:
findmntShow one mount:
findmnt /mnt/usbCheck disk free:
df -hCheck filesystem type:
df -ThExample output:
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 ext4 50G 20G 28G 42% /The filesystem type matters. Permissions, ownership, symlinks and special features behave differently across filesystems such as ext4, xfs, btrfs, nfs, cifs, vfat or exfat.
For example, FAT-based filesystems do not support Unix permissions in the same way as ext4.
If permissions behave strangely on a mounted drive, check filesystem type.
lsblk -f
df -Th
mount | grep /mnt/usbfstab controls persistent mounts
The file:
/etc/fstabdefines filesystems that should mount automatically.
View it:
cat /etc/fstabBefore editing, back it up:
sudo cp /etc/fstab /etc/fstab.$(date +%Y-%m-%d-%H-%M).backupEdit:
sudo nano /etc/fstabTest without rebooting:
sudo mount -aIf mount -a returns errors, fix them before rebooting. A broken fstab can cause boot problems.
A typical line may look like:
UUID=xxxx-xxxx /mnt/data ext4 defaults 0 2Fields:
| Field | Meaning |
|---|---|
| Device or UUID | What to mount |
| Mount point | Where to mount |
| Filesystem type | ext4, xfs, nfs, etc. |
| Options | Mount options |
| Dump | Backup flag, often 0 |
| Pass | Filesystem check order |
Use UUIDs instead of device names when possible because /dev/sdb1 can change between boots.
Show UUIDs:
blkidPersistent mounts are powerful, but mistakes can affect boot. Always test with mount -a.
Practical workflow: safe external disk setup
Plug in disk.
Inspect:
lsblk
lsblk -f
dmesg | tail -n 30Create mount point:
sudo mkdir -p /mnt/dataMount manually:
sudo mount /dev/sdb1 /mnt/dataCheck:
df -h /mnt/data
ls -lah /mnt/dataFind UUID:
sudo blkid /dev/sdb1Back up fstab:
sudo cp /etc/fstab /etc/fstab.backupEdit fstab:
sudo nano /etc/fstabAdd carefully:
UUID=your-uuid-here /mnt/data ext4 defaults,nofail 0 2Test:
sudo umount /mnt/data
sudo mount -a
df -h /mnt/dataThe nofail option can prevent boot failure if an external disk is missing, depending on the use case.
Understanding temporary directories
Linux has multiple temporary locations.
| Directory | Typical behavior |
|---|---|
/tmp | Temporary files, often cleaned on reboot or by policy |
/var/tmp | Temporary files that may persist longer |
/run | Runtime files, cleared on reboot |
| User cache | Usually under ~/.cache |
Check /tmp:
du -sh /tmpFind old files:
sudo find /tmp -type f -mtime +7 -exec ls -lh {} \;Do not blindly run:
sudo rm -rf /tmp/*Some running programs may be using files there.
A safer cleanup:
sudo find /tmp -type f -mtime +7 -delete
sudo find /tmp -type d -empty -deleteStill use caution. Temporary does not always mean safe to delete right now.
Networking, SSH and remote file transfer
Networking, SSH and remote transfer commands make Linux useful across servers, hosting environments and production systems.
Networking in Linux begins with understanding interfaces
Networking in Linux starts with a simple question: how does this machine connect to the outside world?
A Linux system can have one network interface or many. A laptop may have Wi-Fi, Ethernet, Bluetooth networking and virtual interfaces created by VPNs or containers. A server may have one public interface, one private interface, loopback, bridge interfaces, Docker networks, VLANs or bonded interfaces. A Raspberry Pi may have Ethernet, Wi-Fi and USB networking. The command line gives you a precise way to see all of them.
A network interface is the system’s connection point to a network. It may be physical, such as an Ethernet card, or virtual, such as a bridge used by containers.
Common interface names include:
| Interface name | Typical meaning |
|---|---|
lo | Loopback interface used by the system to talk to itself |
eth0 | Traditional name for the first Ethernet interface |
wlan0 | Traditional name for the first Wi-Fi interface |
enp0s3 | Predictable modern Ethernet interface name |
ens33 | Another common modern Ethernet interface name |
wlp2s0 | Predictable modern Wi-Fi interface name |
docker0 | Docker bridge interface |
br0 | Bridge interface |
tun0 | Tunnel interface, often used by VPNs |
The loopback interface lo is always important. It usually uses the IP address:
127.0.0.1and the hostname:
localhostThis means the machine is communicating with itself. Many local services listen on loopback when they should be available only locally.
The first practical networking skill is learning how to inspect interfaces and addresses.
ip: the modern command for network inspection
The modern Linux command for network information is ip.
Show all network addresses:
ip aor the longer form:
ip addressTypical output may include interface names, MAC addresses, IPv4 addresses, IPv6 addresses and interface states.
A simplified example:
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
link/ether 08:00:27:12:34:56 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.50/24 brd 192.168.1.255 scope global dynamic enp0s3
inet6 fe80::a00:27ff:fe12:3456/64 scope linkThis output contains several important pieces of information.
| Field | Meaning |
|---|---|
enp0s3 | Interface name |
UP | Interface is enabled |
LOWER_UP | Physical or link-layer connection is active |
link/ether | MAC address line |
inet | IPv4 address |
192.168.1.50/24 | IPv4 address with subnet prefix |
brd | Broadcast address |
dynamic | Address was likely assigned by DHCP |
inet6 | IPv6 address |
The /24 part is CIDR notation. It describes the subnet size. In many home and office networks, /24 corresponds to a subnet mask of:
255.255.255.0This means addresses such as:
192.168.1.1
192.168.1.50
192.168.1.200are usually in the same local network if they share the same /24 network.
Show only IPv4 addresses:
ip -4 aShow only IPv6 addresses:
ip -6 aShow a specific interface:
ip a show enp0s3Bring an interface up:
sudo ip link set enp0s3 upBring an interface down:
sudo ip link set enp0s3 downShow routing table:
ip routeExample output:
default via 192.168.1.1 dev enp0s3
192.168.1.0/24 dev enp0s3 proto kernel scope link src 192.168.1.50This tells you two important things.
| Route | Meaning |
|---|---|
default via 192.168.1.1 | Traffic to other networks goes through gateway 192.168.1.1 |
192.168.1.0/24 dev enp0s3 | Local network is reachable through interface enp0s3 |
The default gateway is essential. If your IP address is correct but there is no default route, the machine may communicate locally but not reach the internet.
ifconfig: the older network command
The command ifconfig is older and may not be installed by default on many modern distributions. It is still common in older tutorials, embedded systems and some minimal environments.
Basic use:
ifconfigShow a specific interface:
ifconfig eth0If the command is missing, the system may show:
ifconfig: command not foundOn Debian-based systems, it can often be installed with:
sudo apt update
sudo apt install net-toolsHowever, the modern replacement is usually:
ip aComparison:
| Older command | Modern equivalent |
|---|---|
ifconfig | ip a |
ifconfig eth0 | ip a show eth0 |
route -n | ip route |
netstat -tulpn | ss -tulpn |
A serious Linux user should recognize ifconfig, but should become comfortable with ip.
Understanding private and public IP addresses
When inspecting network configuration, you will often see private IP addresses.
Common private IPv4 ranges are:
| Range | Typical use |
|---|---|
10.0.0.0/8 | Large private networks |
172.16.0.0/12 | Medium private networks |
192.168.0.0/16 | Home and small office networks |
Examples:
192.168.1.50
10.0.0.25
172.16.5.10These addresses are not directly reachable from the public internet. They are used inside private networks.
A server may also have a public IP address if it is directly reachable from the internet. In cloud environments, the public IP may be assigned externally by the provider, while the operating system sees only a private address. That depends on the infrastructure.
You can inspect local addresses with:
ip aYou can inspect routes with:
ip routeDo not confuse local IP configuration with public internet identity. They may be different, especially behind NAT, routers, cloud load balancers or VPNs.
ping: testing basic connectivity
The command ping tests whether another host responds to ICMP echo requests.
Basic use:
ping 192.168.1.1This sends packets continuously until stopped with:
Ctrl + CSend only four packets:
ping -c 4 192.168.1.1Test a domain:
ping example.comTest a public IP address:
ping -c 4 8.8.8.8The output may look like this:
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=1.25 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=1.10 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=1.18 msImportant fields:
| Field | Meaning |
|---|---|
64 bytes | Size of response |
from 192.168.1.1 | Host that replied |
icmp_seq | Packet sequence number |
ttl | Time to live |
time | Round-trip latency |
At the end, ping prints statistics.
4 packets transmitted, 4 received, 0% packet lossPacket loss is important. High packet loss indicates network instability.
| Result | Meaning |
|---|---|
0% packet loss | Good basic connectivity |
50% packet loss | Unstable connection |
100% packet loss | No ICMP response or blocked ICMP |
| Very high latency | Slow or distant connection |
| Unknown host | DNS resolution problem |
| Network unreachable | Routing or interface problem |
However, ping is not a complete test. Some servers block ICMP. A failed ping does not always mean the server is down. It may only mean ICMP is blocked.
A better diagnostic approach uses several tests.
ip a
ip route
ping -c 4 192.168.1.1
ping -c 4 8.8.8.8
ping -c 4 example.comThis sequence checks local configuration, route, gateway reachability, internet IP reachability and DNS resolution.
Diagnosing internet connectivity step by step
When internet connectivity fails, do not guess. Test in layers.
First, check whether the system has an IP address:
ip aThen check whether it has a default route:
ip routeThen ping the gateway:
ping -c 4 192.168.1.1Replace 192.168.1.1 with your actual gateway from ip route.
Then ping an external IP:
ping -c 4 8.8.8.8Then ping a domain:
ping -c 4 example.comInterpretation:
| What works | What fails | Likely issue |
|---|---|---|
| No IP address | Everything | DHCP, interface or cable/Wi-Fi problem |
| IP exists, no default route | Internet access | Routing problem |
| Gateway ping fails | External access | Local network problem |
| Gateway works, external IP fails | Internet routing, firewall or ISP problem | |
| External IP works, domain fails | DNS problem | |
| Domain works | Basic internet connectivity is working |
Check DNS configuration:
cat /etc/resolv.confOn many modern systems, DNS may be managed by systemd-resolved, NetworkManager or another service, so /etc/resolv.conf may point to a local stub resolver. Still, it is often useful for inspection.
A practical DNS test tool is getent.
getent hosts example.comIf this returns an IP address, name resolution is working through the system resolver.
hostname: identifying the machine
The command hostname displays the system hostname.
hostnameExample output:
webserver01The hostname is the system’s local name. It may appear in prompts, logs, monitoring systems and network identification.
Set hostname temporarily:
sudo hostname webserver01On modern systems, use:
sudo hostnamectl set-hostname webserver01Show detailed hostname information:
hostnamectlExample output may include static hostname, operating system, kernel and architecture.
A good hostname should be meaningful.
| Poor hostname | Better hostname |
|---|---|
server | web-prod-01 |
ubuntu | db-staging-01 |
test | backup-lab-01 |
raspberrypi | sensor-garage-01 |
Naming matters because logs and monitoring become much easier to understand when machines have clear names.
A hostname should usually indicate function, environment and number if there are multiple machines.
Examples:
web-prod-01
web-prod-02
db-prod-01
web-staging-01
backup-office-01iwconfig and wireless inspection
The command iwconfig shows wireless interface information. It is older, but still useful on systems where wireless tools are available.
Basic use:
iwconfigShow one interface:
iwconfig wlan0The output may include SSID, mode, frequency, access point, bit rate and signal quality.
Important fields:
| Field | Meaning |
|---|---|
ESSID | Wi-Fi network name |
Mode | Managed, monitor or other wireless mode |
Frequency | Wireless frequency |
Access Point | MAC address of the access point |
Bit Rate | Link speed |
Signal level | Wireless signal strength |
On many modern systems, Wi-Fi is managed through tools such as NetworkManager, nmcli, iw or desktop network managers. Still, iwconfig appears in many Linux learning materials and remains useful to recognize.
A practical wireless troubleshooting sequence:
ip a
iwconfig
ip route
ping -c 4 192.168.1.1This checks whether the wireless interface exists, whether it has wireless association information, whether routing exists and whether the gateway responds.
ifup and ifdown
The commands ifup and ifdown enable or disable network interfaces using traditional network configuration systems.
Enable an interface:
sudo ifup eth0Disable an interface:
sudo ifdown eth0These commands are most relevant on systems using /etc/network/interfaces style configuration. On systems using NetworkManager, systemd-networkd or cloud network tools, they may not apply or may not behave as expected.
Modern alternatives may include:
sudo ip link set eth0 up
sudo ip link set eth0 downor NetworkManager commands such as:
nmcli connection showThe key principle is that network management depends on the distribution and configuration stack. The command line helps inspect what is present.
If ifup and ifdown fail, do not assume the interface is broken. The system may be managed by a different network service.
wget: downloading files from the terminal
The command wget downloads files from URLs.
Basic use:
wget http://example.com/file.txtDownload and save with a different filename:
wget http://example.com/file.txt -O target.txtThe -O option means write output to a specific file.
Download into the current directory using the remote filename:
wget https://example.com/archive.tar.gzContinue an interrupted download:
wget -c https://example.com/large-file.isoDownload quietly:
wget -q https://example.com/file.txtDownload with visible progress but less output:
wget -q --show-progress https://example.com/file.txtUseful wget options:
| Option | Meaning |
|---|---|
-O file | Save as a specific filename |
-c | Continue interrupted download |
-q | Quiet mode |
--show-progress | Show progress in quiet mode |
-P directory | Save into directory |
--limit-rate=1m | Limit download speed |
Save into a directory:
wget -P downloads https://example.com/file.zipLimit download speed:
wget --limit-rate=1m https://example.com/large-file.isoThis can be useful on slow connections or shared servers.
A safe download workflow:
mkdir -p ~/downloads
cd ~/downloads
wget https://example.com/archive.tar.gz
ls -lh
tar -tzf archive.tar.gz | headThis downloads into a controlled directory, checks the file size and inspects archive contents before extraction.
curl compared with wget
Although the original command list focuses on wget, many Linux systems also use curl.
Basic curl use:
curl https://example.comSave to file:
curl -o file.txt https://example.com/file.txtFollow redirects:
curl -L -o file.txt https://example.com/file.txtShow only HTTP headers:
curl -I https://example.comComparison:
| Task | wget | curl |
|---|---|---|
| Download a file simply | Very convenient | Possible |
| Save remote file with same name | Default behavior | Needs option in many cases |
| Test HTTP APIs | Less common | Very common |
| Show headers | Possible | Very convenient |
| Use in scripts | Good | Excellent |
| Resume downloads | wget -c | curl -C - |
For downloading files, wget is very comfortable. For API testing and HTTP inspection, curl is often preferred.
A Linux user should know both, but wget remains one of the easiest download tools.
ssh: remote login to Linux systems
The command ssh connects to another system securely over the network.
Basic syntax:
ssh user@hostExample:
ssh pat@192.168.1.10This means: connect to host 192.168.1.10 as user pat.
Connect to a hostname:
ssh pat@webserver01Connect using a specific port:
ssh -p 2222 pat@192.168.1.10The default SSH port is 22, but servers may use a different port.
When connecting for the first time, SSH may ask whether you trust the host key.
Are you sure you want to continue connecting?This protects against connecting to an unexpected machine. In professional environments, host keys should be verified carefully.
After successful connection, you receive a shell on the remote machine. Commands then run on that remote system, not your local computer.
This distinction is critical.
If you SSH into a server and run:
rm file.txtyou are deleting file.txt on the server, not on your local machine.
A safe habit after logging in:
hostname
whoami
pwdThis confirms where you are, which machine you are on and which user you are using.
SSH identity and who you are on the remote system
After logging in through SSH, verify identity.
whoamiShow hostname:
hostnameShow current directory:
pwdShow logged-in users:
whoShow recent login information:
last | headThis matters because administrators often manage multiple servers. Accidentally running a command on production instead of staging can cause serious damage.
A helpful prompt or clear hostname reduces risk. If hostnames are unclear, fix them.
sudo hostnamectl set-hostname web-staging-01A professional SSH habit:
hostname
whoami
uptimeThis immediately tells you where you are, who you are and how long the system has been running.
SSH keys
SSH can authenticate with passwords or keys. SSH keys are usually safer and more convenient when managed properly.
A key pair contains:
| Part | Purpose |
|---|---|
| Private key | Stays on your local machine and must be protected |
| Public key | Can be placed on servers you want to access |
Generate an SSH key:
ssh-keygenA common modern form:
ssh-keygen -t ed25519This creates keys usually under:
~/.ssh/Common files:
| File | Meaning |
|---|---|
~/.ssh/id_ed25519 | Private key |
~/.ssh/id_ed25519.pub | Public key |
~/.ssh/authorized_keys | Public keys allowed to log into this account |
~/.ssh/known_hosts | Known server host keys |
~/.ssh/config | Optional client configuration |
The private key must be protected.
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519If permissions are too open, SSH may refuse to use the key.
Copy a public key to a server:
ssh-copy-id pat@192.168.1.10Then connect:
ssh pat@192.168.1.10If ssh-copy-id is not available, you can manually append the public key to the remote user’s authorized_keys file, but that requires care.
SSH client configuration
If you connect to servers often, create an SSH config file.
Open:
nano ~/.ssh/configExample:
Host webprod
HostName 192.168.1.10
User pat
Port 22
IdentityFile ~/.ssh/id_ed25519Then connect using:
ssh webprodThis is easier than typing the full command every time.
More examples:
Host staging
HostName staging.example.local
User deploy
Port 2222
Host backup
HostName 10.0.0.50
User backupuser
IdentityFile ~/.ssh/backup_keySet secure permissions:
chmod 600 ~/.ssh/configSSH config improves productivity and reduces mistakes.
Running one remote command with ssh
SSH can run one command on a remote server without opening an interactive shell.
ssh pat@192.168.1.10 "uptime"Check disk space remotely:
ssh pat@192.168.1.10 "df -h"Check failed services:
ssh pat@192.168.1.10 "systemctl --failed"Run multiple commands:
ssh pat@192.168.1.10 "hostname; uptime; df -h"This is useful for quick administration and scripting.
Example for multiple servers:
for server in web1 web2 web3; do
echo "Checking $server"
ssh "$server" "hostname; uptime; df -h /"
doneThis shows how SSH becomes an automation tool.
scp: copying files over SSH
The command scp copies files between machines using SSH.
Copy a local file to a remote server:
scp test.txt pat@192.168.1.10:/home/pat/Copy a remote file to the local machine:
scp pat@192.168.1.10:/home/pat/test.txt .The dot . means current local directory.
Copy and rename:
scp test.txt pat@192.168.1.10:/home/pat/remote-test.txtCopy a directory recursively:
scp -r project pat@192.168.1.10:/home/pat/Use a specific SSH port:
scp -P 2222 test.txt pat@192.168.1.10:/home/pat/Important detail: ssh uses lowercase -p for port, but scp uses uppercase -P.
| Command | Port option |
|---|---|
ssh | -p |
scp | -P |
This difference is a common source of mistakes.
rsync: efficient synchronization
The command rsync copies and synchronizes files. It is often better than scp for repeated transfers, backups and large directories because it can transfer only changed parts.
Basic local synchronization:
rsync -av source/ destination/Copy local directory to remote server:
rsync -avz project/ pat@192.168.1.10:/home/pat/project/Copy remote directory to local machine:
rsync -avz pat@192.168.1.10:/home/pat/project/ ./project/Common options:
| Option | Meaning |
|---|---|
-a | Archive mode, preserves permissions, timestamps and structure |
-v | Verbose output |
-z | Compress data during transfer |
-r | Recursive |
-u | Skip files newer on destination |
--delete | Delete destination files not present in source |
--dry-run | Show what would happen without changing anything |
--progress | Show transfer progress |
A safe rsync preview:
rsync -avz --dry-run project/ pat@192.168.1.10:/home/pat/project/If the preview looks correct:
rsync -avz project/ pat@192.168.1.10:/home/pat/project/This is a professional habit. Use --dry-run before major synchronization.
The trailing slash in rsync matters
One of the most important rsync details is the trailing slash.
Compare:
rsync -av project destination/and:
rsync -av project/ destination/They are different.
| Command | Result |
|---|---|
rsync -av project destination/ | Copies the directory project into destination |
rsync -av project/ destination/ | Copies the contents of project into destination |
Example:
rsync -av project backup/Result:
backup/project/file.txtExample:
rsync -av project/ backup/Result:
backup/file.txtThis distinction is critical when deploying websites, synchronizing backups or updating server directories.
When unsure, use:
rsync -av --dry-run source/ destination/Inspect the output before running the real command.
rsync with delete
The --delete option makes the destination match the source by deleting files from the destination that no longer exist in the source.
Example:
rsync -av --delete source/ destination/This is powerful and dangerous.
Use dry run first:
rsync -av --delete --dry-run source/ destination/Only if the planned deletions are correct:
rsync -av --delete source/ destination/A deployment example:
rsync -avz --delete --dry-run ./site/ deploy@server:/var/www/site/Then:
rsync -avz --delete ./site/ deploy@server:/var/www/site/The --delete option is useful when the destination should be an exact mirror. It is dangerous when the destination contains files that are not present in the source but should be preserved.
Choosing between scp and rsync
Both scp and rsync copy files over SSH, but they serve different practical needs.
| Situation | Better tool |
|---|---|
| Copy one small file quickly | scp |
| Copy a directory once | scp or rsync |
| Synchronize a directory repeatedly | rsync |
| Back up changed files | rsync |
| Preserve permissions and timestamps | rsync -a |
| Preview changes before transfer | rsync --dry-run |
| Delete destination files not in source | rsync --delete |
| Transfer over a custom SSH port | Both can do it |
Use scp for simple transfers. Use rsync for serious synchronization.
Example simple file upload:
scp index.html deploy@server:/var/www/site/Example serious deployment:
rsync -avz --delete --dry-run ./site/ deploy@server:/var/www/site/
rsync -avz --delete ./site/ deploy@server:/var/www/site/The second workflow is safer because you can preview the result.
Practical workflow: diagnose a server that cannot reach the internet
Suppose you are logged into a Linux server and it cannot download updates.
Start with IP addresses:
ip aCheck default route:
ip routeTry the gateway:
ping -c 4 192.168.1.1Use the actual gateway from ip route.
Try external IP:
ping -c 4 8.8.8.8Try domain resolution:
ping -c 4 deb.debian.orgCheck DNS:
getent hosts deb.debian.org
cat /etc/resolv.confTry apt update:
sudo apt updateInterpretation:
| Result | Likely area |
|---|---|
| No interface IP | DHCP or interface problem |
| No default route | Routing problem |
| Gateway unreachable | Local network problem |
| External IP unreachable | Upstream network or firewall |
| Domain fails but IP works | DNS problem |
| Only apt fails | Repository, proxy, TLS or package source issue |
This sequence prevents random changes. It identifies the failing layer.
Practical workflow: deploy a static website with rsync
Assume your local website build is in:
./public/and the remote web directory is:
/var/www/example.com/Preview deployment:
rsync -avz --delete --dry-run ./public/ deploy@server:/var/www/example.com/If the preview is correct:
rsync -avz --delete ./public/ deploy@server:/var/www/example.com/Then check remote files:
ssh deploy@server "ls -lah /var/www/example.com | head"If the service needs reload:
ssh deploy@server "sudo systemctl reload nginx"This workflow is fast, repeatable and safer than manual FTP-style copying.
Important details:
| Detail | Why it matters |
|---|---|
./public/ has trailing slash | Copies contents of public, not the folder itself |
--delete mirrors destination | Removes old files no longer present locally |
--dry-run previews changes | Prevents accidental deletion |
| SSH command reloads server | Keeps workflow scriptable |
Practical workflow: copy logs from a remote server for analysis
Create a local directory:
mkdir -p ~/server-logsCopy logs with scp:
scp pat@server:/var/log/syslog ~/server-logs/If permission is denied, you may need to read logs remotely with sudo and save them to a location your user can access.
On the server:
sudo cp /var/log/syslog /home/pat/syslog-copy
sudo chown pat:pat /home/pat/syslog-copyThen locally:
scp pat@server:/home/pat/syslog-copy ~/server-logs/Analyze locally:
grep -i "error" ~/server-logs/syslog-copy
grep -i "failed" ~/server-logs/syslog-copy
tail -n 100 ~/server-logs/syslog-copyA more direct approach:
ssh pat@server "sudo tail -n 200 /var/log/syslog"For longer analysis, copying logs locally can be more convenient.
Practical workflow: monitor a remote service
Check status:
ssh pat@server "systemctl status nginx"Show recent logs:
ssh pat@server "journalctl -u nginx -n 50"Follow logs interactively:
ssh pat@server "journalctl -u nginx -f"Stop with:
Ctrl + CWatch remote disk space:
watch -n 10 'ssh pat@server "df -h /"'This runs the remote check every 10 seconds.
For multiple servers:
for server in web1 web2 web3; do
echo "$server"
ssh "$server" "hostname; uptime; df -h /"
doneThis kind of loop is a simple but powerful administration tool.
Practical workflow: check whether a port is open locally
Suppose a web server should listen on port 80.
Check listening ports:
sudo ss -tulpnFilter port 80:
sudo ss -tulpn | grep ':80'Filter port 443:
sudo ss -tulpn | grep ':443'Example output:
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=842,fd=6))This tells you:
| Field | Meaning |
|---|---|
tcp | TCP socket |
LISTEN | Waiting for incoming connections |
0.0.0.0:80 | Listening on all IPv4 addresses, port 80 |
nginx | Process name |
pid=842 | Process ID |
If a service is running but not listening, check its configuration and logs.
systemctl status nginx
journalctl -u nginx -n 50If another process is using the port:
sudo lsof -i :80Then decide whether the conflict is expected or needs correction.
Practical workflow: check whether a remote port is reachable
From your local machine or another server, you can test TCP connectivity with tools such as nc if installed.
nc -vz server 80Check HTTPS:
nc -vz server 443If nc is not installed:
sudo apt install netcat-openbsdYou can also use curl for HTTP or HTTPS services.
curl -I http://serverFor HTTPS:
curl -I https://serverInterpretation:
| Test | What it proves |
|---|---|
ss -tulpn on server | Service is listening locally |
curl -I localhost on server | Local HTTP response works |
curl -I http://server from another machine | Network path and service work externally |
nc -vz server 80 | TCP port is reachable |
ping server | ICMP responds, but does not prove port availability |
A common mistake is relying only on ping. A server can respond to ping while the web port is closed. A server can also block ping while the web port is open.
Practical workflow: troubleshoot SSH connection failure
If SSH fails, read the error carefully.
Try verbose mode:
ssh -v pat@serverMore verbose:
ssh -vvv pat@serverCommon errors:
| Error | Possible cause |
|---|---|
Connection refused | SSH service not running or wrong port |
Connection timed out | Firewall, routing or host unreachable |
Permission denied | Wrong user, password or key |
Host key verification failed | Host key mismatch |
Could not resolve hostname | DNS or hostname problem |
Check whether server is reachable:
ping -c 4 serverCheck port:
nc -vz server 22Try correct port:
ssh -p 2222 pat@serverOn the server, if you have another access method, check SSH service:
systemctl status ssh
sudo ss -tulpn | grep ':22'
journalctl -u ssh -n 50On some distributions, the service may be named sshd instead of ssh.
systemctl status sshdCheck SSH configuration:
sudo sshd -tIf sshd -t returns no output, the configuration syntax is usually valid.
Practical workflow: safely edit SSH configuration
Editing SSH configuration on a remote server is risky because a mistake can lock you out.
First, keep your current SSH session open.
Back up configuration:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backupEdit:
sudo nano /etc/ssh/sshd_configTest syntax:
sudo sshd -tIf valid, reload rather than restart when possible:
sudo systemctl reload sshor:
sudo systemctl reload sshdOpen a second terminal and test a new SSH login before closing the original session.
ssh pat@serverIf the new login works, the change is safe. If not, restore from the still-open original session.
sudo cp /etc/ssh/sshd_config.backup /etc/ssh/sshd_config
sudo sshd -t
sudo systemctl reload sshThis workflow is professional because it preserves a recovery path.
Practical network checklist
When a Linux machine has network problems, use this checklist:
hostname
ip a
ip route
ping -c 4 127.0.0.1
ping -c 4 <gateway>
ping -c 4 8.8.8.8
getent hosts example.com
ping -c 4 example.comReplace <gateway> with the actual gateway from:
ip routeThen inspect listening services if the problem is inbound access:
sudo ss -tulpnCheck firewall rules if relevant:
sudo ufw statusOn systems using UFW, this shows whether the firewall is active and which ports are allowed.
For service logs:
journalctl -u service-name -n 100This checklist is not random. It moves from local identity to interface, route, local stack, gateway, internet, DNS, inbound ports and logs.
Basic firewall awareness with ufw
Many Debian and Ubuntu systems may use UFW, the uncomplicated firewall. It is not part of the original command set, but it is directly relevant to network troubleshooting.
Check status:
sudo ufw statusVerbose status:
sudo ufw status verboseAllow SSH:
sudo ufw allow sshAllow HTTP:
sudo ufw allow 80/tcpAllow HTTPS:
sudo ufw allow 443/tcpEnable firewall:
sudo ufw enableImportant warning: before enabling a firewall on a remote server, make sure SSH is allowed.
sudo ufw allow ssh
sudo ufw enableOtherwise, you may lock yourself out.
Check numbered rules:
sudo ufw status numberedDelete a rule by number:
sudo ufw delete 3Firewall troubleshooting must be careful because blocking access can make remote administration impossible.
Name resolution and /etc/hosts
Linux can resolve names through DNS and local files.
The file:
/etc/hostscan define local hostname mappings.
View it:
cat /etc/hostsExample:
127.0.0.1 localhost
192.168.1.10 webserver01
192.168.1.20 dbserver01This means the system can resolve webserver01 to 192.168.1.10 locally.
Edit carefully:
sudo nano /etc/hostsTest resolution:
getent hosts webserver01The /etc/hosts file is useful for small labs, local development, temporary migrations and internal names. For larger environments, DNS is usually better.
A practical local lab setup:
192.168.56.10 web-lab
192.168.56.11 db-lab
192.168.56.12 backup-labThen:
ssh pat@web-labThis is easier than remembering IP addresses.
Network file transfer safety
Network file transfer can damage systems if source and destination are confused. Always verify direction.
The structure of scp is:
scp source destinationUpload local to remote:
scp local.txt user@server:/remote/path/Download remote to local:
scp user@server:/remote/path/file.txt .The structure of rsync is similar:
rsync options source destinationUpload:
rsync -av local-folder/ user@server:/remote/folder/Download:
rsync -av user@server:/remote/folder/ local-folder/Before large transfers:
rsync -av --dry-run source destinationCheck these before pressing Enter:
| Check | Why |
|---|---|
| Source path | Prevents copying wrong data |
| Destination path | Prevents overwriting wrong location |
| Trailing slash | Changes rsync behavior |
--delete | Can remove destination files |
| User and host | Prevents deploying to wrong server |
| Current directory | Important when using relative paths |
A precise command is better than a fast command.
Practical workflow: complete remote maintenance session
Start local terminal:
ssh pat@serverConfirm identity:
hostname
whoami
uptimeStart screen:
screen -S maintenanceCheck system:
df -h
free -h
systemctl --failedUpdate package lists:
sudo apt updateReview upgrades:
apt list --upgradableUpgrade:
sudo apt upgradeClean unused dependencies:
sudo apt autoremoveCheck services:
systemctl --failedCheck whether reboot is recommended:
ls /var/run/reboot-requiredIf reboot is needed and acceptable:
sudo rebootAfter reconnecting:
ssh pat@server
uptime
systemctl --failedThis workflow is structured, recoverable and safer than running updates casually.
Practical workflow: migrate files between two servers
If your local machine has access to both servers, you can copy from server A to local, then local to server B.
Download from source:
rsync -avz source-user@source-server:/var/www/site/ ./site-copy/Upload to destination:
rsync -avz --dry-run ./site-copy/ dest-user@dest-server:/var/www/site/If preview is correct:
rsync -avz ./site-copy/ dest-user@dest-server:/var/www/site/Verify destination:
ssh dest-user@dest-server "du -sh /var/www/site; find /var/www/site -maxdepth 2 -type f | head"If permissions need adjustment:
ssh dest-user@dest-server "sudo find /var/www/site -type d -exec chmod 755 {} \;"
ssh dest-user@dest-server "sudo find /var/www/site -type f -exec chmod 644 {} \;"This is a basic migration pattern. For databases, services and live applications, additional steps are required, but the file transfer logic remains fundamental.
Practical workflow: backup a remote directory with rsync
Create local backup directory:
mkdir -p ~/server-backups/sitePreview backup:
rsync -avz --dry-run pat@server:/var/www/site/ ~/server-backups/site/Run backup:
rsync -avz pat@server:/var/www/site/ ~/server-backups/site/Create a compressed archive of the local backup:
tar -czf ~/server-backups/site-$(date +%Y-%m-%d).tar.gz -C ~/server-backups siteVerify archive:
tar -tzf ~/server-backups/site-$(date +%Y-%m-%d).tar.gz | head -30This combines SSH, rsync, date, tar and verification.
Practical workflow: synchronize only changed files
Suppose you have a large directory that changes daily. scp -r would copy everything again. rsync copies only what changed.
rsync -avz /home/pat/Documents/ pat@backup-server:/backups/pat/Documents/Run it again tomorrow. Only changed files need to be transferred.
Add progress:
rsync -avz --progress /home/pat/Documents/ pat@backup-server:/backups/pat/Documents/Add deletion if the backup should mirror the source exactly:
rsync -avz --delete --dry-run /home/pat/Documents/ pat@backup-server:/backups/pat/Documents/Then:
rsync -avz --delete /home/pat/Documents/ pat@backup-server:/backups/pat/Documents/Use --delete only when mirror behavior is intended.
Practical workflow: identify which service owns a port
Suppose port 8080 is in use.
Check with ss:
sudo ss -tulpn | grep ':8080'Check with lsof:
sudo lsof -i :8080Example output may show a process name and PID.
Then inspect process:
ps -p 12345 -fIf it is a systemd service, try:
systemctl status service-nameIf you do not know the service, inspect the command path:
readlink -f /proc/12345/exeShow process working directory:
readlink -f /proc/12345/cwdShow environment carefully:
sudo tr '\0' '\n' < /proc/12345/environThis is more advanced, but it shows how Linux exposes process information through /proc.
Practical workflow: server cannot be reached from outside
If a service works locally but not externally, test in layers.
On the server:
ip a
ip route
systemctl status nginx
sudo ss -tulpn | grep ':80'
curl -I http://localhost
sudo ufw statusFrom another machine:
ping -c 4 server-ip
nc -vz server-ip 80
curl -I http://server-ipInterpretation:
| Local result | Remote result | Likely issue |
|---|---|---|
| Service not listening locally | Remote fails | Service or configuration problem |
Service listens on 127.0.0.1 only | Remote fails | Binding problem |
Service listens on 0.0.0.0:80, local curl works | Remote fails | Firewall, router, cloud security group or network path |
| Remote port works, browser fails by domain | DNS or virtual host problem | |
| IP works but HTTPS fails | TLS, certificate or port 443 configuration |
A listening address matters.
| Listening address | Meaning |
|---|---|
127.0.0.1:80 | Only local machine can connect |
0.0.0.0:80 | All IPv4 interfaces |
[::]:80 | IPv6 all interfaces, may also accept IPv4 depending on configuration |
192.168.1.50:80 | Only that specific interface address |
If a service listens only on 127.0.0.1, external clients cannot reach it.
Practical workflow: check download speed and stability with wget
Download a test file:
wget -O /tmp/test-download.bin https://example.com/large-file.binRemove after testing:
rm -i /tmp/test-download.binIf the connection drops, test resume:
wget -c -O /tmp/test-download.bin https://example.com/large-file.binLimit rate:
wget --limit-rate=500k -O /tmp/test-download.bin https://example.com/large-file.binThis is useful when downloads consume too much bandwidth on a shared server.
Practical workflow: use SSH and screen for safe long transfers
Connect:
ssh pat@serverStart screen:
screen -S transferRun rsync or tar:
rsync -avz --progress /large/source/ backup@backup-server:/backups/source/Detach:
Ctrl + A
Ctrl + DLater, reconnect:
ssh pat@server
screen -r transferThis prevents losing visibility if your SSH connection drops.
Key takeaways
The command-line foundation for Linux networking and remote administration is now in place.
The key commands are:
| Command | Essential role |
|---|---|
ip a | Show network interfaces and addresses |
ip route | Show routing table |
ifconfig | Older interface inspection command |
iwconfig | Older wireless inspection command |
ifup | Bring interface up using traditional configuration |
ifdown | Bring interface down using traditional configuration |
ping | Test basic reachability |
hostname | Show or set system name |
hostnamectl | Modern hostname and system identity tool |
wget | Download files from the terminal |
curl | Test URLs, APIs and HTTP headers |
ssh | Secure remote login |
scp | Simple file copy over SSH |
rsync | Efficient file synchronization |
ss | Inspect listening ports and sockets |
lsof -i | Find processes using network ports |
getent hosts | Test system name resolution |
ufw | Manage simple firewall rules on systems using UFW |
The deeper lesson is that networking must be diagnosed in layers.
First, check whether the machine has an interface and IP address. Then check whether it has a route. Then check whether the gateway responds. Then test external IP connectivity. Then test DNS. Then check whether the local service is listening. Then check whether remote clients can reach it. Then inspect firewall rules, logs and service configuration.
Remote administration adds another layer of responsibility. With SSH, scp and rsync, you can manage systems from anywhere. That power requires careful habits: verify the host, verify the user, preview destructive actions, keep backups, use screen for long tasks and test before restarting critical services.
A Linux system becomes much easier to manage when you understand how files, services, processes and networks connect. The terminal gives you visibility into every layer.
Practical workflow: check open network ports after deployment
After deploying a service, check listening ports:
sudo ss -tulpnFilter expected port:
sudo ss -tulpn | grep ':8080'Check process:
sudo lsof -i :8080Check local response:
curl -I http://localhost:8080Check service status:
systemctl status service-nameCheck logs:
journalctl -u service-name -n 100If the service listens on 127.0.0.1:8080, it is local-only. If it listens on 0.0.0.0:8080, it accepts connections on all IPv4 interfaces.
This distinction is critical for remote access.
Practical workflow: check SSH key permissions
SSH is strict about private key permissions.
Check .ssh directory:
ls -ld ~/.sshCheck private key:
ls -l ~/.ssh/id_ed25519Recommended permissions:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pubCheck authorized keys on server:
ls -l ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keysIf SSH refuses a key, run verbose mode:
ssh -v user@serverLook for lines about which keys are offered and why authentication fails.
Practical workflow: secure file transfer for backups
Create a backup user on the backup server.
sudo adduser backupCreate backup directory.
sudo mkdir -p /backups/server1
sudo chown backup:backup /backups/server1From the source server, generate key if needed.
ssh-keygen -t ed25519Copy key.
ssh-copy-id backup@backup-serverTest.
ssh backup@backup-server "hostname; pwd"Run rsync backup.
rsync -avz --delete /var/www/site/ backup@backup-server:/backups/server1/site/Preview first:
rsync -avz --delete --dry-run /var/www/site/ backup@backup-server:/backups/server1/site/Use a dedicated backup user rather than full administrative root login where possible. Limit access according to the backup design.
Practical workflow: restore from rsync backup
Preview backup contents:
rsync -avz --dry-run backup@backup-server:/backups/server1/site/ /tmp/site-restore/Restore to test directory first:
mkdir -p /tmp/site-restore
rsync -avz backup@backup-server:/backups/server1/site/ /tmp/site-restore/Inspect:
tree -L 2 /tmp/site-restore
du -sh /tmp/site-restoreRestore to real location only when sure:
sudo rsync -avz /tmp/site-restore/ /var/www/site/Fix ownership and permissions if needed:
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;Then verify service:
sudo nginx -t
sudo systemctl reload nginx
curl -I http://localhostRestoration should be tested in a safe directory before touching production paths.
Automation, shell scripting and scheduled tasks
Automation turns tested command sequences into repeatable tools. History, cron, scripts, aliases, loops and timers help convert one-time work into reliable procedures.
screen: keeping terminal sessions alive
The command screen creates a virtual terminal session that can continue running even if you disconnect.
Install screen:
sudo apt update
sudo apt install screenStart a named session:
screen -S backupNow run a long command:
tar -czf big-backup.tar.gz /home/pat/projectDetach from the session:
Ctrl + A
Ctrl + DList screen sessions:
screen -lsResume session:
screen -r backupExit a screen session after the work is done:
Ctrl + Dor type:
exitWhy screen matters:
| Situation | Why screen helps |
|---|---|
| Long backups | Continue after SSH disconnect |
| Large file transfers | Avoid losing process when connection drops |
| Remote updates | Keep session stable |
| Monitoring logs | Leave a live session running |
| Slow scripts | Detach and return later |
A practical remote workflow:
ssh pat@server
screen -S maintenance
sudo apt update
sudo apt upgradeIf the SSH connection drops, reconnect and run:
screen -r maintenanceThis is much safer than running critical long tasks in a normal SSH session.
Command history
The shell stores commands you have typed. The history command displays them.
historyShow the last 20 commands:
history | tail -n 20Search history:
history | grep aptRun the previous command again:
!!A very common use:
apt updateIf you forgot sudo, run:
sudo !!This becomes:
sudo apt updateRun a command by history number:
!123Be careful with this because history numbers change as new commands are added.
Delete one history entry:
history -d 123Clear history:
history -cHistory is useful for productivity, auditing your own work and repeating complex commands. It is also sensitive because commands may contain paths, server names or accidentally typed secrets.
Avoid typing passwords directly into commands because they may be stored in shell history.
Command history is your memory in the terminal
The command history shows commands previously typed in the shell.
historyThe output usually shows numbered commands.
1001 pwd
1002 ls -lah
1003 sudo apt update
1004 grep -i "error" /var/log/syslogYou can show the last 20 commands:
history | tail -n 20You can search your command history:
history | grep aptYou can search for previous SSH commands:
history | grep sshYou can search for previous rsync operations:
history | grep rsyncThis is useful because many real commands are long. Instead of retyping them, you can find and reuse them.
The shell also supports command repetition shortcuts.
| Shortcut | Meaning |
|---|---|
!! | Run the previous command again |
sudo !! | Run the previous command again with sudo |
!123 | Run command number 123 from history |
!grep | Run the most recent command beginning with grep |
!?error | Run the most recent command containing error |
The most famous example is forgetting sudo.
You type:
apt updateThe system refuses because administrative privileges are required. Then you run:
sudo !!The shell expands it to:
sudo apt updateThis is convenient, but it must be used carefully. Repeating commands blindly can be dangerous, especially after deletion, permission or remote commands.
Before using history expansion on destructive commands, inspect the command first.
history | tail -n 10Then decide whether it is safe to repeat.
History can also be a security risk
Shell history is useful, but it can accidentally store sensitive information.
Bad practice:
mysql -u root -pMySecretPasswordThis places the password directly in shell history.
Better practice:
mysql -u root -pThis prompts for the password without storing it in the command line.
Another bad practice:
curl -H "Authorization: Bearer secret-token-value" https://api.example.comThis may store a token in history.
If sensitive data was typed accidentally, you can delete a specific history line.
First find it:
history | grep secretThen delete by number:
history -d 1234Clear all history:
history -cHowever, clearing history does not solve every security problem. The command may already have been written to disk, captured by terminal logging, stored in process lists or recorded elsewhere. The better habit is to avoid placing secrets directly in command lines.
Sensitive values should be entered through prompts, protected files, environment mechanisms or secret management systems appropriate to the environment.
crontab schedules commands automatically
The command crontab manages scheduled tasks for a user.
List current cron jobs:
crontab -lEdit cron jobs:
crontab -eA cron line has five time fields followed by a command.
* * * * * commandThe fields are:
| Field | Meaning | Values |
|---|---|---|
| Minute | Minute of the hour | 0-59 |
| Hour | Hour of the day | 0-23 |
| Day of month | Day number | 1-31 |
| Month | Month number | 1-12 |
| Day of week | Weekday | 0-7 |
Both 0 and 7 can mean Sunday on many systems.
Examples:
| Cron expression | Meaning |
|---|---|
* * * * * | Every minute |
*/5 * * * * | Every 5 minutes |
0 * * * * | Every hour at minute 0 |
0 2 * * * | Every day at 02:00 |
30 3 * * 1 | Every Monday at 03:30 |
0 0 1 * * | At midnight on the first day of each month |
0 4 * * 0 | Every Sunday at 04:00 |
A daily backup at 02:00:
0 2 * * * /usr/bin/bash /home/pat/scripts/backup.shCron runs commands in a limited environment. This is very important. A command that works in your interactive shell may fail in cron because environment variables, working directory or PATH are different.
Use absolute paths in cron.
Better:
0 2 * * * /usr/bin/bash /home/pat/scripts/backup.shRiskier:
0 2 * * * bash backup.shThe second version depends on the current directory and PATH.
Cron environment and logging
Cron jobs should write logs. Otherwise, failures may be invisible.
Example cron line:
0 2 * * * /usr/bin/bash /home/pat/scripts/backup.sh >> /home/pat/logs/backup.log 2>&1This appends both output and errors to backup.log.
Breakdown:
| Part | Meaning |
|---|---|
0 2 * * * | Run daily at 02:00 |
/usr/bin/bash | Use Bash explicitly |
/home/pat/scripts/backup.sh | Script path |
>> /home/pat/logs/backup.log | Append normal output |
2>&1 | Send errors to the same log |
Make sure the log directory exists:
mkdir -p /home/pat/logsA script can also log internally.
#!/bin/bash
LOG="/home/pat/logs/backup.log"
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup started" >> "$LOG"
tar -czf /home/pat/backups/site-$(date +%Y-%m-%d).tar.gz /var/www/site >> "$LOG" 2>&1
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup finished" >> "$LOG"This makes cron behavior easier to audit.
Testing cron jobs safely
Do not put a complex script into cron without testing it manually.
Step one: run the script manually.
/usr/bin/bash /home/pat/scripts/backup.shStep two: check exit status.
echo $?Exit status 0 usually means success. Nonzero usually means failure.
Step three: check logs.
tail -n 50 /home/pat/logs/backup.logStep four: test with a near-time cron schedule.
For example, run every minute temporarily:
* * * * * /usr/bin/bash /home/pat/scripts/backup.sh >> /home/pat/logs/backup.log 2>&1After confirming it works, change to the real schedule.
A common mistake is leaving a test cron job running every minute. Always check:
crontab -lSystem cron directories
Besides user crontabs, Linux systems may have system-wide cron locations.
Common locations:
| Location | Purpose |
|---|---|
/etc/crontab | System-wide crontab |
/etc/cron.d/ | Drop-in cron files |
/etc/cron.daily/ | Scripts run daily |
/etc/cron.hourly/ | Scripts run hourly |
/etc/cron.weekly/ | Scripts run weekly |
/etc/cron.monthly/ | Scripts run monthly |
System crontab format can include a user field.
Example from a system cron style:
0 2 * * * root /usr/bin/bash /root/scripts/system-backup.shUser crontab does not include the user field.
User crontab:
0 2 * * * /usr/bin/bash /home/pat/scripts/backup.shSystem crontab:
0 2 * * * pat /usr/bin/bash /home/pat/scripts/backup.shThis difference is important. Putting the wrong format in the wrong place can break scheduling.
screen keeps sessions alive
The command screen creates detachable terminal sessions. This is useful over SSH because a normal command may stop if your connection drops.
Start a named screen session:
screen -S maintenanceRun a long command:
sudo apt upgradeDetach:
Ctrl + A
Ctrl + DList sessions:
screen -lsReattach:
screen -r maintenanceExit a screen session:
exitor press:
Ctrl + DUse cases:
| Use case | Why screen helps |
|---|---|
| System upgrades | Avoid interruption during SSH disconnect |
| Long backups | Keep process running |
| Large rsync transfers | Reconnect later |
| Log monitoring | Leave session open |
| Slow scripts | Detach and return |
A safe remote update pattern:
ssh pat@server
screen -S update
sudo apt update
sudo apt upgradeIf the connection drops:
ssh pat@server
screen -r updateThis is much safer than running long maintenance in an ordinary SSH session.
Basic shell scripting turns workflows into tools
A shell script is a file containing commands.
Create a script:
nano hello.shContent:
#!/bin/bash
echo "Hello from Linux"Make it executable:
chmod +x hello.shRun it:
./hello.shThe first line:
#!/bin/bashis called a shebang. It tells Linux which interpreter should run the file.
Without execute permission, this may fail:
Permission deniedFix:
chmod +x hello.shA script does not need to be executable if you run it explicitly with Bash:
bash hello.shBut executable scripts are more convenient.
Script safety basics
At the beginning of many scripts, you may see:
set -eThis tells the script to stop if a command fails.
A stricter pattern:
set -euo pipefailMeaning:
| Option | Meaning |
|---|---|
-e | Exit if a command fails |
-u | Treat unset variables as errors |
-o pipefail | Fail pipeline if any command in it fails |
Example:
#!/bin/bash
set -euo pipefail
SOURCE="/var/www/site"
DESTINATION="/home/pat/backups"
mkdir -p "$DESTINATION"
tar -czf "$DESTINATION/site.tar.gz" "$SOURCE"
echo "Backup completed"This makes scripts safer, but it also requires more careful writing. A command that returns a nonzero status intentionally may stop the script unless handled.
For beginner scripts, start simple. As scripts become important, add safety options and logging.
Exit status explains success and failure
Every Linux command returns an exit status.
Check the last command’s exit status:
echo $?Usually:
| Exit status | Meaning |
|---|---|
0 | Success |
| Nonzero | Failure or special condition |
Example:
ls /home
echo $?If successful, output is likely:
0Now try a missing path:
ls /path/that/does/not/exist
echo $?The exit status will be nonzero.
This is essential in scripts.
if tar -tzf backup.tar.gz > /dev/null 2>&1; then
echo "Archive is readable"
else
echo "Archive verification failed"
fiThe if statement checks the command’s exit status.
if statements in shell scripts
A basic if statement:
if command; then
echo "Command succeeded"
else
echo "Command failed"
fiCheck whether a file exists:
if [ -f "/etc/hosts" ]; then
echo "File exists"
else
echo "File does not exist"
fiCheck whether a directory exists:
if [ -d "/var/www/site" ]; then
echo "Directory exists"
else
echo "Directory does not exist"
fiCommon test operators:
| Test | Meaning |
|---|---|
-f file | Regular file exists |
-d directory | Directory exists |
-e path | Path exists |
-r file | File is readable |
-w file | File is writable |
-x file | File is executable |
-s file | File exists and is not empty |
Example backup check:
if [ ! -d "$SOURCE" ]; then
echo "Source directory does not exist: $SOURCE"
exit 1
fiThe ! means not.
for loops repeat actions
A for loop runs commands for multiple values.
for file in *.log; do
echo "$file"
doneCompress old-style log files:
for file in *.log; do
gzip "$file"
doneLoop through servers:
for server in web1 web2 web3; do
echo "Checking $server"
ssh "$server" "hostname; uptime; df -h /"
doneLoop through users:
for user in alice bob charlie; do
echo "User: $user"
doneAlways quote variables unless you intentionally want word splitting.
Better:
echo "$file"Riskier:
echo $fileFor loops are the foundation of simple automation.
while loops process input line by line
A while loop can read a file line by line.
while read -r line; do
echo "$line"
done < file.txtThe -r option prevents backslash interpretation.
Read server names from a file:
while read -r server; do
echo "Checking $server"
ssh "$server" "uptime"
done < servers.txtIf servers.txt contains:
web1
web2
web3the script checks each server.
This is a simple but powerful administration pattern.
aliases make frequent commands shorter
An alias creates a shortcut.
alias ll='ls -lah'Now you can run:
llShow aliases:
aliasRemove an alias:
unalias llTo make aliases permanent, add them to your shell configuration file, often:
~/.bashrcEdit:
nano ~/.bashrcAdd:
alias ll='ls -lah'
alias gs='git status'
alias update='sudo apt update && sudo apt upgrade'Reload:
source ~/.bashrcAliases are useful, but do not make destructive aliases too casual. For example, aliasing rm to rm -rf would be dangerous. A safer alias is:
alias rm='rm -i'This asks before deletion, but it can also affect scripts or expectations in interactive use. Use aliases thoughtfully.
Practical workflow: create a reusable maintenance script
Create script:
nano ~/scripts/server-check.shScript content:
#!/bin/bash
set -euo pipefail
echo "Hostname:"
hostname
echo
echo "Uptime:"
uptime
echo
echo "Disk usage:"
df -h
echo
echo "Failed services:"
systemctl --failed || true
echo
echo "Top memory processes:"
ps aux --sort=-%mem | head -10
echo
echo "Top CPU processes:"
ps aux --sort=-%cpu | head -10Make executable:
chmod +x ~/scripts/server-check.shRun:
~/scripts/server-check.shWhy || true after systemctl --failed?
Some commands can return nonzero status even when the output is still useful. Since the script uses set -e, a nonzero status could stop the script. The || true prevents that specific command from stopping the script.
This script combines system identity, uptime, disk usage, failed services and top processes into one repeatable check.
Practical workflow: remote health check for multiple servers
Create servers.txt:
nano servers.txtExample content:
web1
web2
db1
backup1Create script:
nano check-servers.shScript:
#!/bin/bash
while read -r server; do
echo "Checking $server"
ssh "$server" "hostname; uptime; df -h /; systemctl --failed"
echo
done < servers.txtMake executable:
chmod +x check-servers.shRun:
./check-servers.shThis is a simple example of how shell scripting and SSH combine into real administration.
Practical workflow: build a daily report from logs
Suppose you want a daily error report from app.log.
Create script:
nano ~/scripts/error-report.shScript:
#!/bin/bash
set -euo pipefail
LOG_FILE="/var/log/myapp/app.log"
REPORT_DIR="/home/pat/reports"
DATE=$(date +%Y-%m-%d)
REPORT="$REPORT_DIR/error-report-$DATE.txt"
mkdir -p "$REPORT_DIR"
{
echo "Error report for $DATE"
echo
echo "Total log lines:"
wc -l "$LOG_FILE"
echo
echo "Total error lines:"
grep -i "error" "$LOG_FILE" | wc -l
echo
echo "Most common error lines:"
grep -i "error" "$LOG_FILE" | sort | uniq -c | sort -nr | head -20
} > "$REPORT"
echo "Report created: $REPORT"Make executable:
chmod +x ~/scripts/error-report.shRun manually:
~/scripts/error-report.shSchedule daily at 06:00:
crontab -eAdd:
0 6 * * * /home/pat/scripts/error-report.sh >> /home/pat/reports/error-report-cron.log 2>&1This turns log analysis into scheduled reporting.
Practical workflow: build a safe cleanup script
Create script:
nano ~/scripts/cleanup-old-logs.shScript:
#!/bin/bash
set -euo pipefail
LOG_DIR="/var/log/myapp"
DAYS="30"
REPORT="/home/pat/logs/cleanup-old-logs-$(date +%Y-%m-%d).log"
mkdir -p "$(dirname "$REPORT")"
echo "$(date +"%Y-%m-%d %H:%M:%S") Cleanup preview started" | tee -a "$REPORT"
find "$LOG_DIR" -type f -name "*.gz" -mtime +"$DAYS" -exec ls -lh {} \; | tee -a "$REPORT"
echo "$(date +"%Y-%m-%d %H:%M:%S") Cleanup deletion started" | tee -a "$REPORT"
find "$LOG_DIR" -type f -name "*.gz" -mtime +"$DAYS" -delete
echo "$(date +"%Y-%m-%d %H:%M:%S") Cleanup finished" | tee -a "$REPORT"Make executable:
chmod +x ~/scripts/cleanup-old-logs.shRun manually:
~/scripts/cleanup-old-logs.shSchedule weekly:
crontab -eAdd:
0 3 * * 0 /home/pat/scripts/cleanup-old-logs.sh >> /home/pat/logs/cleanup-cron.log 2>&1This script logs what it is doing and limits cleanup to a precise directory and file pattern.
Practical workflow: combine cron and reports
Create script:
nano ~/scripts/web-log-report.shScript:
#!/bin/bash
set -euo pipefail
ACCESS_LOG="/var/log/nginx/access.log"
REPORT_DIR="/home/pat/reports"
DATE=$(date +%Y-%m-%d)
REPORT="$REPORT_DIR/web-log-report-$DATE.txt"
mkdir -p "$REPORT_DIR"
{
echo "Web log report for $DATE"
echo
echo "Top client IPs"
awk '{print $1}' "$ACCESS_LOG" | sort | uniq -c | sort -nr | head -20
echo
echo "HTTP status code counts"
awk '{print $9}' "$ACCESS_LOG" | sort | uniq -c | sort -nr
echo
echo "Top 404 sources"
awk '$9 == 404 {print $1}' "$ACCESS_LOG" | sort | uniq -c | sort -nr | head -20
echo
echo "Recent 500 errors"
awk '$9 == 500 {print $0}' "$ACCESS_LOG" | tail -20
} > "$REPORT"
echo "Report created: $REPORT"Make executable:
chmod +x ~/scripts/web-log-report.shRun manually:
~/scripts/web-log-report.shSchedule daily:
crontab -eAdd:
5 6 * * * /home/pat/scripts/web-log-report.sh >> /home/pat/reports/web-log-report-cron.log 2>&1This is how Linux text tools become automated operations.
Practical workflow: check command paths in scripts
Cron and scripts may not have the same PATH as your interactive shell.
Find command paths:
command -v bash
command -v tar
command -v awk
command -v sort
command -v uniq
command -v headExample output:
/usr/bin/bash
/usr/bin/tar
/usr/bin/awk
/usr/bin/sort
/usr/bin/uniq
/usr/bin/headIn important scripts, you can either use absolute command paths or define PATH explicitly.
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"Example:
#!/bin/bash
set -euo pipefail
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
tar -czf /home/pat/backups/site.tar.gz /var/www/siteThis reduces differences between interactive and scheduled execution.
Practical workflow: use shellcheck for better scripts
A very useful tool for checking shell scripts is shellcheck.
Install:
sudo apt update
sudo apt install shellcheckCheck a script:
shellcheck backup.shshellcheck can detect quoting issues, unsafe patterns, unused variables and many common scripting mistakes.
Example:
shellcheck ~/scripts/site-backup.shThis is not part of the original beginner cheat sheet, but it is one of the best practical tools for writing safer shell scripts.
Practical workflow: build a personal Linux command library
As you learn, create your own command notes.
mkdir -p ~/linux-notes
nano ~/linux-notes/useful-commands.mdAdd commands with comments.
Check disk:
df -h
Largest folders:
du -h --max-depth=1 /var | sort -h
Find large files:
find /var -type f -size +100M -exec ls -lh {} \;
Check failed services:
systemctl --failed
Follow Nginx logs:
journalctl -u nginx -fThis becomes your personal operational knowledge base.
You can search it:
grep -i "disk" ~/linux-notes/useful-commands.mdYou can version it with Git later if needed.
Key takeaways
The focus now shifts from individual commands to workflows, scripts and scheduled automation.
The key commands and concepts are:
| Tool or concept | Essential role |
|---|---|
history | Reuse and inspect previous commands |
!! | Repeat the previous command |
!123 | Run a command from history by number |
| Wildcards | Match groups of files |
| Quotes | Control shell interpretation |
| Variables | Store reusable values |
$(command) | Insert command output into another command |
| ` | ` |
> | Redirect output and overwrite |
>> | Redirect output and append |
2> | Redirect errors |
2>&1 | Merge errors with output |
tee | Display and save output |
xargs | Convert input into command arguments |
wc | Count lines, words, characters and bytes |
cut | Extract simple fields |
awk | Process structured text and apply logic |
sed | Edit and transform text streams |
sort | Sort lines |
uniq | Remove or count repeated adjacent lines |
crontab | Schedule commands |
screen | Keep terminal sessions alive |
| Shell scripts | Save workflows as reusable tools |
| Exit status | Detect success or failure |
if | Add decisions to scripts |
for | Repeat actions over a list |
while read | Process files line by line |
watch | Repeat a command visually |
lsof | Identify open files and port usage |
dmesg | Inspect kernel and hardware messages |
The deeper lesson is that Linux is built around text streams. Logs are text. Configuration files are text. Command output is text. Scripts are text. Reports can be generated from text. Automation is often just carefully structured text moving through commands.
Once you understand pipes, redirection, quoting, variables, text processing and cron, the terminal stops being a manual interface only. It becomes a system for building your own tools.
Exit status tells whether a command succeeded
Every Linux command returns an exit status. This is a small numeric result that tells the shell whether the command succeeded or failed.
Check the exit status of the previous command:
echo $?The general convention is:
| Exit status | Meaning |
|---|---|
0 | Success |
| Nonzero | Failure, warning or special condition |
Example:
ls /home
echo $?If /home exists and is readable, the exit status is likely:
0Now try:
ls /directory/that/does/not/exist
echo $?The exit status will be nonzero.
This is important in scripts because scripts use exit status to decide what to do next.
Example:
if tar -tzf backup.tar.gz > /dev/null 2>&1; then
echo "Archive is valid"
else
echo "Archive is damaged or unreadable"
fiThe script does not need to parse text output. It checks whether tar succeeded.
This is a clean Linux pattern: let commands report success or failure through exit status.
Practical workflow: create a safe update script
Create script:
nano ~/scripts/safe-update.shScript:
#!/bin/bash
set -euo pipefail
LOG_DIR="/home/pat/maintenance-logs"
DATE=$(date +%Y-%m-%d-%H-%M)
LOG="$LOG_DIR/update-$DATE.log"
mkdir -p "$LOG_DIR"
{
echo "Maintenance started at $(date +"%Y-%m-%d %H:%M:%S")"
echo
echo "Host:"
hostname
echo
echo "Uptime before:"
uptime
echo
echo "Disk before:"
df -h
echo
echo "Failed services before:"
systemctl --failed || true
echo
echo "Updating package lists:"
sudo apt update
echo
echo "Upgradable packages:"
apt list --upgradable
echo
echo "Applying upgrades:"
sudo apt upgrade
echo
echo "Removing unused dependencies:"
sudo apt autoremove
echo
echo "Failed services after:"
systemctl --failed || true
echo
echo "Disk after:"
df -h
echo
echo "Maintenance finished at $(date +"%Y-%m-%d %H:%M:%S")"
} 2>&1 | tee "$LOG"Make executable:
chmod +x ~/scripts/safe-update.shRun:
~/scripts/safe-update.shThis script records maintenance output. It is still interactive because apt upgrade may ask for confirmation. That is often desirable for manual maintenance.
For fully automated updates, more policy decisions are needed. Automation should not blindly upgrade critical production systems without testing and rollback planning.
Practical workflow: use aliases for safer inspection
Useful aliases can make inspection faster.
Open:
nano ~/.bashrcAdd:
alias ll='ls -lah'
alias la='ls -la'
alias lt='ls -lahtr'
alias disk='df -h'
alias ports='sudo ss -tulpn'
alias failed='systemctl --failed'Reload:
source ~/.bashrcNow:
ll
disk
failed
portsAliases should make safe inspection easier. Avoid aliases that hide dangerous behavior.
A dangerous alias would be:
alias cleanup='rm -rf *'A safer alias:
alias rm='rm -i'Even this should be used thoughtfully. Some experienced users prefer not to alias core commands because it can create surprises on other systems.
Practical workflow: create reusable functions
Shell functions are more flexible than aliases.
Add to ~/.bashrc:
servercheck() {
hostname
uptime
df -h
systemctl --failed
}Reload:
source ~/.bashrcRun:
servercheckFunction with argument:
portcheck() {
sudo ss -tulpn | grep ":$1"
}Use:
portcheck 80Function for log search:
logerrors() {
grep -i "error" "$1" | tail -n 50
}Use:
logerrors /var/log/syslogFunctions can turn frequent diagnostic patterns into personal tools.
Practical workflow: make personal scripts portable
If you create scripts in:
~/scriptsAdd this directory to PATH.
echo 'export PATH="$PATH:$HOME/scripts"' >> ~/.bashrc
source ~/.bashrcCreate a script:
nano ~/scripts/servercheckContent:
#!/bin/bash
hostname
uptime
df -h
systemctl --failed || trueMake executable:
chmod +x ~/scripts/servercheckRun from anywhere:
servercheckThis is how personal command-line workflows become reusable tools.
Here-documents for writing multi-line files
A here-document lets you send multiple lines into a command.
Example:
cat > notes.txt << 'EOF'
This is line one.
This is line two.
This is line three.
EOFThis writes three lines into notes.txt.
Use with sudo tee for protected files:
sudo tee /etc/example.conf > /dev/null << 'EOF'
setting_one=yes
setting_two=no
EOFThe quoted 'EOF' prevents variable expansion inside the block.
Without quotes:
cat > file.txt << EOF
Home is $HOME
EOFThe variable expands.
With quotes:
cat > file.txt << 'EOF'
Home is $HOME
EOFThe literal text $HOME is written.
Here-documents are useful for scripts, configuration templates, test files and documentation generation.
systemd timers as an alternative to cron
Cron is classic and simple. Systemd timers are a modern alternative on systemd systems. They integrate with system logs and service units.
A timer usually has two files: a service and a timer.
Create service:
sudo nano /etc/systemd/system/site-backup.serviceContent:
[Unit]
Description=Run site backup
[Service]
Type=oneshot
User=pat
ExecStart=/home/pat/ops/scripts/site-backupCreate timer:
sudo nano /etc/systemd/system/site-backup.timerContent:
[Unit]
Description=Run site backup daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.targetReload systemd:
sudo systemctl daemon-reloadEnable and start timer:
sudo systemctl enable --now site-backup.timerList timers:
systemctl list-timersCheck timer status:
systemctl status site-backup.timerCheck service logs:
journalctl -u site-backup.serviceSystemd timer advantages:
| Feature | Benefit |
|---|---|
| Integrated logging | Use journalctl |
| Persistent timers | Missed runs can execute after boot |
| Dependency management | Can depend on services or targets |
| Service isolation | Runs as defined unit |
| Status visibility | systemctl status and list-timers |
Cron is still excellent. Systemd timers are better when you want deeper integration with systemd.
Understanding lock files
Some programs create lock files to prevent multiple instances from running at the same time.
A lock file may live under:
/run
/var/lock
/tmp
application-specific directoriesIf a script says another instance is running, it may be using a lock file.
Find lock files:
find /run /var/lock /tmp -iname "*lock*" 2> /dev/null | headDo not delete lock files blindly. First check whether the related process is still running.
Example:
ps aux | grep process-nameIf a program crashed and left a stale lock, removing it may be appropriate after verification.
Lock files exist to prevent data corruption. Treat them as warning signs.
Practical workflow: prevent duplicate script runs with flock
If a cron job might overlap with itself, use flock.
Example cron line:
0 * * * * /usr/bin/flock -n /tmp/myjob.lock /home/pat/scripts/myjob.sh >> /home/pat/logs/myjob.log 2>&1This means the job runs only if it can acquire the lock.
If the previous run is still active, the new run exits.
Manual example:
flock -n /tmp/backup.lock /home/pat/scripts/backup.shThis is useful for backups, imports, synchronizations and cleanup tasks.
Without locking, a slow job scheduled every hour may still be running when the next hour begins. Two copies may run at once and cause problems.
Understanding grep exit status
grep returns different exit statuses depending on whether it found a match.
grep "error" app.log
echo $?Typical behavior:
| Exit status | Meaning |
|---|---|
0 | Match found |
1 | No match found |
2 | Error occurred |
This matters in scripts using set -e.
Example:
grep -i "error" app.log | wc -lIf no error exists, this still prints 0 because wc runs. But in some script structures, a no-match grep may stop the script if set -e is active.
Safer in reports:
grep -i "error" app.log || trueor:
ERROR_COUNT=$(grep -i "error" app.log | wc -l)When writing strict scripts, understand which commands return nonzero for normal conditions.
Understanding return codes in maintenance scripts
A maintenance script should exit with a meaningful status.
Successful end:
exit 0Failure:
exit 1Example:
if [ ! -d "$SOURCE" ]; then
echo "Source directory missing: $SOURCE"
exit 1
fiThis tells cron, systemd or another calling process that the script failed.
For systemd services of type oneshot, exit status matters. A nonzero exit means the unit failed.
Check:
systemctl status site-backup.serviceRead logs:
journalctl -u site-backup.serviceGood scripts communicate failure clearly.
Practical workflow: turn a script into a monitored systemd job
Create backup script:
nano /home/pat/ops/scripts/site-backupMake executable:
chmod +x /home/pat/ops/scripts/site-backupCreate service:
sudo nano /etc/systemd/system/site-backup.serviceContent:
[Unit]
Description=Site backup job
[Service]
Type=oneshot
User=pat
ExecStart=/home/pat/ops/scripts/site-backupCreate timer:
sudo nano /etc/systemd/system/site-backup.timerContent:
[Unit]
Description=Run site backup every day
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.targetReload:
sudo systemctl daemon-reloadTest service:
sudo systemctl start site-backup.serviceCheck:
systemctl status site-backup.service
journalctl -u site-backup.service -n 100Enable timer:
sudo systemctl enable --now site-backup.timerList timers:
systemctl list-timersThis gives scheduled execution with systemd visibility.
Understanding persistent timers
In a systemd timer, this line is important:
Persistent=trueIt means that if the system was powered off when the timer should have run, systemd can run the missed job after boot.
This is useful for laptops, small servers and machines that are not always on.
Cron does not behave exactly the same by default. If a cron job is missed because the machine is off, it may simply not run.
This makes systemd timers attractive for some scheduled maintenance tasks.
Practical workflow: inspect what a timer will run
List timers:
systemctl list-timersShow timer:
systemctl status site-backup.timerShow related service:
systemctl cat site-backup.serviceShow logs:
journalctl -u site-backup.serviceRun manually:
sudo systemctl start site-backup.serviceCheck result:
systemctl status site-backup.serviceThis workflow makes scheduled jobs easier to audit than hidden cron entries.
Troubleshooting Linux methodically
Troubleshooting Linux is a method. Good diagnosis follows evidence, error messages, logs, exit status, layered checks and reproducible verification.
Practical workflow: fix a script that will not run
Suppose you have a script named backup.sh.
Try to run it:
./backup.shIf you see:
Permission deniedinspect permissions:
ls -l backup.shIf it looks like this:
-rw-r--r-- 1 pat pat 500 May 14 10:20 backup.shit does not have execute permission.
Add execute permission:
chmod +x backup.shCheck again:
ls -l backup.shNow it may look like:
-rwxr-xr-x 1 pat pat 500 May 14 10:20 backup.shRun again:
./backup.shIf it still fails, check the first line of the script:
head -n 1 backup.shA Bash script should often begin with:
#!/bin/bashor:
#!/usr/bin/env bashThis first line is called the shebang. It tells Linux which interpreter should run the script.
Practical workflow: investigate high CPU usage
Suppose a server feels slow. The goal is to identify whether CPU load is high and which process is responsible.
Start with uptime:
uptimeLook at load average.
Then open htop:
htopIf htop is not available:
topFind the process consuming CPU. Note its PID and command.
Inspect it with ps:
ps -p 12345 -fShow more detail:
ps aux | grep process-nameIf it belongs to a service:
systemctl status service-nameCheck logs:
journalctl -u service-name -n 50Only then decide whether to restart:
sudo systemctl restart service-nameIf a single runaway process does not stop gracefully:
kill 12345If still running:
kill -9 12345Professional sequence:
| Step | Command | Purpose |
|---|---|---|
| Check system load | uptime | Confirm load pressure |
| Inspect processes | htop | Identify heavy process |
| Verify process | ps -p PID -f | Understand what it is |
| Check service | systemctl status service | See managed service state |
| Read logs | journalctl -u service -n 50 | Find cause |
| Restart service | systemctl restart service | Controlled recovery |
| Kill only if needed | kill PID | Stop unmanaged process |
Do not start with kill -9. Start with diagnosis.
Practical workflow: investigate a failed service
Suppose Apache is not working.
Check service status:
systemctl status apache2Read recent logs:
journalctl -u apache2 -n 80Test configuration:
sudo apache2ctl configtestIf configuration is valid, restart:
sudo systemctl restart apache2Check status again:
systemctl status apache2Check listening ports:
sudo ss -tulpn | grep apacheor:
sudo ss -tulpn | grep ':80'If Apache fails after a configuration edit, restore backup:
sudo cp /etc/apache2/apache2.conf.backup /etc/apache2/apache2.conf
sudo systemctl restart apache2This shows why configuration backups matter.
A good service troubleshooting pattern:
systemctl status service-name
journalctl -u service-name -n 50
sudo systemctl restart service-name
systemctl status service-nameFor web servers, add configuration test commands where available.
Practical workflow: why a website is down
Suppose a website is not responding. A beginner may restart everything. A better workflow is systematic.
Check whether the server is reachable:
ping server-ipLog in:
ssh user@server-ipCheck disk space:
df -hCheck web service:
systemctl status nginxor:
systemctl status apache2Check listening ports:
sudo ss -tulpn | grep ':80'
sudo ss -tulpn | grep ':443'Check logs:
journalctl -u nginx -n 80or:
journalctl -u apache2 -n 80Check web root permissions:
ls -ld /var/www
ls -lah /var/www/htmlCheck recent changes:
find /var/www/html -type f -mmin -60Restart only after diagnosis:
sudo systemctl restart nginxor:
sudo systemctl restart apache2Then verify:
systemctl status nginx
sudo ss -tulpn | grep ':80'This workflow connects networking, services, ports, logs, files and permissions.
Practical workflow: recover from a broken configuration change
Before editing a configuration file, make a backup.
Example:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup
sudo nano /etc/nginx/nginx.confTest configuration:
sudo nginx -tIf valid, reload:
sudo systemctl reload nginxIf broken, restore backup:
sudo cp /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf
sudo nginx -t
sudo systemctl reload nginxThe same logic applies to many services.
| Service | Test command example |
|---|---|
| Apache | sudo apache2ctl configtest |
| Nginx | sudo nginx -t |
| SSH | sudo sshd -t |
| Systemd service file | sudo systemd-analyze verify file.service |
Testing before restarting is a professional habit.
This is especially important for SSH configuration. If you break SSH and restart it on a remote server, you may lock yourself out.
A safer SSH configuration workflow:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sudo nano /etc/ssh/sshd_config
sudo sshd -t
sudo systemctl reload sshKeep an existing SSH session open while testing a new one.
Network troubleshooting as layered thinking
Network troubleshooting should be done in layers. Each layer answers a different question.
| Layer | Question | Commands |
|---|---|---|
| Interface | Does the machine have a network interface and IP? | ip a |
| Link | Is the interface up? | ip link, ip a |
| Route | Does traffic know where to go? | ip route |
| Gateway | Can the local router be reached? | ping gateway |
| Internet IP | Can external IPs be reached? | ping 8.8.8.8 |
| DNS | Can names be resolved? | getent hosts domain, ping domain |
| Local service | Is the service listening? | ss -tulpn, lsof -i :port |
| Remote service | Can another host reach the port? | curl, nc, browser |
| Logs | What does the service report? | journalctl, /var/log |
This method avoids confusion. For example, if DNS fails, restarting the web server will not help. If the service is not listening, changing DNS will not help. If the interface has no IP, editing application configuration will not help.
A strong Linux user does not guess the layer. They test it.
Practical workflow: understand command failure with set -x
In shell scripts, set -x prints commands as they are executed. This is useful for debugging.
Example:
#!/bin/bash
set -x
SOURCE="/var/www/site"
DESTINATION="/home/pat/backups"
mkdir -p "$DESTINATION"
tar -czf "$DESTINATION/site.tar.gz" "$SOURCE"Run the script and observe each expanded command.
You can enable debugging only for part of a script.
set -x
tar -czf "$ARCHIVE" "$SOURCE"
set +xDo not use set -x around secrets because it may print sensitive values.
Troubleshooting Linux is a method, not a panic reaction
Linux troubleshooting is not about randomly restarting services, deleting files, changing permissions or copying commands from forums. Good troubleshooting is a method. It begins with observation, continues with narrowing the problem, then moves to controlled action and ends with verification.
A beginner often reacts to an error by immediately looking for a command that “fixes” it. An experienced Linux user first asks what layer is failing.
Is the problem in the filesystem?
Is the disk full?
Is the service stopped?
Is the process running but not responding?
Is the network interface down?
Is DNS failing?
Is a firewall blocking access?
Is a configuration file invalid?
Is a permission missing?
Is the command being run as the wrong user?
Is the script using the wrong working directory?
Is cron running with a different environment?
The terminal gives you tools to answer all of these questions. The real skill is choosing the right question first.
A professional Linux troubleshooting process usually follows this structure.
| Stage | Purpose | Typical commands |
|---|---|---|
| Identify | Confirm what system, user and location you are working with | hostname, whoami, pwd, id |
| Observe | Check visible system state | uptime, df -h, free -h, systemctl --failed |
| Inspect | Read logs, files and service status | tail, grep, journalctl, systemctl status |
| Narrow down | Test one layer at a time | ip a, ip route, ping, ss, lsof, find |
| Act | Make the smallest necessary change | nano, systemctl reload, chmod, chown, apt install |
| Verify | Confirm the result after action | systemctl status, curl, df -h, tail, grep |
| Document | Save what was done | history, tee, notes, scripts |
The most dangerous troubleshooting pattern is this:
sudo chmod -R 777 /var/www
sudo systemctl restart apache2
sudo rm -rf cacheThis may appear to solve a symptom temporarily, but it creates security problems, removes evidence and may damage the system.
A better approach is:
hostname
whoami
pwd
df -h
systemctl status apache2
journalctl -u apache2 -n 100
ls -ld /var/www
ls -lah /var/wwwThis sequence gathers facts before making changes.
The deepest Linux troubleshooting principle is simple: do not destroy information before you understand it. Logs, permissions, timestamps, process states and configuration files are evidence. If you delete or overwrite them too early, you make the real cause harder to find.
Read the error message before searching for the solution
Many Linux errors already contain the answer, but beginners often ignore them because they look technical.
Consider this error:
Permission deniedThis usually points to permissions, ownership, execution rights or user identity.
Start with:
whoami
pwd
ls -ld .
ls -l target-fileAnother error:
No such file or directoryThis does not always mean the file never existed. It may mean the path is wrong, the current directory is different, the filename case is wrong, a script interpreter path is invalid or a symbolic link points to a missing target.
Start with:
pwd
ls -lah
ls -l /full/path/to/fileAnother error:
command not foundThis may mean the package is not installed, the command name is wrong or the command is not in your PATH.
Start with:
type command-name
command -v command-name
echo "$PATH"Another error:
Address already in useThis means a service tried to bind to a port that is already occupied.
Start with:
sudo ss -tulpn | grep ':80'
sudo lsof -i :80Another error:
No space left on deviceThis may mean the filesystem is full, inodes are exhausted or a deleted file is still held open.
Start with:
df -h
df -i
sudo du -h --max-depth=1 / | sort -h
sudo lsof | grep deletedA Linux error message is not an obstacle. It is a diagnostic clue.
Command not found means more than missing software
When you see:
command not founddo not immediately assume Linux is broken. This error has several possible causes.
| Cause | Example | Diagnostic command |
|---|---|---|
| Command is not installed | htop missing | command -v htop |
| Command name is misspelled | ifconfg | Check spelling |
| Command exists but is not in PATH | Custom script | echo "$PATH" |
Script is in current directory but not called with ./ | myscript.sh | ./myscript.sh |
| File is not executable | Script exists but cannot run | ls -l script.sh |
| Wrong shell or environment | Cron cannot find command | Use absolute paths |
Check whether a command exists:
command -v htopCheck command type:
type htopFind possible package:
apt search htopInstall if needed:
sudo apt update
sudo apt install htopIf you created a script in the current directory, this may fail:
backup.shThe current directory is usually not searched automatically for security reasons. Use:
./backup.shIf permission is missing:
chmod +x backup.sh
./backup.shIf a script works interactively but fails in cron, use absolute paths.
Find command path:
command -v tar
command -v bash
command -v rsyncThen use full paths in cron or define PATH.
Understanding PATH
PATH is an environment variable that tells the shell where to look for commands.
Show it:
echo "$PATH"Example output:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binThe directories are separated by colons.
When you type:
lsthe shell searches directories in PATH until it finds an executable named ls.
Check where ls is found:
command -v lsExample:
/usr/bin/lsIf your own script is in:
/home/pat/scriptsand that directory is not in PATH, you cannot run the script by name alone unless you use the full path or ./.
Run by full path:
/home/pat/scripts/backup.shRun from inside the script directory:
./backup.shAdd a directory to PATH temporarily:
export PATH="$PATH:/home/pat/scripts"To make it persistent, add it to ~/.bashrc:
nano ~/.bashrcAdd:
export PATH="$PATH:/home/pat/scripts"Reload:
source ~/.bashrcBe careful with PATH. Do not add untrusted writable directories to the beginning of PATH, especially on shared systems. If a malicious executable appears there with the same name as a common command, it may be run accidentally.
Environment variables shape command behavior
Environment variables are named values available to programs.
Show all environment variables:
envShow a specific variable:
echo "$HOME"Common variables:
| Variable | Meaning |
|---|---|
HOME | Current user’s home directory |
USER | Current username |
SHELL | Current user’s shell |
PATH | Directories searched for commands |
PWD | Current working directory |
LANG | Locale and language setting |
TERM | Terminal type |
EDITOR | Preferred text editor |
Examples:
echo "$HOME"
echo "$USER"
echo "$SHELL"
echo "$PWD"Set a variable for the current shell:
PROJECT="linux-guide"Export it so child processes can see it:
export PROJECTOr in one line:
export PROJECT="linux-guide"Run one command with a temporary environment variable:
LANG=C sort file.txtThis sets LANG only for that command.
Environment variables are important in scripts, cron jobs, development tools, build systems and deployment workflows.
Practical workflow: investigate command not found in cron
A script works manually but fails in cron. This is common.
First inspect cron:
crontab -lCheck script permissions:
ls -l /home/pat/scripts/backup.shRun with full path:
/usr/bin/bash /home/pat/scripts/backup.shAdd logging to cron:
0 2 * * * /usr/bin/bash /home/pat/scripts/backup.sh >> /home/pat/logs/backup.log 2>&1Inside the script, define PATH:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"Find command paths:
command -v tar
command -v rsync
command -v dateUse absolute paths for important commands if necessary.
A debugging script can temporarily include:
env > /home/pat/logs/cron-env.log
pwd >> /home/pat/logs/cron-env.logThis shows the environment cron provides.
Cron failures are often environment failures, not command failures.
Practical workflow: investigate disk full correctly
When a disk is full, start with filesystems.
df -hAlso check inodes:
df -iA filesystem can be full because of data blocks or because it has run out of inodes. Inodes represent filesystem entries. A huge number of tiny files can exhaust inodes even when space remains.
If normal space is full, inspect directories:
sudo du -h --max-depth=1 / | sort -hIf /var is large:
sudo du -h --max-depth=1 /var | sort -hIf logs are large:
sudo du -h --max-depth=1 /var/log | sort -hFind large files:
sudo find /var -type f -size +500M -exec ls -lh {} \;If inodes are full, count files by directory:
sudo find /var -xdev -type f | cut -d / -f 2,3 | sort | uniq -c | sort -nr | head -20This helps identify where too many files exist.
If disk usage remains high after deletion:
sudo lsof | grep deletedRestart the process holding deleted files if appropriate.
This is professional disk troubleshooting: check blocks, check inodes, locate large files, locate too many files, check deleted open files.
Practical workflow: investigate high memory usage
Check memory:
free -hCheck processes by memory:
ps aux --sort=-%mem | head -15Use htop:
htopCheck service status if a known service is consuming memory:
systemctl status service-nameRead logs:
journalctl -u service-name -n 100Do not kill the largest process without understanding what it is. It may be the database, web server, backup process or monitoring agent.
If a process is part of a service, prefer restarting the service:
sudo systemctl restart service-namerather than killing random PIDs.
If the system is swapping heavily, check:
swapon --show
free -hMemory troubleshooting is not just “what uses the most RAM?” It is also “is this expected for the workload?”
A database may use memory as cache. That can be normal. A runaway script consuming memory endlessly is different.
Practical workflow: diagnose CPU-heavy processes
Check load:
uptimeOpen interactive process viewer:
htopOr use ps:
ps aux --sort=-%cpu | head -15Inspect a specific process:
ps -p PID -fReplace PID with the real process ID.
Show process tree:
ps auxfIf the process belongs to a service:
systemctl status service-name
journalctl -u service-name -n 100If it is a script, inspect where it runs from:
readlink -f /proc/PID/cwd
readlink -f /proc/PID/exeReplace PID with the real process ID.
If appropriate, terminate gracefully:
kill PIDForce only if necessary:
kill -9 PIDHigh CPU is a symptom. The cause may be traffic, a bug, a loop, compression, backup, attack, indexing, database query or scheduled task.
Practical workflow: investigate service starts and immediately fails
Check status:
systemctl status service-nameRead logs:
journalctl -u service-name -n 100Try starting:
sudo systemctl start service-nameCheck again:
systemctl status service-nameIf it fails because of configuration, test configuration if the service provides a test command.
Examples:
sudo nginx -t
sudo apache2ctl configtest
sudo sshd -tIf it fails because a port is occupied:
sudo ss -tulpn | grep ':PORT'
sudo lsof -i :PORTIf it fails because of permissions:
ls -ld /path
ls -l /path/file
namei -l /path/fileIf it fails because of missing files:
ls -l /expected/pathThe logs usually contain the strongest clue. Read them before editing configuration.
Practical workflow: investigate why a script behaves differently under sudo
A script may work as a normal user but behave differently under sudo.
Check user:
whoami
sudo whoamiCheck environment:
env | sort
sudo env | sortCheck home:
echo "$HOME"
sudo sh -c 'echo "$HOME"'When using sudo, environment variables may be reset or changed. The working directory may remain the same, but home and PATH may differ depending on sudo policy.
If a script depends on user-specific files like:
~/.ssh/config
~/.aws/credentials
~/.config/app/settingsthen running it with sudo may look in root’s home instead of the user’s home.
A safer script uses explicit paths and avoids relying blindly on ~ when privilege changes are involved.
Practical workflow: investigate symbolic link deployment
Suppose a website uses this layout:
/var/www/releases/2026-05-14
/var/www/releases/2026-05-15
/var/www/current -> /var/www/releases/2026-05-15Check link:
ls -l /var/www/currentResolve:
readlink -f /var/www/currentCheck current release:
ls -lah /var/www/currentSwitch to a new release:
sudo ln -sfn /var/www/releases/2026-05-16 /var/www/currentOptions:
| Option | Meaning |
|---|---|
-s | Create symbolic link |
-f | Force replacement |
-n | Treat destination symlink as normal file |
Then reload service if needed:
sudo systemctl reload nginxRollback:
sudo ln -sfn /var/www/releases/2026-05-15 /var/www/current
sudo systemctl reload nginxSymbolic links make deployments fast, but they require careful path management.
Practical workflow: recover from accidental config edit
Suppose you edited:
/etc/nginx/nginx.confand now Nginx fails.
Check status:
systemctl status nginxTest configuration:
sudo nginx -tIf you made a backup:
sudo cp /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf
sudo nginx -t
sudo systemctl reload nginxIf you did not make a backup, check package default examples or reinstall package configuration carefully. Do not immediately purge the package unless you understand consequences.
If using version control for /etc, restore from Git. Some administrators manage /etc with tools such as etckeeper, but even without that, the habit is clear: configuration should be backed up before editing.
A safer future workflow:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%Y-%m-%d-%H-%M).backup
sudo nano /etc/nginx/nginx.conf
sudo nginx -t
sudo systemctl reload nginxPractical workflow: compare before and after system state
Before maintenance:
mkdir -p ~/maintenance-logs
{
date
hostname
uptime
df -h
systemctl --failed
apt list --upgradable
} > ~/maintenance-logs/before.txtAfter maintenance:
{
date
hostname
uptime
df -h
systemctl --failed
} > ~/maintenance-logs/after.txtCompare:
diff -u ~/maintenance-logs/before.txt ~/maintenance-logs/after.txtThis creates a record of system state before and after work.
For important servers, this habit is valuable because it separates real changes from assumptions.
Practical workflow: check what changed in the last hour
Find files modified in the last hour under a project:
find /var/www/site -type f -mmin -60 -exec ls -lh {} \;Find configuration files modified recently:
sudo find /etc -type f -mmin -60 -exec ls -lh {} \;Find logs updated recently:
sudo find /var/log -type f -mmin -60 -exec ls -lh {} \;Sort current directory by time:
ls -ltThis is useful after unexpected behavior. If something broke recently, ask what changed recently.
Do not assume. Search timestamps.
Practical workflow: detect and inspect failed login attempts
On many Debian-based systems, authentication logs are stored in:
/var/log/auth.logSearch failed password attempts:
sudo grep -i "failed password" /var/log/auth.logCount them:
sudo grep -i "failed password" /var/log/auth.log | wc -lTop source IPs:
sudo grep -i "failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | headLog formats can vary, so the field position may need adjustment. Always inspect a few lines first:
sudo grep -i "failed password" /var/log/auth.log | headFor systemd journal:
journalctl -u ssh -n 200or depending on service name:
journalctl -u sshd -n 200Security analysis starts with logs, not assumptions.
Practical workflow: create a minimal diagnostic script
Create:
nano ~/scripts/diagnostic-summary.shScript:
#!/bin/bash
echo "System diagnostic summary"
echo
echo "Hostname:"
hostname
echo
echo "User:"
whoami
id
echo
echo "Current directory:"
pwd
echo
echo "Uptime:"
uptime
echo
echo "Disk usage:"
df -h
echo
echo "Memory:"
free -h
echo
echo "Failed services:"
systemctl --failed || true
echo
echo "Listening ports:"
sudo ss -tulpn
echo
echo "Recent system logs:"
journalctl -n 50Make executable:
chmod +x ~/scripts/diagnostic-summary.shRun:
~/scripts/diagnostic-summary.shSave output:
~/scripts/diagnostic-summary.sh 2>&1 | tee diagnostic-summary.txtThis script is not a replacement for real diagnosis, but it collects a useful first snapshot.
Practical workflow: use watch during repair
When fixing a problem, you may want to monitor state repeatedly.
Watch disk:
watch -n 5 df -hWatch service status:
watch -n 3 "systemctl is-active nginx"Watch a port:
watch -n 2 "ss -tulpn | grep ':80'"Watch log error count:
watch -n 10 "grep -i error /var/log/syslog | wc -l"Watch file growth:
watch -n 2 "ls -lh app.log"Use quotes around complex watched commands. Without quotes, pipes may be interpreted outside watch.
Practical workflow: stop guessing with a checklist
When a Linux service fails, use a checklist.
hostname
whoami
uptime
df -h
systemctl status service-name
journalctl -u service-name -n 100
sudo ss -tulpn
ps aux | grep service-nameIf configuration was changed:
find /etc -type f -mmin -60 -exec ls -lh {} \;If files were deployed:
find /var/www/site -type f -mmin -60 -exec ls -lh {} \;If permissions may be wrong:
namei -l /path/to/problem/fileIf disk may be full:
df -h
df -iThis checklist does not fix everything. It prevents blind troubleshooting.
Practical workflow: document a repair session
Start a log file:
LOG="repair-$(date +%Y-%m-%d-%H-%M).log"Record basic state:
{
date
hostname
whoami
uptime
df -h
systemctl --failed
} | tee "$LOG"Append commands manually as you work:
echo "Checked nginx status" | tee -a "$LOG"
systemctl status nginx 2>&1 | tee -a "$LOG"Append logs:
journalctl -u nginx -n 100 2>&1 | tee -a "$LOG"At the end:
echo "Repair finished at $(date)" | tee -a "$LOG"This creates a trace of what you observed and changed. On professional systems, this is valuable for post-incident review.
Practical workflow: safely run a risky command
A risky command is any command that deletes, overwrites, recursively changes permissions, changes ownership, restarts critical services or synchronizes with deletion.
Before a risky command, ask:
| Question | Verification command |
|---|---|
| Am I on the right host? | hostname |
| Am I the right user? | whoami, id |
| Am I in the right directory? | pwd |
| What will this affect? | ls, find, tree, du |
| Can I preview it? | echo, --dry-run, -print |
| Do I have a backup? | ls -lh backup |
| Can I rollback? | Backup config, previous release, service reload |
| Is this production? | Hostname, prompt, deployment context |
Example risky rsync:
rsync -av --delete site/ server:/var/www/site/Safer:
hostname
pwd
rsync -av --delete --dry-run site/ server:/var/www/site/Only after verifying:
rsync -av --delete site/ server:/var/www/site/Example risky deletion:
find /var/log/myapp -type f -name "*.gz" -mtime +30 -deleteSafer:
find /var/log/myapp -type f -name "*.gz" -mtime +30 -exec ls -lh {} \;Then delete.
Practical workflow: build a learning lab for troubleshooting
Create a safe lab:
mkdir -p ~/linux-lab/{logs,scripts,data,backups}
cd ~/linux-labCreate sample log:
cat > logs/app.log << 'EOF'
2026-05-14 10:00:01 INFO Application started
2026-05-14 10:01:15 ERROR Database connection failed
2026-05-14 10:02:10 INFO Retrying connection
2026-05-14 10:03:44 ERROR Timeout while connecting
2026-05-14 10:04:01 INFO Application stopped
EOFAnalyze:
cat logs/app.log
grep -i error logs/app.log
grep -i error logs/app.log | wc -l
tail -n 2 logs/app.logCreate script:
cat > scripts/analyze-log.sh << 'EOF'
#!/bin/bash
LOG_FILE="$1"
echo "Total lines:"
wc -l "$LOG_FILE"
echo "Error lines:"
grep -i "error" "$LOG_FILE" || true
echo "Error count:"
grep -i "error" "$LOG_FILE" | wc -l
EOFMake executable:
chmod +x scripts/analyze-log.shRun:
scripts/analyze-log.sh logs/app.logThis small lab teaches files, redirection, here-documents, grep, wc, scripts and arguments.
Practical workflow: log rotation awareness
Logs should not grow forever. Many Linux systems use logrotate to rotate, compress and remove old logs.
Configuration is commonly found in:
/etc/logrotate.conf
/etc/logrotate.d/List logrotate configurations:
ls -lah /etc/logrotate.d/View one:
cat /etc/logrotate.d/nginxForce debug mode without rotating:
sudo logrotate -d /etc/logrotate.confThe -d option runs in debug mode and does not make changes.
This is useful when logs are growing too large. Instead of manually deleting active logs, check whether log rotation is configured correctly.
If an application writes huge logs, the solution may be:
| Problem | Better direction |
|---|---|
| Logs never rotate | Configure logrotate |
| Logs are too verbose | Adjust application log level |
| Logs grow during errors | Fix root cause |
| Deleted log does not free space | Restart process or use log rotation properly |
Manual deletion is not a complete log management strategy.
Practical workflow: truncate a log safely
Sometimes you need to reduce a huge active log without deleting the file. Deleting an active log can leave disk space held by a running process.
Truncate a file to zero size:
sudo truncate -s 0 /var/log/myapp/app.logOr:
: | sudo tee /var/log/myapp/app.log > /dev/nullCheck size:
ls -lh /var/log/myapp/app.logThis keeps the same file path, which may be important if a process is still writing to it.
Do this only when you understand the operational impact. Logs are evidence. If needed, archive first:
sudo cp /var/log/myapp/app.log /var/log/myapp/app.log.$(date +%Y-%m-%d-%H-%M).backup
sudo truncate -s 0 /var/log/myapp/app.logPractical workflow: find configuration files for a service
If you do not know where a service stores configuration, use several methods.
Check package files:
dpkg -L nginx | grep confCheck common directories:
find /etc -iname "*nginx*"Check service definition:
systemctl cat nginxCheck process command line:
ps aux | grep nginxCheck manual:
man nginxThe command:
systemctl cat service-nameshows the systemd unit file content and can reveal startup commands and environment files.
This connects package data, filesystem search, service management and process inspection.
Practical workflow: detect recently failed commands in a script
Use set -e to stop on failure and add logging.
Example:
#!/bin/bash
set -euo pipefail
LOG="/home/pat/logs/script.log"
mkdir -p "$(dirname "$LOG")"
echo "$(date +"%Y-%m-%d %H:%M:%S") Script started" | tee -a "$LOG"
echo "Running backup" | tee -a "$LOG"
tar -czf /home/pat/backups/site.tar.gz /var/www/site 2>&1 | tee -a "$LOG"
echo "$(date +"%Y-%m-%d %H:%M:%S") Script finished" | tee -a "$LOG"If the script fails, the log shows the last successful step.
For deeper debugging, temporarily add:
set -xBut avoid set -x around secrets.
Practical workflow: create a command review habit
Before pressing Enter on a serious command, read it aloud logically.
Example:
sudo find /var/log/myapp -type f -name "*.gz" -mtime +30 -deleteRead it as:
Find inside /var/log/myapp, only regular files, named *.gz, older than 30 days, and delete them.
If any part is not what you intend, do not run it.
For rsync:
rsync -avz --delete ./public/ deploy@server:/var/www/site/Read it as:
Synchronize contents of local public into remote /var/www/site, preserve metadata, compress transfer, and delete files on destination that are not in source.
That last part is critical. If you are not sure, use:
rsync -avz --delete --dry-run ./public/ deploy@server:/var/www/site/This habit prevents many disasters.
Key takeaways
Individual Linux commands become more valuable when they are connected into professional diagnostic thinking.
The key tools and concepts are:
| Tool or concept | Essential role |
|---|---|
| Error messages | First diagnostic clue |
| Exit status | Machine-readable success or failure |
echo $? | Show previous command result |
PATH | Command search locations |
type | Show how shell interprets a command |
command -v | Locate command reliably |
which | Quick command path lookup |
whereis | Locate binary and manual paths |
man | Full command documentation |
apropos | Search manual descriptions |
help | Help for shell built-ins |
env | Show environment variables |
export | Make variables available to child processes |
whoami | Show current user |
id | Show user and group IDs |
groups | Show group membership |
sudo | Run command with elevated privileges |
su | Switch user |
sudo -u | Run command as a specific user |
passwd | Change password |
adduser | Create user on Debian-style systems |
usermod -aG | Add user to group |
stat | Show detailed file metadata |
namei -l | Show permissions along a path |
ln -s | Create symbolic links |
readlink | Resolve symbolic links |
diff | Compare files |
truncate | Reduce file size without deleting file |
systemctl cat | Show service unit definition |
systemctl show | Show service properties |
logrotate | Manage log rotation |
tee | Save and display command output |
The deeper lesson is that Linux troubleshooting should be evidence-based.
A problem is not solved by force. It is solved by understanding which layer failed and applying the smallest correct change.
If a command is missing, inspect PATH and packages.
If access is denied, inspect user, groups, file permissions and every parent directory.
If a disk is full, check filesystems, inodes, large directories and deleted open files.
If a service fails, read status, logs, configuration tests, ports and recent changes.
If a script fails in cron, inspect environment, paths, permissions and logs.
If remote access fails, test interface, route, DNS, port, firewall and service state.
This is how Linux becomes predictable. The terminal does not merely let you control the system. It lets you prove what is happening.
Practical workflow: investigate a problem after deployment
Suppose a website broke after a deployment around 14:30.
Check service:
systemctl status nginxCheck web server config:
sudo nginx -tCheck logs since deployment:
journalctl -u nginx --since "2026-05-14 14:30"Check application files modified since deployment:
find /var/www/site -type f -newermt "2026-05-14 14:30" -exec ls -lh {} \;Check permissions of changed files:
find /var/www/site -type f -newermt "2026-05-14 14:30" -exec ls -l {} \;Check recent error logs:
tail -n 100 /var/log/nginx/error.logIf using a symlink release structure:
ls -l /var/www/current
readlink -f /var/www/currentRollback if needed:
sudo ln -sfn /var/www/releases/previous-release /var/www/current
sudo systemctl reload nginxThen verify:
curl -I http://localhost
systemctl status nginxThis workflow focuses on the exact time when the problem began.
Practical workflow: investigate a cron job that did not run
Check crontab:
crontab -lCheck system cron service:
systemctl status cronor on some systems:
systemctl status crondCheck cron logs. On Debian-based systems, cron entries may appear in syslog.
grep CRON /var/log/syslogCheck script path:
ls -l /home/pat/scripts/job.shRun manually with full path:
/usr/bin/bash /home/pat/scripts/job.shCheck log redirection in cron:
0 2 * * * /usr/bin/bash /home/pat/scripts/job.sh >> /home/pat/logs/job.log 2>&1If no log exists, create log directory:
mkdir -p /home/pat/logsAdd environment debugging temporarily:
env > /home/pat/logs/cron-env.log
pwd >> /home/pat/logs/cron-env.logCommon cron problems:
| Problem | Cause |
|---|---|
| Script works manually but not in cron | PATH or environment differs |
| Relative paths fail | Cron starts from a different directory |
| Permission denied | Script not executable or user lacks access |
| No output visible | Output not redirected |
| Job runs as wrong user | System crontab format misunderstood |
| Command not found | Use absolute command paths |
| Time wrong | Server timezone or cron expression wrong |
Cron troubleshooting is mostly environment troubleshooting.
Practical workflow: check timezone and time synchronization
Show date:
dateShow timedate status:
timedatectlExample output includes local time, universal time, timezone and NTP synchronization status.
Set timezone:
sudo timedatectl set-timezone Europe/BratislavaList timezones:
timedatectl list-timezonesSearch:
timedatectl list-timezones | grep EuropeTime matters for logs, cron jobs, certificates, backups and distributed systems.
If logs appear to have the wrong time, check:
date
timedatectlA wrong timezone can make troubleshooting confusing because event order becomes unclear.
Practical workflow: check certificates and date related failures
TLS certificates depend on correct time. If system time is wrong, HTTPS connections can fail.
Check time:
date
timedatectlTest HTTPS headers:
curl -I https://example.comVerbose TLS connection:
curl -Iv https://example.comCertificate tools may vary, but time correctness is always a first check.
If package updates fail with certificate validity errors, check time before changing repositories.
date
timedatectl
sudo apt updateTime is a hidden dependency in many Linux operations.
Configuration files should be changed with a rollback plan
A configuration change should always have a rollback path.
For a single file:
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%Y-%m-%d-%H-%M).backupFor a whole directory:
sudo tar -czf /root/nginx-config-backup-$(date +%Y-%m-%d-%H-%M).tar.gz /etc/nginxVerify archive:
sudo tar -tzf /root/nginx-config-backup-$(date +%Y-%m-%d-%H-%M).tar.gz | headEdit:
sudo nano /etc/nginx/nginx.confTest:
sudo nginx -tReload:
sudo systemctl reload nginxIf broken, restore:
sudo cp /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf
sudo nginx -t
sudo systemctl reload nginxA rollback plan does not mean you expect failure. It means you respect production systems.
Rescue thinking before disaster happens
A good Linux administrator thinks about recovery before the system breaks.
Questions to answer:
| Recovery question | Why it matters |
|---|---|
| Can I access the server if SSH breaks? | Need console, provider panel or physical access |
| Do I have a recent backup? | Recovery depends on data availability |
| Can I restore the backup? | Untested backups are uncertain |
| Do I know critical service names? | Needed for repair |
| Do I know where configuration lives? | Needed for rollback |
| Do I know disk layout? | Needed for mount and recovery |
| Are important commands installed? | Recovery environment may be minimal |
| Are credentials available securely? | Recovery may need access keys or passwords |
Collect basic system information before disaster:
hostname
lsblk -f
df -h
ip a
ip route
systemctl list-units --type=service --state=running
crontab -lSave it:
mkdir -p ~/ops/reports
{
date
hostname
lsblk -f
df -h
ip a
ip route
systemctl list-units --type=service --state=running
crontab -l
} > ~/ops/reports/system-baseline-$(date +%Y-%m-%d).txtA baseline report helps when comparing future system state.
Creating a system baseline report
A system baseline is a snapshot of important system facts. It helps you understand what changed later.
Create script:
nano ~/ops/scripts/system-baselineScript:
#!/bin/bash
set -euo pipefail
REPORT_DIR="$HOME/ops/reports"
DATE=$(date +%Y-%m-%d-%H-%M)
REPORT="$REPORT_DIR/system-baseline-$DATE.txt"
mkdir -p "$REPORT_DIR"
{
echo "System baseline"
echo "Generated at: $(date)"
echo
echo "Hostname"
hostname
echo
echo "User"
whoami
id
echo
echo "Kernel"
uname -a
echo
echo "Uptime"
uptime
echo
echo "Block devices"
lsblk -f
echo
echo "Filesystems"
df -h
echo
echo "Inodes"
df -i
echo
echo "Network addresses"
ip a
echo
echo "Routes"
ip route
echo
echo "Listening ports"
sudo ss -tulpn
echo
echo "Failed services"
systemctl --failed || true
echo
echo "Running services"
systemctl list-units --type=service --state=running
echo
echo "Timers"
systemctl list-timers || true
echo
echo "User crontab"
crontab -l || true
} > "$REPORT"
echo "Baseline saved to: $REPORT"Make executable:
chmod +x ~/ops/scripts/system-baselineRun:
~/ops/scripts/system-baselineThis gives you a dated record of system state.
The difference between shell variables and service environment variables
A shell variable exists in your current shell.
APP_ENV=productionAn exported variable is passed to child processes.
export APP_ENV=productionBut a systemd service does not automatically inherit your interactive shell environment. If you run:
export APP_ENV=production
sudo systemctl restart myappthat does not necessarily set APP_ENV for the service.
Service environment must be configured in the service unit, an environment file or application-specific configuration.
Check service environment in the unit:
systemctl cat myappCheck process environment carefully:
sudo tr '\0' '\n' < /proc/PID/environReplace PID with the service process ID.
This distinction explains many problems where an application works manually but fails as a service.
Practical workflow: script works manually but fails as service
Check script permissions:
ls -l /home/pat/scripts/myapp.shCheck shebang:
head -n 1 /home/pat/scripts/myapp.shRun manually as the service user:
sudo -u pat /home/pat/scripts/myapp.shCheck service file:
systemctl cat myappCheck working directory:
systemctl show myapp -p WorkingDirectoryCheck logs:
journalctl -u myapp -n 100Check environment:
systemctl show myapp -p EnvironmentInspect process if running:
ps aux | grep myapp
readlink -f /proc/PID/cwd
tr '\0' '\n' < /proc/PID/environCommon causes:
| Symptom | Likely cause |
|---|---|
| File not found | Wrong working directory or relative path |
| Permission denied | Service user lacks access |
| Command not found | PATH differs in service |
| Environment missing | Variables set only in shell |
| Port already in use | Another process is listening |
| Immediate exit | Script ends or fails quickly |
| Restart loop | Service restart policy keeps retrying |
A service is a controlled environment. Treat it differently from an interactive shell.
Practical workflow: robust log report with no match handling
Script fragment:
ERROR_COUNT=$(grep -i "error" "$LOG_FILE" | wc -l || true)Better structure:
ERROR_COUNT=$(grep -i "error" "$LOG_FILE" | wc -l)This usually works because wc completes, but with pipefail enabled, the pipeline may fail if grep finds no match.
With set -o pipefail, use:
ERROR_COUNT=$(grep -i "error" "$LOG_FILE" || true)But that stores lines, not count.
A clear version:
if grep -qi "error" "$LOG_FILE"; then
grep -i "error" "$LOG_FILE" | wc -l
else
echo 0
fiThis is longer but explicit.
In scripts, clarity is better than cleverness.
Real-world Linux administration workflows
Real administration depends on repeatable workflows. Maintenance, backups, web server checks, SSH changes, incident response and server preparation all benefit from structured command sequences.
Linux administration is built from repeatable workflows
A Linux command becomes truly valuable when it becomes part of a repeatable workflow. Knowing that df -h shows disk space is useful. Knowing when to run it, how to interpret it, how to combine it with du, how to investigate a full filesystem and how to document the result is professional administration.
The same applies to almost every Linux command. A command is the smallest unit. A workflow is the real skill.
A workflow has a beginning, a purpose, a safe sequence and a verification step.
For example, this is only a command:
df -hThis is a workflow:
hostname
uptime
df -h
sudo du -h --max-depth=1 /var | sort -h
sudo find /var/log -type f -size +100M -exec ls -lh {} \;
sudo lsof | grep deletedThe workflow answers several connected questions.
| Question | Command |
|---|---|
| Which machine am I on? | hostname |
| How long has it been running? | uptime |
| Which filesystem is full? | df -h |
| Which folder is using space? | du -h --max-depth=1 |
| Which large files exist? | find -size +100M |
| Is deleted data still held open? | `lsof |
This is the difference between command memory and operational understanding.
A Linux system is predictable when you approach it in layers. Files belong to users and groups. Services create processes. Processes open files and ports. Logs record events. Package managers install software. Cron schedules commands. SSH connects systems. The shell connects all these pieces.
Professional Linux work is not based on improvisation. It is based on repeatable inspection, careful change and verification.
A daily Linux server check
A daily server check does not need to be complicated. The purpose is to quickly detect obvious problems before they become serious incidents.
A practical daily check may include identity, uptime, disk space, memory, failed services, listening ports and recent critical logs.
hostname
uptime
df -h
free -h
systemctl --failed
sudo ss -tulpn
journalctl -p warning -n 50Each command has a clear purpose.
| Command | What it checks |
|---|---|
hostname | Confirms the machine identity |
uptime | Shows runtime and load average |
df -h | Shows filesystem usage |
free -h | Shows memory and swap usage |
systemctl --failed | Shows failed systemd units |
ss -tulpn | Shows listening network ports |
journalctl -p warning -n 50 | Shows recent warning-level and higher logs |
This does not replace monitoring. It gives a human-readable snapshot.
A more structured version can be saved as a script.
nano ~/scripts/daily-server-check.shScript content:
#!/bin/bash
echo "Daily Linux server check"
echo
echo "Hostname"
hostname
echo
echo "Current user"
whoami
echo
echo "Uptime and load"
uptime
echo
echo "Disk usage"
df -h
echo
echo "Memory usage"
free -h
echo
echo "Failed services"
systemctl --failed || true
echo
echo "Listening ports"
sudo ss -tulpn
echo
echo "Recent warnings"
journalctl -p warning -n 50Make it executable:
chmod +x ~/scripts/daily-server-check.shRun it:
~/scripts/daily-server-check.shSave output:
~/scripts/daily-server-check.sh 2>&1 | tee ~/daily-server-check.txtThis is a simple but serious administrative habit. It creates awareness of the machine before problems become urgent.
A weekly Linux maintenance workflow
Weekly maintenance is deeper than a daily check. It should review updates, disk usage, logs, backups, services and security-sensitive access.
A safe weekly maintenance workflow begins with observation.
hostname
who
uptime
df -h
free -h
systemctl --failedThen check package updates.
sudo apt update
apt list --upgradableThen upgrade if appropriate.
sudo apt upgrade
sudo apt autoremoveThen verify service state.
systemctl --failed
journalctl -p err -n 100Then check disk usage more deeply.
sudo du -h --max-depth=1 /var | sort -h
sudo du -h --max-depth=1 /home | sort -hThen check large logs.
sudo find /var/log -type f -size +100M -exec ls -lh {} \;Then verify backup location.
ls -lah /home/pat/backups
find /home/pat/backups -type f -mtime -7 -exec ls -lh {} \;A weekly maintenance table may look like this.
| Area | Command | Purpose |
|---|---|---|
| Users | who | See who is logged in |
| Runtime | uptime | Check load and uptime |
| Disk | df -h | Detect full filesystems |
| Memory | free -h | Check RAM and swap |
| Services | systemctl --failed | Detect failed services |
| Updates | apt list --upgradable | Review available updates |
| Logs | journalctl -p err -n 100 | Review recent errors |
| Large files | find /var/log -size +100M | Detect oversized logs |
| Backups | find backups -mtime -7 | Confirm recent backups exist |
The important point is that maintenance must be repeatable. A checklist prevents omissions.
Safe package update policy
Updating Linux packages is important, but updates should still be done thoughtfully. A personal laptop, a lab Raspberry Pi and a production web server do not have the same risk profile.
For a personal machine, this may be acceptable:
sudo apt update
sudo apt upgradeFor a production server, it is better to inspect first.
hostname
uptime
df -h
systemctl --failed
sudo apt update
apt list --upgradableThen decide whether to continue.
sudo apt upgradeAfter the upgrade:
systemctl --failed
journalctl -p err -n 100If a reboot is recommended:
ls /var/run/reboot-requiredIf that file exists, plan a reboot window.
sudo rebootAfter reconnecting:
uptime
systemctl --failedThe correct update policy depends on the environment.
| Environment | Recommended attitude |
|---|---|
| Personal computer | Regular updates are usually straightforward |
| Development VM | Update often, snapshots are useful |
| Raspberry Pi project | Update carefully if it controls hardware |
| Small web server | Inspect before and after updates |
| Production server | Use maintenance windows and rollback planning |
| Critical infrastructure | Test updates before production deployment |
The package manager is safe compared with random manual installation, but any system change can have side effects. Good administrators verify before and after.
Building a backup strategy with Linux commands
A backup is not a backup until it can be restored. Many users create archives. Fewer users verify them. Even fewer test restoration.
A serious Linux backup strategy answers five questions.
| Question | Why it matters |
|---|---|
| What is being backed up? | Avoid missing important data |
| Where is the backup stored? | Avoid storing backup only on the same failing disk |
| How often is it created? | Define acceptable data loss |
| How is it verified? | Detect broken archives |
| How is it restored? | Ensure recovery is possible |
A basic local backup command:
tar -czf /home/pat/backups/site-$(date +%Y-%m-%d).tar.gz /var/www/siteThis creates a compressed archive of /var/www/site.
But a better workflow creates the destination, logs the process and verifies the archive.
mkdir -p /home/pat/backups
tar -czf /home/pat/backups/site-$(date +%Y-%m-%d).tar.gz /var/www/site
tar -tzf /home/pat/backups/site-$(date +%Y-%m-%d).tar.gz > /dev/null
ls -lh /home/pat/backups/site-$(date +%Y-%m-%d).tar.gzThis confirms the archive can be listed.
A better backup script:
#!/bin/bash
set -euo pipefail
SOURCE="/var/www/site"
BACKUP_DIR="/home/pat/backups"
LOG_DIR="/home/pat/logs"
DATE=$(date +%Y-%m-%d-%H-%M)
ARCHIVE="$BACKUP_DIR/site-$DATE.tar.gz"
LOG="$LOG_DIR/site-backup-$DATE.log"
mkdir -p "$BACKUP_DIR"
mkdir -p "$LOG_DIR"
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup started" | tee -a "$LOG"
echo "Source: $SOURCE" | tee -a "$LOG"
echo "Archive: $ARCHIVE" | tee -a "$LOG"
tar -czf "$ARCHIVE" "$SOURCE" 2>&1 | tee -a "$LOG"
if tar -tzf "$ARCHIVE" > /dev/null 2>&1; then
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup verified" | tee -a "$LOG"
else
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup verification failed" | tee -a "$LOG"
exit 1
fi
ls -lh "$ARCHIVE" | tee -a "$LOG"
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup finished" | tee -a "$LOG"This script is still simple, but it includes destination creation, timestamping, logging and verification.
Local backups and remote backups are different
A local backup protects against accidental deletion, failed configuration changes or application-level mistakes. It does not protect well against disk failure, server destruction, theft or ransomware.
A remote backup sends data to another machine.
Using rsync:
rsync -avz /var/www/site/ backupuser@backup-server:/backups/site/For repeated backups, rsync is efficient because it copies only changed files.
A safer preview:
rsync -avz --dry-run /var/www/site/ backupuser@backup-server:/backups/site/Then run the real command:
rsync -avz /var/www/site/ backupuser@backup-server:/backups/site/If the remote directory should exactly mirror the source:
rsync -avz --delete --dry-run /var/www/site/ backupuser@backup-server:/backups/site/Then:
rsync -avz --delete /var/www/site/ backupuser@backup-server:/backups/site/Use --delete only when you really want destination files removed if they no longer exist in the source.
Backup strategy comparison:
| Backup type | Command example | Protects against | Weakness |
|---|---|---|---|
| Local archive | tar -czf backup.tar.gz source | Accidental file changes | Same-disk failure |
| Local sync | rsync -av source/ backup/ | Fast local copy | Same-machine risk |
| Remote sync | rsync -avz source/ server:/backup/ | Server data loss | Needs remote access |
| Remote archive | tar plus scp or rsync | Portable restore point | Larger transfers |
| Snapshot | Filesystem or provider tool | Fast rollback | Platform-specific |
The best backup plan often combines more than one method.
Testing restoration
A backup must be tested. The simplest test is listing archive contents.
tar -tzf backup.tar.gz | head -30A stronger test is extraction into a temporary directory.
mkdir -p /tmp/restore-test
tar -xzf backup.tar.gz -C /tmp/restore-test
tree -L 2 /tmp/restore-testCheck size:
du -sh /tmp/restore-testIf the archive contains a project, inspect key files.
find /tmp/restore-test -maxdepth 3 -type f | head -50For a website backup, you may need to verify:
| Component | Verification |
|---|---|
| Web files | Archive contains expected project files |
| Uploads | User-uploaded data exists |
| Configuration | Required config files exist |
| Permissions | Ownership and permissions can be restored |
| Database | Database dump exists and can be imported |
| Cron jobs | Scheduled tasks are documented |
| Services | Required packages and service names are known |
A file backup alone may not be enough. Many applications need both files and databases.
A typical website backup may require:
tar -czf site-files.tar.gz /var/www/site
mysqldump database_name > database.sql
tar -czf full-site-backup.tar.gz site-files.tar.gz database.sqlDatabase backup commands depend on the database system and authentication setup, but the principle is universal: back up both application files and application data.
Retention: how many backups should be kept
If backups are never removed, they eventually fill the disk. If too few backups are kept, recovery options are limited.
A simple retention policy might keep daily backups for 7 days.
Find backups older than 7 days:
find /home/pat/backups -type f -name "site-*.tar.gz" -mtime +7 -exec ls -lh {} \;Delete only after verifying:
find /home/pat/backups -type f -name "site-*.tar.gz" -mtime +7 -deleteA safer cleanup script logs what it removes.
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/home/pat/backups"
LOG="/home/pat/logs/backup-cleanup.log"
mkdir -p "$(dirname "$LOG")"
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup cleanup started" | tee -a "$LOG"
find "$BACKUP_DIR" -type f -name "site-*.tar.gz" -mtime +7 -exec ls -lh {} \; | tee -a "$LOG"
find "$BACKUP_DIR" -type f -name "site-*.tar.gz" -mtime +7 -delete
echo "$(date +"%Y-%m-%d %H:%M:%S") Backup cleanup finished" | tee -a "$LOG"Schedule weekly:
crontab -eCron line:
0 4 * * 0 /home/pat/scripts/backup-cleanup.sh >> /home/pat/logs/backup-cleanup-cron.log 2>&1Retention should be intentional, not accidental.
Log management is system memory management
Logs are the memory of a Linux system. They explain what happened, when it happened and often why it happened. But logs can also grow until they fill a disk.
The most important log locations on many Linux systems are:
| Location | Purpose |
|---|---|
/var/log/syslog | General system logs on many Debian-based systems |
/var/log/auth.log | Authentication events |
/var/log/kern.log | Kernel messages on some systems |
/var/log/apache2/ | Apache logs |
/var/log/nginx/ | Nginx logs |
/var/log/mysql/ | MySQL or MariaDB logs on some systems |
journalctl | systemd journal access |
/var/log/apt/ | Package management logs |
Basic log inspection:
tail -n 100 /var/log/syslogFollow live:
sudo tail -f /var/log/syslogSearch errors:
grep -i "error" /var/log/syslogRead systemd journal:
journalctl -n 100Show errors only:
journalctl -p err -n 100Follow journal live:
journalctl -fShow logs for one service:
journalctl -u nginx -n 100Follow service logs:
journalctl -u nginx -fLogs are useful only when you know how to filter them. A system may produce thousands of lines. The goal is not to read everything. The goal is to locate relevant evidence.
A practical log analysis workflow
Suppose an application log is located at:
/var/log/myapp/app.logStart by checking size and timestamp.
ls -lh /var/log/myapp/app.logRead the last lines.
tail -n 100 /var/log/myapp/app.logSearch errors.
grep -i "error" /var/log/myapp/app.logCount errors.
grep -i "error" /var/log/myapp/app.log | wc -lShow most common error lines.
grep -i "error" /var/log/myapp/app.log | sort | uniq -c | sort -nr | head -20Follow only new errors.
tail -f /var/log/myapp/app.log | grep -i "error"Create a report.
{
echo "Log report generated at $(date)"
echo
echo "File size"
ls -lh /var/log/myapp/app.log
echo
echo "Total lines"
wc -l /var/log/myapp/app.log
echo
echo "Error count"
grep -i "error" /var/log/myapp/app.log | wc -l
echo
echo "Most common errors"
grep -i "error" /var/log/myapp/app.log | sort | uniq -c | sort -nr | head -20
} > myapp-log-report.txtThis workflow changes logs from noise into structured information.
Log rotation prevents uncontrolled growth
Log rotation means old logs are renamed, compressed and eventually removed according to policy. On many Linux systems, logrotate manages this.
Main configuration:
/etc/logrotate.confService-specific configurations:
/etc/logrotate.d/List configurations:
ls -lah /etc/logrotate.d/Inspect one:
cat /etc/logrotate.d/nginxDebug logrotate without changing anything:
sudo logrotate -d /etc/logrotate.confForce logrotate:
sudo logrotate -f /etc/logrotate.confUse force carefully. Debug mode is safer for inspection.
A typical logrotate configuration may define:
| Directive | Meaning |
|---|---|
daily | Rotate daily |
weekly | Rotate weekly |
rotate 7 | Keep seven rotated logs |
compress | Compress old logs |
missingok | Do not fail if log is missing |
notifempty | Do not rotate empty logs |
create | Create a new log file after rotation |
postrotate | Run commands after rotation |
If logs grow too large, do not only delete them. Ask why rotation did not manage them.
Possible causes:
| Cause | Explanation |
|---|---|
| Application writes outside expected log path | logrotate config does not cover it |
| Rotation frequency is too low | Logs grow faster than policy |
| Compression disabled | Old logs use more space |
| Service keeps old file handle | Service may need reload after rotation |
| Application log level too verbose | Too much unnecessary logging |
| Error loop | Application repeatedly logs the same failure |
Log growth is often a symptom, not the root cause.
Truncating active logs safely
If a log file is huge and active, deleting it may not immediately free space if the process still holds the file open. Truncating keeps the same file but reduces its size.
First archive if needed:
sudo cp /var/log/myapp/app.log /var/log/myapp/app.log.$(date +%Y-%m-%d-%H-%M).backupThen truncate:
sudo truncate -s 0 /var/log/myapp/app.logCheck:
ls -lh /var/log/myapp/app.logIf disk space still does not return after deletion or truncation, check deleted open files.
sudo lsof | grep deletedA process may need restart or reload.
sudo systemctl restart myappOnly restart after understanding the operational impact.
Web server administration with Linux commands
A Linux web server is a combination of packages, configuration files, services, processes, ports, logs, permissions and application files.
For Apache, common commands include:
systemctl status apache2
sudo systemctl restart apache2
sudo apache2ctl configtest
journalctl -u apache2 -n 100For Nginx:
systemctl status nginx
sudo systemctl restart nginx
sudo nginx -t
journalctl -u nginx -n 100A web server also needs listening ports.
sudo ss -tulpn | grep ':80'
sudo ss -tulpn | grep ':443'And logs.
tail -n 100 /var/log/nginx/access.log
tail -n 100 /var/log/nginx/error.logor:
tail -n 100 /var/log/apache2/access.log
tail -n 100 /var/log/apache2/error.logAnd file permissions.
ls -ld /var/www
ls -lah /var/www/site
namei -l /var/www/site/index.htmlA web server problem is rarely “just one thing.” It may be a service problem, port problem, permission problem, configuration problem, DNS problem, firewall problem or application problem.
A complete web server down workflow
If a website is down, avoid random restarts. Use a layered workflow.
Check system identity and load.
hostname
uptime
df -h
free -hCheck service.
For Nginx:
systemctl status nginxFor Apache:
systemctl status apache2Check configuration.
sudo nginx -tor:
sudo apache2ctl configtestCheck ports.
sudo ss -tulpn | grep ':80'
sudo ss -tulpn | grep ':443'Check local HTTP response.
curl -I http://localhostCheck logs.
journalctl -u nginx -n 100or:
journalctl -u apache2 -n 100Check web root.
ls -lah /var/www
find /var/www -maxdepth 3 -type f -mmin -60 -exec ls -lh {} \;Check permissions.
namei -l /var/www/site/index.htmlOnly after diagnosis, restart or reload.
sudo systemctl reload nginxor:
sudo systemctl restart nginxThen verify.
systemctl status nginx
curl -I http://localhost
sudo ss -tulpn | grep ':80'The workflow moves from system health to service state, configuration, ports, response, logs, files and permissions.
Web deployment with rsync
A simple deployment often means copying local build files to a remote web root.
Preview first:
rsync -avz --delete --dry-run ./public/ deploy@server:/var/www/site/If correct:
rsync -avz --delete ./public/ deploy@server:/var/www/site/Then verify remotely:
ssh deploy@server "ls -lah /var/www/site | head"Reload web server if needed:
ssh deploy@server "sudo systemctl reload nginx"The trailing slash is critical.
| Source | Meaning |
|---|---|
./public | Copy the directory itself |
./public/ | Copy the contents of the directory |
Deployment should be previewed because --delete can remove destination files.
A safer deployment script:
#!/bin/bash
set -euo pipefail
SOURCE="./public/"
DESTINATION="deploy@server:/var/www/site/"
echo "Previewing deployment"
rsync -avz --delete --dry-run "$SOURCE" "$DESTINATION"
echo "If the preview is correct, run:"
echo "rsync -avz --delete \"$SOURCE\" \"$DESTINATION\""For real automation, you can add a confirmation prompt.
read -r -p "Deploy now? Type yes to continue: " ANSWER
if [ "$ANSWER" = "yes" ]; then
rsync -avz --delete "$SOURCE" "$DESTINATION"
else
echo "Deployment cancelled"
fiThis prevents accidental deployment.
Permissions for web projects
Web permissions are one of the most common Linux problems. Too strict, and the application cannot read or write. Too open, and the server becomes insecure.
A common baseline for static files:
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;This means directories are enterable and listable, files are readable, and only owners can write.
For upload or cache directories, the web server may need write access.
Example:
sudo chown -R www-data:www-data /var/www/site/uploads
sudo chmod -R 755 /var/www/site/uploadsDepending on the application, group-based access may be better.
sudo chown -R deploy:www-data /var/www/site
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;
sudo chmod -R 775 /var/www/site/storageThe exact setup depends on the application.
Key questions:
| Question | Why it matters |
|---|---|
| Which user deploys files? | Determines owner |
| Which user runs the web server? | Determines read/write needs |
| Which directories need writing? | Cache, uploads, sessions, generated files |
| Which files contain secrets? | Must not be world-readable if unnecessary |
| Does the application require executable files? | Most web files do not need execute permission |
Avoid the lazy fix:
sudo chmod -R 777 /var/www/siteThis grants everyone full access and is almost never the correct solution.
Diagnosing web permission errors
If a web app cannot write to a directory, identify the web server user.
For Apache on many Debian-based systems:
ps aux | grep apacheFor Nginx:
ps aux | grep nginxFor PHP-FPM:
ps aux | grep php-fpmCommon web users include:
| User | Common context |
|---|---|
www-data | Debian and Ubuntu web services |
nginx | Some Nginx-based systems |
apache | Some Apache-based distributions |
deploy | Deployment user, not necessarily runtime user |
Test as the web user.
sudo -u www-data ls -lah /var/www/siteTest writing to a cache directory.
sudo -u www-data touch /var/www/site/cache/test-fileIf this fails, inspect path permissions.
namei -l /var/www/site/cache/test-fileThen fix the specific directory, not the whole server.
sudo chown -R www-data:www-data /var/www/site/cache
sudo chmod -R 755 /var/www/site/cacheOr if group write is intended:
sudo chown -R deploy:www-data /var/www/site/cache
sudo chmod -R 775 /var/www/site/cacheThe correct fix depends on the ownership model.
SSH hardening begins with visibility
SSH is one of the most important services on a Linux server because it controls remote administrative access.
Check SSH status.
systemctl status sshor:
systemctl status sshdCheck listening port.
sudo ss -tulpn | grep ':22'Check recent SSH logs.
journalctl -u ssh -n 100or:
journalctl -u sshd -n 100On many Debian-based systems:
sudo tail -n 100 /var/log/auth.logSearch failed logins.
sudo grep -i "failed password" /var/log/auth.log | tail -50Count failed logins.
sudo grep -i "failed password" /var/log/auth.log | wc -lTop sources may require log-format-specific parsing, but a common starting point is:
sudo grep -i "failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | headAlways inspect sample lines first because log formats vary.
sudo grep -i "failed password" /var/log/auth.log | headSecurity begins with knowing what is happening.
Safe SSH configuration editing
Before editing SSH configuration, keep an existing SSH session open.
Back up the file.
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.$(date +%Y-%m-%d-%H-%M).backupEdit.
sudo nano /etc/ssh/sshd_configTest syntax.
sudo sshd -tIf the test returns no output, syntax is usually valid.
Reload SSH.
sudo systemctl reload sshor:
sudo systemctl reload sshdOpen a second terminal and test login.
ssh user@serverOnly close the original session after confirming the new login works.
If the new login fails, restore from backup in the still-open original session.
sudo cp /etc/ssh/sshd_config.backup /etc/ssh/sshd_config
sudo sshd -t
sudo systemctl reload sshSSH configuration mistakes can lock you out. The safe workflow preserves recovery access.
Firewall changes must protect your current access
A firewall can protect a server, but it can also block your own SSH session if configured carelessly.
If using UFW, always allow SSH before enabling.
sudo ufw allow ssh
sudo ufw status
sudo ufw enableIf SSH uses a custom port:
sudo ufw allow 2222/tcp
sudo ufw enableAllow web traffic:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpCheck status:
sudo ufw status verboseShow numbered rules:
sudo ufw status numberedDelete a rule:
sudo ufw delete 3Firewall troubleshooting must include both server-side and external tests.
On the server:
sudo ss -tulpn
sudo ufw statusFrom another machine:
nc -vz server 22
nc -vz server 80
curl -I http://serverA service can be listening locally but blocked externally by firewall, router, cloud security group or network policy.
Security baseline for a small Linux server
A simple security baseline is not a complete security program, but it improves basic posture.
Start with updates.
sudo apt update
sudo apt upgradeCreate a normal user with sudo access if needed.
sudo adduser adminuser
sudo usermod -aG sudo adminuserUse SSH keys.
ssh-keygen -t ed25519
ssh-copy-id adminuser@serverCheck SSH login.
ssh adminuser@serverConfigure firewall carefully.
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verboseCheck running services.
systemctl --type=service --state=running
sudo ss -tulpnDisable services that are not needed.
sudo systemctl disable --now service-nameCheck failed services.
systemctl --failedReview logs.
journalctl -p warning -n 100Set up backups.
mkdir -p ~/backupsCreate and test a backup script.
Security is not one command. It is a collection of habits: least privilege, updates, key-based access, firewall rules, minimal services, logs and backups.
Practical incident workflow
When something breaks, use a structured incident workflow.
First snapshot:
date
hostname
whoami
uptime
df -h
free -h
systemctl --failedThen define the symptom.
| Symptom | First commands |
|---|---|
| Website down | systemctl status nginx, curl -I localhost, ss -tulpn |
| SSH failing | systemctl status ssh, `ss -tulpn |
| Disk full | df -h, du, find, lsof deleted |
| High CPU | uptime, htop, ps --sort=-%cpu |
| High memory | free -h, ps --sort=-%mem, htop |
| Package failure | apt update, apt logs, disk space |
| Permission error | whoami, id, ls -l, namei -l |
| Cron failure | crontab -l, logs, absolute paths, environment |
Collect evidence:
journalctl -n 200or service-specific:
journalctl -u service-name -n 200Make one change at a time.
Verify after each change.
Document what worked.
This is slower than guessing at first, but much faster when problems are serious.
Building a personal Linux operations folder
A useful professional habit is keeping your own operational scripts and notes.
Create structure:
mkdir -p ~/ops/{scripts,logs,reports,notes}Create notes:
nano ~/ops/notes/linux-commands.mdAdd commonly used commands.
Disk check:
df -h
du -h --max-depth=1 /var | sort -h
Service check:
systemctl status nginx
journalctl -u nginx -n 100
Ports:
sudo ss -tulpn
Large files:
find /var -type f -size +100M -exec ls -lh {} \;
Failed SSH logins:
grep -i "failed password" /var/log/auth.logSearch notes:
grep -i "disk" ~/ops/notes/linux-commands.mdCreate scripts:
nano ~/ops/scripts/server-summary.shMake scripts executable:
chmod +x ~/ops/scripts/server-summary.shAdd scripts to PATH if desired.
echo 'export PATH="$PATH:$HOME/ops/scripts"' >> ~/.bashrc
source ~/.bashrcThis turns learning into a personal toolkit.
A complete server summary script
Create:
nano ~/ops/scripts/server-summaryScript:
#!/bin/bash
echo "Server summary"
echo
echo "Date"
date
echo
echo "Hostname"
hostname
echo
echo "User"
whoami
id
echo
echo "Uptime"
uptime
echo
echo "Disk usage"
df -h
echo
echo "Inode usage"
df -i
echo
echo "Memory"
free -h
echo
echo "Failed services"
systemctl --failed || true
echo
echo "Listening ports"
sudo ss -tulpn
echo
echo "Top CPU processes"
ps aux --sort=-%cpu | head -10
echo
echo "Top memory processes"
ps aux --sort=-%mem | head -10
echo
echo "Recent errors"
journalctl -p err -n 50 || trueMake executable:
chmod +x ~/ops/scripts/server-summaryRun:
server-summarySave report:
server-summary 2>&1 | tee ~/ops/reports/server-summary-$(date +%Y-%m-%d-%H-%M).txtThis script uses many Linux fundamentals in one place: identity, users, disk, inodes, memory, services, ports, processes and logs.
A complete backup and cleanup script
Create:
nano ~/ops/scripts/site-backupScript:
#!/bin/bash
set -euo pipefail
SOURCE="/var/www/site"
BACKUP_DIR="/home/pat/backups/site"
LOG_DIR="/home/pat/ops/logs"
RETENTION_DAYS="14"
DATE=$(date +%Y-%m-%d-%H-%M)
ARCHIVE="$BACKUP_DIR/site-$DATE.tar.gz"
LOG="$LOG_DIR/site-backup-$DATE.log"
mkdir -p "$BACKUP_DIR"
mkdir -p "$LOG_DIR"
{
echo "Backup started at $(date +"%Y-%m-%d %H:%M:%S")"
echo "Source: $SOURCE"
echo "Archive: $ARCHIVE"
echo
if [ ! -d "$SOURCE" ]; then
echo "Source directory does not exist"
exit 1
fi
echo "Creating archive"
tar -czf "$ARCHIVE" "$SOURCE"
echo "Verifying archive"
tar -tzf "$ARCHIVE" > /dev/null
echo "Archive size"
ls -lh "$ARCHIVE"
echo
echo "Removing backups older than $RETENTION_DAYS days"
find "$BACKUP_DIR" -type f -name "site-*.tar.gz" -mtime +"$RETENTION_DAYS" -exec ls -lh {} \;
find "$BACKUP_DIR" -type f -name "site-*.tar.gz" -mtime +"$RETENTION_DAYS" -delete
echo
echo "Backup finished at $(date +"%Y-%m-%d %H:%M:%S")"
} 2>&1 | tee "$LOG"Make executable:
chmod +x ~/ops/scripts/site-backupRun:
site-backupSchedule:
crontab -eAdd:
0 2 * * * /home/pat/ops/scripts/site-backup >> /home/pat/ops/logs/site-backup-cron.log 2>&1This script includes backup creation, verification, retention cleanup and logging.
A complete log report script
Create:
nano ~/ops/scripts/log-reportScript:
#!/bin/bash
set -euo pipefail
LOG_FILE="${1:-/var/log/syslog}"
REPORT_DIR="/home/pat/ops/reports"
DATE=$(date +%Y-%m-%d-%H-%M)
REPORT="$REPORT_DIR/log-report-$DATE.txt"
mkdir -p "$REPORT_DIR"
if [ ! -f "$LOG_FILE" ]; then
echo "Log file does not exist: $LOG_FILE"
exit 1
fi
{
echo "Log report"
echo "Generated at: $(date)"
echo "Log file: $LOG_FILE"
echo
echo "File size"
ls -lh "$LOG_FILE"
echo
echo "Total lines"
wc -l "$LOG_FILE"
echo
echo "Error count"
grep -i "error" "$LOG_FILE" | wc -l || true
echo
echo "Warning count"
grep -i "warning" "$LOG_FILE" | wc -l || true
echo
echo "Most common error lines"
grep -i "error" "$LOG_FILE" | sort | uniq -c | sort -nr | head -20 || true
echo
echo "Last 50 lines"
tail -n 50 "$LOG_FILE"
} > "$REPORT"
echo "Report created: $REPORT"Make executable:
chmod +x ~/ops/scripts/log-reportRun with default log:
log-reportRun with specific log:
log-report /var/log/nginx/error.logThis is a reusable log analysis tool.
A complete port investigation workflow
When a port conflict appears, use this sequence.
Suppose port 8080 is already in use.
sudo ss -tulpn | grep ':8080'Then:
sudo lsof -i :8080Find process details:
ps -p PID -fReplace PID with the actual process ID.
Find executable:
readlink -f /proc/PID/exeFind working directory:
readlink -f /proc/PID/cwdCheck whether it belongs to a service:
systemctl status service-nameIf unknown, inspect process tree:
ps auxf | grep PIDOnly then decide whether to stop it.
kill PIDor, if it is a service:
sudo systemctl stop service-namePrefer stopping the service rather than killing its process if it is managed by systemd.
A complete disk cleanup workflow
Disk cleanup must be careful because deleting the wrong files can break applications or remove evidence.
Start:
df -h
df -iFind large top-level directories:
sudo du -h --max-depth=1 / | sort -hIf /var is large:
sudo du -h --max-depth=1 /var | sort -hIf /var/log is large:
sudo du -h --max-depth=1 /var/log | sort -hFind large files:
sudo find /var/log -type f -size +100M -exec ls -lh {} \;Check old compressed logs:
sudo find /var/log -type f -name "*.gz" -mtime +30 -exec ls -lh {} \;Remove only if safe:
sudo find /var/log -type f -name "*.gz" -mtime +30 -deleteCheck deleted open files:
sudo lsof | grep deletedCheck again:
df -hThis workflow avoids deleting random files from /usr, /bin, /etc or application directories.
A complete permission repair workflow
Permission repair should begin with diagnosis.
Suppose the problematic path is:
/var/www/site/storage/cache/file.txtCheck identity:
whoami
idCheck file:
ls -l /var/www/site/storage/cache/file.txtCheck directory:
ls -ld /var/www/site/storage/cacheCheck full path:
namei -l /var/www/site/storage/cache/file.txtCheck runtime user:
ps aux | grep php
ps aux | grep nginx
ps aux | grep apacheTest as runtime user:
sudo -u www-data ls -l /var/www/site/storage/cache/file.txtTest write if needed:
sudo -u www-data touch /var/www/site/storage/cache/test-fileApply minimal fix.
sudo chown -R www-data:www-data /var/www/site/storage/cacheor group-based:
sudo chown -R deploy:www-data /var/www/site/storage/cache
sudo chmod -R 775 /var/www/site/storage/cacheVerify:
sudo -u www-data touch /var/www/site/storage/cache/test-file
ls -l /var/www/site/storage/cache/test-fileRemove test file:
sudo rm /var/www/site/storage/cache/test-fileThe repair is complete only after verification.
A complete SSH access workflow
Before changing SSH settings, document current access.
whoami
hostname
systemctl status ssh
sudo ss -tulpn | grep ':22'Check current SSH config syntax.
sudo sshd -tBack up config.
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.$(date +%Y-%m-%d-%H-%M).backupEdit.
sudo nano /etc/ssh/sshd_configTest.
sudo sshd -tReload.
sudo systemctl reload sshTest new login from a second terminal.
ssh user@serverIf login works, close old session later. If not, restore.
This workflow prevents lockout.
A complete new server preparation workflow
A fresh Debian-based server can be prepared with a structured sequence.
Update package information.
sudo apt update
sudo apt upgradeInstall basic tools.
sudo apt install htop tree curl wget unzip zip rsync screenCreate administrative user if needed.
sudo adduser adminuser
sudo usermod -aG sudo adminuserSet hostname.
sudo hostnamectl set-hostname web-prod-01Configure firewall carefully.
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verboseCheck services.
systemctl --failed
sudo ss -tulpnCreate operations folders.
mkdir -p ~/ops/{scripts,logs,reports,notes}Create first server summary.
hostname
uptime
df -h
free -h
systemctl --failedSet up backups before deploying important data.
This is not a complete hardening guide, but it creates a controlled starting point.
A complete learning roadmap for Linux commands
Linux should be learned in layers.
| Stage | Focus | Commands |
|---|---|---|
| Orientation | Where am I and what is here? | pwd, cd, ls, tree |
| File basics | Create, copy, move, remove | mkdir, touch, cp, mv, rm |
| Reading files | Inspect text | cat, less, head, tail |
| Searching | Find text and files | grep, find |
| Permissions | Understand access | chmod, chown, id, namei |
| Packages | Manage software | apt, dpkg |
| Services | Manage daemons | systemctl, service, journalctl |
| Processes | Inspect running programs | ps, htop, kill |
| Disk | Manage space | df, du, mount, umount |
| Networking | Diagnose connectivity | ip, ping, ssh, ss, rsync |
| Text processing | Analyze logs | awk, sed, cut, wc, sort, uniq |
| Automation | Repeat work | cron, scripts, screen, watch |
| Troubleshooting | Think in layers | all of the above |
Do not try to learn everything at once. Learn workflows. Use a practice directory. Build small scripts. Read logs. Break problems into layers.
Key takeaways
Linux command knowledge becomes operational practice when it is organized into repeatable checks, backups, deployment steps and incident workflows.
The central lessons are:
| Lesson | Meaning |
|---|---|
| Commands become valuable inside workflows | One command is useful, a repeatable process is stronger |
| Maintenance should be structured | Check identity, load, disk, memory, services, logs and updates |
| Backups must be verified | An archive that cannot be restored is not a backup |
| Logs must be managed | Logs are evidence, but uncontrolled logs can fill disks |
| Web servers are layered systems | Files, permissions, services, ports, logs and configuration interact |
| SSH must be handled carefully | Remote access mistakes can lock you out |
| Firewalls require access planning | Always allow SSH before enabling remote firewall rules |
| Permissions should follow least privilege | Avoid 777 and fix only what needs fixing |
| Dangerous commands need previews | Use --dry-run, find -print, ls, backups and confirmation |
| Troubleshooting should be evidence-based | Inspect first, change second, verify third |
Linux expertise is not measured by how many commands someone can type from memory. It is measured by how safely and accurately they can move from symptom to cause to solution.
A strong Linux user does not simply know grep, find, chmod, systemctl, rsync or tar. A strong Linux user knows when to use them, how to combine them, how to avoid damage and how to prove that the result is correct.
Conclusion: building lasting Linux command competence
Linux command competence is not built by memorizing hundreds of isolated commands. It is built by understanding how Linux represents files, users, processes, services, storage, network connections, permissions and logs, and then using commands to inspect those structures before changing them.
A strong Linux workflow always starts with context. Confirm the current directory, inspect the target, read the relevant status or log, make the smallest safe change and verify the result. This habit matters more than knowing a long list of options by memory.
The commands in this guide form a practical operating language. Navigation commands show where you are. File commands change data. Search commands reveal patterns. Permission commands explain access. Package commands manage software. System commands control services. Network commands connect machines. Automation commands turn repeated work into reliable procedures.
When these layers come together, the command line stops feeling like a collection of cryptic instructions. It becomes a precise, observable and repeatable way to understand Linux systems.
The best long-term approach is simple: practice in a safe directory, read manual pages, build small workflows, keep notes, test backups, document commands that worked, and troubleshoot from evidence rather than from panic. That is how Linux commands become durable system knowledge.
Questions that turn Linux commands into real working knowledge
Linux commands are text-based instructions used to interact with a Linux operating system through a terminal and a shell. A command can list files, move between directories, install software, inspect running processes, read logs, check disk space, manage permissions, connect to remote servers or automate repeated tasks. Linux commands are important because they expose the operating system in a precise and repeatable way. Instead of relying on a graphical interface, a user can run a clear instruction, copy it into documentation, place it in a script or repeat it on another server. This is why Linux commands are essential for system administration, web hosting, DevOps, cybersecurity, software development, cloud infrastructure and troubleshooting.
Linux commands remain important because Linux powers servers, cloud instances, containers, routers, embedded devices, development environments and many production systems that do not use a graphical desktop. In these environments, the terminal is often the main control interface. Commands are also faster to repeat, easier to document and more precise than graphical actions. A command such as systemctl status nginx clearly checks the state of a service, while tail -f /var/log/syslog lets an administrator observe system events in real time. Linux command-line knowledge is not outdated. It is one of the core skills behind modern infrastructure, automation, hosting and technical troubleshooting.
A terminal is the interface where you type commands and see output. A shell is the command interpreter that reads what you type, expands variables or wildcards, handles pipes and redirection, and starts programs. A command is the actual instruction being executed. For example, in ls -lah /var/log, the terminal is where you enter the line, the shell interprets it, ls is the command, -lah are options and /var/log is the argument. This distinction matters because many behaviors users experience are caused by the shell, not by the command itself. Quoting, wildcard expansion, environment variables and pipes are shell features that shape how commands run.
The best way to learn Linux commands is to start with context before memorization. A beginner should first understand the current directory, absolute paths, relative paths, command options, arguments and permissions. Then it makes sense to learn safe inspection commands such as pwd, ls, cd, cat, less, head, tail, man and tree. These commands teach how to move through the system and read information without changing anything. After that, file operations such as cp, mv, mkdir, touch and rm become easier to understand. Linux should be learned as a set of practical workflows, not as a random list of commands.
The pwd command means print working directory. It shows the full path of the directory where the current shell session is located. This matters because many Linux commands operate relative to the current directory. If you run rm notes.txt, Linux looks for notes.txt in the current location. If you run cp report.txt backup/, both paths may be interpreted relative to where you are. Running pwd before editing, moving, copying or deleting files is a simple but professional safety habit. It prevents many beginner mistakes because it confirms the context before any action is taken.
The cd command changes the current directory. It is one of the most frequently used Linux commands because it lets users move through the filesystem. For example, cd /var/log moves to the system log directory, while cd Documents moves into a directory named Documents relative to the current location. Running cd without arguments usually returns the user to their home directory. cd .. moves one level up, and cd – returns to the previous directory. Since cd changes the context for later commands, it should often be followed by pwd and ls -lah before making changes.
The ls command lists files and directories. By itself, it shows visible entries in the current directory. With options, it becomes much more informative. ls -l shows a long listing with permissions, ownership, size and modification time. ls -a includes hidden files that begin with a dot. ls -h shows human-readable sizes when combined with long format. A common practical form is ls -lah, which gives a clear overview of files, hidden entries, owners, permissions and sizes. ls is one of the first commands to run before copying, deleting, editing or troubleshooting files because it shows what actually exists.
The tree command displays files and directories in a visual tree structure. It helps users understand how a directory is organized below the current level. For example, tree -L 2 shows two levels of structure, which is often enough to understand a project, backup folder or website directory without overwhelming output. tree is useful before deleting, archiving or moving a directory because it lets you inspect the structure first. It may not be installed by default on minimal systems. If it is unavailable, find . -maxdepth 2 can provide a similar overview, although the output is less visually organized.
An absolute path is a full path that starts at the root directory /. For example, /home/pat/Documents/report.txt is an absolute path because it describes the complete location from the top of the filesystem tree. Absolute paths work no matter where the current shell is located. This makes them especially useful in scripts, system administration, documentation and scheduled tasks. If a script uses an absolute path, it does not depend on the current directory from which the script was started. Absolute paths reduce ambiguity and are safer when working with configuration files, backups, services, logs and system directories.
A relative path describes a location starting from the current working directory. For example, if the current directory is /home/pat, the relative path Documents/report.txt refers to /home/pat/Documents/report.txt. Relative paths are convenient because they are shorter, but they depend entirely on context. The same command can affect different files if it is run from different directories. This is why pwd is so important. Before using relative paths with commands such as rm, cp, mv, tar or rsync, users should confirm where they are. Relative paths are efficient for daily work, but they require awareness.
The Linux filesystem is described as a tree because everything begins at a single root directory written as /, and all files, directories, devices and mounted filesystems appear somewhere below it. Unlike systems that organize storage primarily by drive letters, Linux uses one unified hierarchy. A user’s home folder may be under /home, system configuration under /etc, logs under /var/log, programs under /usr/bin and devices under /dev. Additional disks and USB drives can be mounted into this same tree. Understanding the tree structure is essential because nearly every Linux command works with paths inside this hierarchy.
The root directory is the top-level directory of the Linux filesystem and is written as /. Every absolute path begins there. Directories such as /home, /etc, /var, /usr, /boot, /dev and /tmp are located under the root directory. The root directory should not be confused with the root user. The root directory is a filesystem location, while the root user is the administrative superuser account. This distinction is important because “go to root” may mean changing to /, while “run as root” means using administrative privileges. The two concepts are related but not the same.
The root user is the administrative superuser account in Linux. It has the highest level of authority and can modify system files, install software, manage users, change ownership, restart services and remove protected data. This power is necessary for system administration, but it is also dangerous when used carelessly. A mistake made as root can damage the entire operating system. Modern Linux workflows often avoid direct root login and use sudo for specific administrative commands instead. This gives users elevated privileges only when needed and makes it easier to control and audit administrative actions.
The sudo command runs another command with elevated privileges, usually as the root user. For example, sudo apt update refreshes package lists with administrative rights. sudo is required for many system-level tasks, including installing software, editing files under /etc, restarting services, mounting filesystems and changing ownership of protected files. However, sudo should not be used automatically for every command. If a task can be done as a normal user, it is usually safer to do it that way. Using sudo unnecessarily increases the risk of damaging system files or hiding the real cause of permission errors.
Beginners should avoid using sudo for everything because it bypasses normal Linux permission protections. Those protections exist to prevent accidental damage to system files and other users’ data. When a command fails with “permission denied,” the correct response is not always to add sudo. The better response is to understand why permission was denied. The user may be in the wrong directory, targeting the wrong file, using the wrong account or trying to perform an action that should not be performed. sudo is powerful, but it should be used deliberately. A safe Linux user understands the task before elevating privileges.
The man command opens the manual page for another command. For example, man ls shows official documentation for the ls command, including its syntax, options and behavior. Manual pages are one of the most reliable sources of Linux command information because they are installed with the system and correspond to the tools available on that machine. Inside many manual pages, Space moves forward, b moves backward, /word searches and q exits. Although manual pages can be dense at first, learning to read them is one of the best ways to become independent on Linux. Good users do not memorize everything. They know how to find accurate information.
Many Linux commands support the –help option, which prints a quick usage summary in the terminal. For example, ls –help shows common options and syntax for ls. This is useful when you need a fast reminder and do not want to open a full manual page. The output is usually shorter and less detailed than man, but it is often enough for basic usage. A practical learning workflow is to use command –help for quick reference, man command for deeper documentation and safe test files for experimentation. Not every command behaves identically, but –help is a widely used convention.
The type command shows how the shell interprets a command name. It can reveal whether a command is a shell built-in, alias, function, keyword or external executable. For example, type cd may show that cd is a shell built-in, while type grep may show that grep is located at /usr/bin/grep. This matters because built-ins affect the current shell directly, while external commands run as separate programs. type is also useful when a command behaves unexpectedly. An alias may be changing the default behavior, or a different executable may be found earlier in the PATH.
The command -v command shows how a command name will be resolved by the shell. For example, command -v python3 may return /usr/bin/python3. It is useful for checking whether a command exists, where it is located and whether the shell can find it through the PATH. In scripts, command -v is often preferred over which because it is more reliable in POSIX-style shell logic. If command -v nginx returns nothing, the shell cannot find an executable named nginx in the current command search path. This helps diagnose “command not found” errors.
The mkdir command creates directories. For example, mkdir projects creates a directory named projects in the current location. With an absolute path, mkdir /home/pat/projects creates the directory at that exact location if the user has permission. The -p option is especially useful because it creates parent directories if they do not already exist. For example, mkdir -p projects/linux/scripts creates the full nested structure. mkdir is usually safe, but users should still understand where they are creating directories. Creating folders inside system locations such as /etc, /usr, /var or /opt may require administrative privileges and should be intentional.
The touch command creates an empty file if it does not exist or updates the timestamp of an existing file. For example, touch notes.txt creates an empty file named notes.txt in the current directory. It is useful for placeholders, test files, scripts and timestamp-based workflows. However, touch does not open an editor and does not add content. If a user wants to write text immediately, an editor such as nano may be more practical. In scripts, touch is often used to ensure that a file exists before another command writes to it or checks its modification time.
nano is a terminal-based text editor used to create and edit text files. It is popular with beginners because its basic keyboard shortcuts are visible at the bottom of the screen. For example, nano notes.txt opens notes.txt for editing or creates it if it does not exist. nano is often used for quick notes, shell scripts and configuration files. When editing protected system files, it may need sudo, such as sudo nano /etc/hosts. The main safety rule is to understand what file you are editing before saving. This is especially important under /etc, where configuration mistakes can affect services or networking.
The cat command displays file contents in the terminal and can concatenate multiple files. For example, cat notes.txt prints the entire file to standard output. It is useful for short files, quick checks and simple content inspection. However, cat is not ideal for long files because it prints everything at once and can flood the terminal. For large files, logs or configuration files, less is usually better because it allows scrolling and searching. cat is also commonly used in pipelines, although many commands can read files directly. Use cat when you want fast complete output from a manageable text file.
The less command opens a file or command output in an interactive pager. It lets users scroll, search and inspect long text without dumping everything into the terminal at once. For example, less /var/log/syslog opens the system log for controlled reading. Inside less, /pattern searches, n moves to the next match and q exits. less is especially useful for logs, documentation, configuration files and long command output. It is safer and more practical than cat for large files because it does not overwhelm the terminal. Many manual pages and command outputs are displayed through a pager like less.
The head command displays the beginning of a file. By default, it usually shows the first ten lines. For example, head file.txt shows the start of the file, while head -n 20 file.txt shows the first twenty lines. head is useful when inspecting structured files because headers, column names or format examples often appear near the top. It is also useful for quickly checking whether a file contains the expected kind of data. Since head only reads data and does not modify files, it is a safe inspection command. It is commonly used before deeper filtering, parsing or processing.
The tail command displays the end of a file. By default, it usually shows the last ten lines, which makes it especially useful for log files because the newest entries are commonly written at the bottom. For example, tail -n 50 /var/log/syslog shows the last fifty lines of the system log. The option -f follows a file as it grows, so tail -f /var/log/syslog displays new log entries in real time. This is one of the most practical Linux troubleshooting commands because it allows a user to restart a service, reproduce an error or trigger an application action and immediately watch what the system records.
The cp command copies files and directories. For example, cp report.txt backup/ copies the file report.txt into the backup directory. To copy a directory and its contents, the recursive option is usually required, such as cp -r project project-backup. For more careful copying, cp -i asks before overwriting existing files, while cp -a preserves attributes such as permissions, timestamps and symbolic links more completely. cp is often used before editing configuration files because a backup copy gives the user a way to restore the previous version. The main safety rule is to verify both the source and the destination before running the command.
The mv command moves or renames files and directories. For example, mv oldname.txt newname.txt renames a file, while mv report.txt archive/ moves the file into the archive directory. Unlike cp, mv does not leave the original file in place. This makes it efficient but also more risky when used without checking paths. If the destination already exists, the command may overwrite or replace data depending on the situation and options. The interactive form mv -i can reduce risk by asking before overwriting. mv is also commonly used in deployment and configuration workflows where a prepared file is moved into its final location.
The rm command removes files. For example, rm old.txt deletes the file named old.txt from the current directory. To remove directories, users often add recursive options, but this should be done carefully because command-line deletion usually does not move files to a trash folder. Once data is removed with rm, recovery may be difficult or impossible. Safer habits include running pwd to confirm the current location, using ls -lah to inspect the target, checking directory size with du -sh and using rm -i for confirmation. rm is a normal Linux command, but it should be treated as a final action after inspection.
rm -rf is dangerous because it combines recursive deletion with forced deletion. The -r option removes directories and everything inside them, while -f suppresses many warnings and ignores missing files. If the target path is wrong, too broad or unexpectedly expanded by the shell, the command can remove a large amount of data very quickly. The risk becomes much greater when used with sudo because administrative privileges may allow system files to be deleted as well. Before using rm -rf, inspect the target with pwd, ls -lah, du -sh and preferably tree or find. Professional Linux users respect this command because it obeys exactly what was typed.
The grep command searches text for lines that match a pattern. For example, grep “error” app.log prints lines from app.log that contain the word error. It is one of the most important Linux commands because Linux stores a great deal of useful information as text, including logs, configuration files, command output and scripts. Common options include grep -i for case-insensitive search, grep -r for recursive search through directories, grep -n for line numbers and grep -v for inverted matches. grep is especially powerful in pipelines because it can filter large command outputs into only the lines that matter.
The find command searches the filesystem for files and directories based on criteria such as name, type, size, modification time, ownership and location. For example, find /home/pat -name “*.txt” searches for text files under /home/pat. find . -type f lists files below the current directory, while find . -type d lists directories. It can also find large files with -size, recently changed files with -mtime and run commands on results with -exec. find is essential for cleanup, troubleshooting, audits, automation and file discovery because it searches through directory trees with precise conditions.
grep searches inside text, while find searches for filesystem objects such as files and directories. If you need to locate files by name, type, size or modification date, use find. If you need to locate lines of text inside files or command output, use grep. For example, find /var/log -name “*.log” finds log files, while grep “permission denied” /var/log/syslog searches for a phrase inside a log file. The two commands are often used together. A user may first use find to locate a set of files and then use grep to search inside those files.
Pipes send the output of one command into another command as input. The pipe character is |. For example, ps aux | grep nginx sends the list of running processes into grep, which filters lines containing nginx. Pipes are one of the most important ideas in Linux because they allow small commands to be combined into custom workflows. One command can list data, another can filter it, another can sort it, another can count it and another can save it. This is why Linux commands are powerful even when individual commands are simple. The real strength often comes from combining tools correctly.
Output redirection sends command output to a file instead of displaying it in the terminal. The > operator writes output to a file and replaces existing content, while >> appends output to the end of a file. For example, ls -lah > listing.txt saves a directory listing into listing.txt, and date >> activity.log appends the current date to activity.log. Redirection is useful for reports, logs, automation and scripts. It must be used carefully because > can overwrite an existing file. If preserving previous content matters, >> is safer, but the user should still confirm which file is being written.
Standard error, often called stderr, is a separate output stream used for error messages. Standard output, or stdout, is used for normal command results. This separation allows a command to send useful results to one place and error messages to another. For example, normal output can be saved to a file while errors remain visible in the terminal. In shell syntax, > redirects standard output, while 2> redirects standard error. Understanding standard error is important in scripts and troubleshooting because a command may fail even when its normal output file looks empty. Error streams help explain what went wrong and where.
shows whether it succeeded. Exit codes are essential in shell scripting because they allow scripts to make decisions. A script can continue only if a command succeeded, stop when a command fails or report failure to another tool. Exit codes are one of the reasons Linux commands work well in automation.
awk is a text processing language commonly used to extract, filter and transform structured text. It is especially useful when working with columns, logs, CSV-like files or command output. For example, awk ‘{print $1}’ access.log prints the first whitespace-separated field from each line. awk can also apply conditions, calculate totals, format output and process records line by line. While grep is excellent for finding matching lines, awk is better when the user needs specific fields or more complex logic. It is one of the classic Linux tools for turning raw text into meaningful information.
sed is a stream editor used to transform text. It can replace text, delete lines, print selected ranges and perform automated edits. A common use is substitution, such as sed ‘s/old/new/g’ file.txt, which prints the file content with occurrences of old replaced by new. By default, sed usually writes transformed output to the terminal without changing the original file. In-place editing requires an option such as -i, which should be used carefully. sed is powerful in scripts because it can apply repeatable text transformations to logs, configuration files and generated output, but every expression should be tested before modifying important files.
The cut command extracts selected parts of each line from text input. It is commonly used to extract fields or character ranges. For example, cut -d: -f1 /etc/passwd uses the colon as a delimiter and prints the first field, which contains usernames. cut is useful when the input has a consistent structure and delimiter. It is simpler than awk, which makes it convenient for quick field extraction. However, cut is less flexible when spacing is inconsistent or when conditional logic is needed. In those cases, awk is usually more appropriate. cut is best for simple, predictable text extraction tasks.
The wc command counts lines, words, bytes or characters. For example, wc -l file.txt counts the number of lines in a file. In pipelines, wc -l is often used to count how many results another command produced. For example, find . -type f | wc -l counts files below the current directory. wc is useful in administration, scripting, data validation and log analysis. It can answer practical questions such as how many errors appeared, how many files match a condition or whether an export contains the expected number of lines. It is simple, but it becomes powerful when combined with pipes.
The sort command sorts lines of text. It can sort alphabetically, numerically, in reverse order or by specific fields depending on options. For example, sort names.txt sorts names alphabetically, while sort -n numbers.txt sorts numbers numerically. In pipelines, sort is often used before uniq so repeated values appear next to each other and can be counted. This is common in log analysis, where users may extract IP addresses, sort them and count repeated entries. sort does not normally modify the original file unless output is redirected or special options are used. It is primarily an organization and analysis tool.
The uniq command filters adjacent duplicate lines. It is usually used after sort because duplicate lines must be next to each other for uniq to detect them reliably. For example, sort names.txt | uniq prints unique sorted names. With uniq -c, it counts repeated lines, which is useful for frequency analysis. A common log analysis pattern is to extract values, sort them, count them with uniq -c and sort the counts again. This can reveal the most common IP addresses, errors, URLs, usernames or repeated events. uniq is simple, but it is very effective in command pipelines.
The tar command creates and extracts archive files. An archive combines many files and directories into one file. With compression, tar archives often use extensions such as .tar.gz or .tgz. For example, tar -czf backup.tar.gz project/ creates a compressed archive of the project directory, while tar -xzf backup.tar.gz extracts it. tar is widely used for backups, software distribution, log packaging and migration. The common options include c for create, x for extract, z for gzip compression and f for filename. Users should inspect paths carefully before extracting archives because files may be placed in unexpected locations.
tar creates an archive by combining files and directories into one file, but it does not necessarily compress them by itself. gzip compresses a file or stream. A .tar.gz file is usually a tar archive compressed with gzip. zip combines archiving and compression in a format commonly used across Windows, macOS and Linux. On Linux systems, .tar.gz is common for backups, source code and software packages because it preserves Unix-style structure well. zip is often convenient when sharing files with users on different operating systems. The right choice depends on compatibility, preservation needs and workflow.
The chmod command changes file and directory permissions. Permissions decide who can read, write or execute a file. For example, chmod +x script.sh makes a script executable, while chmod 644 file.txt sets a common permission mode for a regular text file. For directories, execute permission has a special meaning because it allows users to enter or traverse the directory. chmod is essential for scripts, web hosting, shared directories and security. It should be used precisely. Broad commands such as recursive permission changes or chmod 777 can create security problems or break applications if the user does not understand the effect.
The chown command changes the owner and optionally the group of a file or directory. For example, sudo chown pat:www-data file.txt changes the owner to pat and the group to www-data. Ownership is central to Linux permissions because access is evaluated differently for the owner, group and others. chown is often used in web hosting, deployment, shared directories and service configuration. It usually requires administrative privileges when changing ownership to another user. Recursive ownership changes with chown -R should be used carefully because they can affect many files at once. Wrong ownership can break services or expose sensitive data.
Ownership defines who owns a file and which group is associated with it. Permissions define what the owner, group and others can do with that file. For example, a file may be owned by pat, assigned to the group www-data and configured so the owner can write while the group can read. chown changes ownership, while chmod changes permission bits. Both must be understood together. If a web server cannot read a file, the problem may be ownership, permissions or parent directory access. A good troubleshooting workflow checks the user running the process, the file owner, the group and the permission mode.
umask controls the default permissions assigned to newly created files and directories. It does not change existing files. Instead, it subtracts permissions from the system’s default creation mode. For example, a common umask of 022 allows the owner to write while preventing group and others from writing by default. This matters for security and collaboration because it affects how open or restrictive new files are. If files are created with permissions that are too broad, sensitive data may be exposed. If they are too restrictive, services or team members may not be able to access them. umask explains many default permission behaviors.
Users represent accounts that can own files and run processes. Groups are collections of users used to manage shared access. Every file has an owner and a group, and permissions are evaluated for the owner, the group and everyone else. Processes also run as users, which affects what files and ports they can access. This is why services such as web servers and databases often run under dedicated service accounts instead of root. Groups make collaboration safer because they allow several users or services to share access without making files open to everyone. Understanding users and groups is essential for Linux permissions, security and administration.
The apt command manages software packages on Debian-based Linux distributions such as Debian, Ubuntu and Linux Mint. It can update package information, install software, upgrade installed packages, remove software and search available packages. Common examples include sudo apt update, sudo apt upgrade, sudo apt install package-name and sudo apt remove package-name. apt is important because it does more than download software. It also works with repositories, dependency information, package versions and system integration. A user should understand that installing software through apt is usually safer and more maintainable than downloading random installation files from the internet, because the package manager can track installed files and future updates.
apt update refreshes the local package index from configured software repositories. It does not install new versions by itself. It only tells the system what package versions are currently available. apt upgrade installs available updates for packages that are already installed on the system. This distinction is important because users often think apt update updates the operating system, but it only updates package information. A normal maintenance workflow on Debian-based systems often begins with sudo apt update, then continues with sudo apt upgrade. On production servers, upgrades should be reviewed carefully because package updates may restart services, change dependencies or require configuration decisions.
dpkg is a lower-level package management tool used on Debian-based Linux systems. While apt works with repositories and resolves dependencies, dpkg directly installs, removes and queries individual .deb package files. For example, sudo dpkg -i package.deb installs a local Debian package file. If dependencies are missing, dpkg may report errors instead of automatically resolving them the way apt usually does. dpkg -l lists installed packages and is often combined with grep to search for a specific package. For everyday package management, apt is usually more convenient, but dpkg remains important for manual installations, package audits and troubleshooting broken package states.
systemctl controls and inspects services and system units managed by systemd. It can start, stop, restart, reload, enable, disable and check the status of services. For example, systemctl status nginx shows whether the Nginx service is active, failed or inactive, while sudo systemctl restart nginx restarts it. sudo systemctl enable nginx configures the service to start automatically at boot. systemctl is one of the most important commands for modern Linux administration because many background services, including web servers, databases, SSH, scheduled units and system components, are managed by systemd. It should generally be preferred over older service-management commands on systemd-based systems.
Restarting a service stops it and starts it again. Reloading a service asks it to re-read configuration without fully stopping, if the service supports that behavior. For example, a web server may support reload so configuration changes can be applied with less disruption than a full restart. However, not every service supports reload, and not every change can be applied safely without restarting. A professional workflow is to test the configuration first when the service provides a test command, then reload if appropriate, then verify status and logs. Restarting is more disruptive but often more complete. Reloading is gentler, but it depends on the service.
journalctl reads logs collected by the systemd journal. It is essential on modern Linux systems because many service messages, boot logs and system events are stored in the journal. For example, journalctl -u nginx shows logs for the Nginx service, while journalctl -f follows new log entries in real time. journalctl -u service-name -f is especially useful when restarting a service or reproducing an error because it shows new messages as they appear. Compared with reading one static log file, journalctl can filter by service, boot, priority and time range. It is one of the strongest troubleshooting tools in systemd-based Linux administration.
The ps command shows running processes. A common form is ps aux, which lists processes from all users with details such as user, process ID, CPU usage, memory usage, start time and command. ps is useful when a user needs to know whether a program is running, which user started it, what command launched it or which process ID should be inspected. Unlike top or htop, ps shows a snapshot at one moment rather than a live updating view. It is often combined with filtering commands such as grep, although commands such as pgrep may be cleaner for finding processes by name.
The top command displays a live, updating view of system activity. It shows running processes, CPU usage, memory usage, load average, task counts and other system information. top is useful when a system feels slow and the user needs to identify which processes are consuming resources. It can show whether high CPU usage comes from one process, whether memory is under pressure or whether the system is handling an unusual workload. top is available on many Linux systems by default, which makes it reliable during troubleshooting. Although htop is often more comfortable to use, top remains a standard command every administrator should understand.
htop is an interactive process viewer similar to top, but usually more readable and easier to control. It shows CPU, memory, swap and process information in a visual terminal interface. Users can sort processes, search, filter and send signals more conveniently than with basic top. htop is especially useful for beginners because it makes system activity easier to understand. However, it may not be installed by default on minimal servers, so it may need to be installed through a package manager. htop does not replace understanding process behavior, but it provides a practical live view of which programs are using system resources.
The kill command sends a signal to a process. Despite its name, it does not always immediately destroy a process. By default, it usually sends a termination signal, asking the process to stop gracefully. For example, kill 12345 sends a signal to the process with the process ID 12345. If a process does not respond, kill -9 12345 sends SIGKILL, which forces termination and cannot be handled by the process. This should be a last resort because it does not allow clean shutdown or cleanup. A careful workflow is to identify the process, understand its purpose and attempt graceful termination before forcing it.
The killall command sends a signal to processes by name rather than by process ID. For example, killall php sends a signal to all processes named php. This can be convenient, but it can also be risky because it may affect more processes than intended. On a shared server or production machine, several unrelated tasks may use the same command name. Before using killall, it is safer to inspect matching processes with tools such as ps, pgrep or htop. killall is useful when the target is clear, but precise process IDs are safer when only one specific process should be stopped.
A process ID, often shortened to PID, is a unique number assigned to a running process. Commands such as ps, top, htop, pgrep and systemctl status can show process IDs. PIDs matter because many administrative actions target a specific process. For example, kill 12345 sends a signal to the process with PID 12345. A PID exists only while the process is running, and Linux may reuse that number later for a different process. This means users should verify the current process before acting on a PID. Process IDs are central to understanding how Linux tracks and controls running programs.
This is important because a full filesystem can cause package updates, logs, databases, uploads and services to fail. df does not tell which directory is responsible for the usage. It shows the filesystem overview. After identifying a full filesystem with df -h, administrators usually use du to investigate which directories or files are consuming space.
The du command estimates disk usage for files and directories. For example, du -sh /var/log shows the total size of the /var/log directory in a human-readable summary. It is commonly used after df -h shows that a filesystem is full. While df identifies the full filesystem, du helps locate the directories responsible for the usage. A practical troubleshooting pattern is to check major directories one level at a time, then drill down into the largest one. du is a read-only inspection command, but it may require administrative privileges to inspect directories that the current user cannot access.
df reports used and available space at the filesystem level, while du reports estimated usage by files and directories. If a server has a full disk, df -h tells which filesystem has no space left. Then du helps identify what directory or file tree is using the space. The two commands can sometimes appear to disagree. One common reason is that a large file has been deleted but is still held open by a running process. In that case, du may not show the file because it no longer has a visible name, while df still shows the space as used. Commands such as lsof can help diagnose that situation.
The mount command attaches a filesystem to a directory in the Linux filesystem tree. A disk partition, USB drive, network share or image file can be mounted so its contents appear under a mount point such as /mnt/usb or /mnt/backup. This reflects the Linux design where storage devices are integrated into one directory hierarchy instead of being represented primarily as drive letters. Mounting usually requires administrative privileges. It should be done carefully because mounting a filesystem over an existing directory can temporarily hide the original contents of that directory. mount is a core command for storage administration, recovery work and removable media handling.
The umount command detaches a mounted filesystem from the Linux directory tree. For example, sudo umount /mnt/usb unmounts the filesystem mounted at /mnt/usb. This is important before removing external drives because Linux may cache writes for performance. A proper unmount helps ensure that pending data is written and the filesystem is cleanly detached. If umount reports that the target is busy, some process may still be using files inside the mounted filesystem. In that case, tools such as lsof or fuser can help identify the process. Forced unmounts should be avoided unless truly necessary because they can risk data integrity.
The ip command displays and manages network configuration. It is the modern replacement for many older tools such as ifconfig and route. Common uses include ip addr to show network interfaces and IP addresses, ip link to show interface state and ip route to show routing information. The ip command is essential for diagnosing network problems because it shows whether an interface has an address, whether it is up and which gateway is being used. New Linux users may still find older tutorials using ifconfig, but learning ip is more accurate for current Linux administration.
The ping command tests basic network reachability by sending ICMP echo requests to a host and waiting for replies. For example, ping 8.8.8.8 tests whether the system can reach that IP address, while ping example.com also involves DNS resolution because the hostname must be translated to an IP address. Ping results can show latency and packet loss. However, ping is not a complete test of network health. Some systems block ICMP, and a successful ping does not prove that a specific service such as HTTP, SSH or a database is reachable. It is best used as one step in a layered network diagnosis.
The ss command displays socket and network connection information. It is the modern replacement for many uses of netstat. A common administrative command is ss -tulpn, which shows listening TCP and UDP sockets and the associated processes when permissions allow. This helps answer whether a service is listening on the expected port and address. For example, if a web server is running but unreachable, ss can show whether it is actually listening on port 80 or 443. ss is especially useful for troubleshooting web servers, databases, SSH, reverse proxies and application services that depend on network ports.
netstat displays network connections, routing tables, interface statistics and listening ports, but it is considered a legacy tool on many modern Linux systems. It may not be installed by default because newer tools such as ss and ip have replaced much of its functionality. Older tutorials often use commands like netstat -tulpn to show listening services. On current systems, ss -tulpn is usually the preferred equivalent. netstat remains useful when maintaining older systems or reading older documentation, but new Linux learners should understand that it belongs to an older generation of networking tools.
ssh provides secure remote access to another system over the network. For example, ssh pat@192.168.1.10 connects to the host at that IP address as the user pat, if authentication succeeds. SSH encrypts the session, which makes it the standard way to administer Linux servers remotely. It can use password authentication, but key-based authentication is usually more secure and convenient for professional use. SSH can also run remote commands, forward ports and support secure file transfer through tools such as scp and rsync. Because SSH often protects administrative access, it should be configured carefully and secured with strong authentication.
scp copies files securely between systems using SSH. For example, scp file.txt pat@server:/home/pat/ copies file.txt to the remote server. It is simple and useful for one-time transfers when SSH access is already available. However, for large transfers, repeated synchronization, deployments or backups, rsync is often more efficient and flexible. scp is still valuable because its syntax is straightforward and it works well for basic file copying. Users should pay close attention to local and remote paths. A missing colon, wrong username or incorrect destination can change the meaning of the command and place files somewhere unexpected.
rsync synchronizes files and directories locally or over SSH. It is more efficient than simple copying because it can transfer only changed files or changed parts of files. A common form is rsync -av source/ destination/, where -a preserves attributes and -v shows verbose output. Over SSH, it can synchronize with a remote server, such as rsync -av site/ user@server:/var/www/site/. rsync is widely used for backups, deployments, migrations and large directory transfers. The trailing slash in the source path is very important because it changes whether the directory itself or only its contents are copied.
The trailing slash in an rsync source path changes what is copied. When the source ends with a slash, such as project/, rsync copies the contents of the directory. Without the trailing slash, such as project, it may copy the directory itself into the destination. This difference can create unexpected nested directories or deploy files to the wrong level. For example, a website deployment may accidentally produce /var/www/html/project instead of placing files directly into /var/www/html. Because rsync is powerful and often works with important data, using –dry-run before a real synchronization is a professional safety practice.
The wget command downloads files from the web or another network location directly from the terminal. It is useful on servers, minimal systems and SSH sessions where no graphical browser is available. For example, a user can download an archive, installation file, text resource or backup file by giving wget a URL. wget can also save files under a different name, resume interrupted downloads in some cases and work in scripts. It should be used carefully because downloading a file is not the same as trusting it. Before running anything downloaded with wget, users should verify the source, prefer HTTPS, check official documentation and use checksums or signatures when available.
The curl command transfers data to or from servers using protocols such as HTTP, HTTPS, FTP and others. It is widely used for testing websites, inspecting HTTP headers, calling APIs, downloading files and debugging network services. For example, curl -I https://example.com retrieves HTTP response headers, while curl https://example.com prints the response body. Compared with wget, curl is often better for APIs and diagnostics because it gives fine control over headers, methods, authentication and request data. Developers, system administrators and DevOps engineers use curl constantly because modern infrastructure often communicates through web services, REST APIs and HTTP-based endpoints.
The hostname command displays the current system hostname and, depending on usage and permissions, may temporarily set it. A hostname identifies a machine in prompts, logs, monitoring tools and network contexts. On modern Linux systems, persistent hostname changes are often managed with hostnamectl, for example with a command that sets a new static hostname. A clear hostname is important in server administration because it helps distinguish production servers, staging machines, development systems and containers. Hostname changes should be planned because they may affect scripts, certificates, service discovery, monitoring, configuration management and documentation. A hostname is not just a label; it is part of operational clarity.
The date command displays the current system date and time. It can also format date output in specific ways, which makes it useful in scripts, backup filenames, logs and reports. For example, a script may use a formatted date to create a filename for a daily archive. Accurate time is important on Linux because logs, scheduled jobs, certificates, authentication, databases and distributed systems depend on correct timestamps. If the time is wrong, troubleshooting becomes harder because events appear out of order or certificates may fail validation. Changing system time usually requires administrative privileges and is often handled through time synchronization services rather than manual commands.
The uptime command shows how long the system has been running, how many users are logged in and the system load averages. It is a quick health check during troubleshooting. A very short uptime may indicate a recent reboot, crash or maintenance event. A very long uptime may look impressive, but it can also mean kernel updates have not been applied. Load averages help show how much work the system has been handling over the last one, five and fifteen minutes. However, load must be interpreted in context. A load average that is high for a single-core machine may be normal for a server with many CPU cores.
The history command shows previously executed shell commands. It is useful for repeating commands, reviewing recent work, documenting troubleshooting steps and learning from real command usage. Many shells also support reverse search through history, often with Ctrl+R, which helps find a command typed earlier. Shell history is helpful, but it should not be treated as a secure audit log. It may be incomplete, edited, disabled or cleared. Users should also avoid typing passwords, tokens or secrets directly into commands because they may be stored in history or visible to other tools. History is a productivity feature, not a safe place for sensitive information.
The crontab command manages scheduled tasks for cron. Cron allows commands or scripts to run automatically at specific times, such as every hour, every night or every week. It is commonly used for backups, cleanup tasks, reports, monitoring scripts and maintenance jobs. A cron entry contains time fields followed by the command to run. Cron is powerful, but it can surprise beginners because scheduled jobs often run with a limited environment. They may not have the same PATH, working directory or environment variables as an interactive terminal. Good cron jobs use absolute paths, log their output, handle errors and are tested manually before being scheduled.
The screen command starts a persistent terminal session that can continue running even if the user disconnects. This is useful over SSH when running long tasks such as backups, migrations, package builds, large file transfers or maintenance scripts. A user can start a screen session, run a command, detach from the session and later reattach to continue monitoring it. This protects work from network interruptions or accidental terminal closure. Many administrators now also use tmux, which provides similar functionality with modern features. The important concept is persistent terminal work: long-running tasks should not depend entirely on one fragile SSH connection.
An alias creates a shortcut or replacement for a command in the shell. For example, an alias can make a short command such as ll run a longer command such as ls -lah. Aliases can improve productivity, standardize frequently used options and reduce typing. They can also create safer defaults, although users should be careful not to hide important behavior behind unexpected shortcuts. If a command behaves differently than documentation suggests, type command-name can reveal whether an alias is involved. Aliases are often stored in shell configuration files such as .bashrc or .zshrc. They are useful, but they should remain clear and predictable.
PATH is an environment variable that tells the shell where to look for executable commands. It contains a list of directories separated by colons. When a user types a command such as grep, the shell searches the directories in PATH until it finds a matching executable. If a program is installed but its directory is not in PATH, the shell may return “command not found” even though the file exists. Understanding PATH helps diagnose command resolution problems, conflicting software versions and script behavior. It is especially important for root, cron jobs, deployment scripts and development environments where different command versions may exist.
Environment variables are named values available to processes and shells. They store information such as the current user’s home directory, preferred editor, language settings, command search path and application-specific configuration. Common examples include HOME, USER, SHELL, PATH and LANG. Programs and scripts often read environment variables to decide how they should behave. This is why a command may work in an interactive terminal but fail in cron, systemd or a deployment job where the environment is different. Understanding environment variables helps users troubleshoot inconsistent behavior, configure applications and write scripts that are predictable across different execution contexts.
A shell script is a text file containing commands that are executed by a shell. Shell scripts automate repeated tasks such as backups, deployments, log cleanup, system checks, file processing and maintenance workflows. A script can turn a manual sequence of commands into a reusable tool. Good shell scripts are careful with paths, quote variables, check errors and avoid destructive actions without validation. They should be tested on safe data before being used in production. Shell scripting is one of the most practical extensions of Linux command knowledge because it moves a user from typing individual commands to building repeatable administrative workflows.
A shebang is the first line of a script that tells Linux which interpreter should run the file. It begins with #!. For example, #!/bin/bash tells the system to run the script with Bash when the file is executed directly. Another common form is #!/usr/bin/env bash, which finds Bash through the environment. The shebang matters because different shells support different syntax. A script written for Bash may fail if it is run by a more minimal shell. A correct shebang makes script behavior more predictable and easier to understand. It is a small line with a major effect on script execution.
Variables should usually be quoted in shell scripts to prevent word splitting and unexpected wildcard expansion. If a variable contains a filename with spaces and the script uses it without quotes, the shell may treat it as multiple arguments. Quoting the variable keeps the value together as one argument. This is one of the most important safety habits in shell scripting. It protects scripts from filenames with spaces, empty values, special characters and unexpected input. Many script bugs and data-loss incidents come from unquoted variables in commands such as rm, cp, mv, rsync or loops. A good default is to quote variables unless there is a deliberate reason not to.
The source command runs a script or configuration file in the current shell instead of launching a separate shell process. This matters because a normal script usually cannot permanently change the parent shell’s environment. When a file is sourced, variable assignments, functions, aliases and directory changes can remain active in the current session. For example, users often source shell configuration files after editing them so the changes apply immediately. source is useful for environment setup, development workflows and shell customization. It should be used only with trusted files because sourced code runs directly in the current shell context with the user’s privileges.
The ln command creates links between files. It can create hard links or symbolic links. Symbolic links, created with ln -s, are more common in everyday administration because they act like path-based shortcuts to another file or directory. For example, a symlink may point from an enabled configuration location to an available configuration file. Links are useful for version switching, shared resources, configuration management and directory organization. However, symbolic links can break if the target is moved or deleted. Understanding links helps users understand many Linux systems, because symlinks are widely used in configuration directories, software paths and service setups.
A symbolic link, often called a symlink, is a special file that points to another path. It behaves like a shortcut. If the target exists, accessing the symlink usually accesses the target. If the target is removed or moved, the symlink becomes broken. Symlinks are created with ln -s target link-name. They are different from copies because they do not duplicate the target content. They are also different from hard links because they store a path reference rather than another direct name for the same file data. Symlinks are common in Linux because they provide flexible ways to connect files, directories, versions and configurations.
The file command identifies the type of a file by inspecting its contents and metadata rather than relying only on the filename extension. This is useful because Linux does not depend on extensions in the same way many users expect. A file named .txt may not contain plain text, and an executable file may have no extension at all. For example, file script.sh may identify a shell script, while file image.png may identify image data. The command is helpful before opening unknown files, troubleshooting downloads or checking whether a file is text, binary, compressed, executable or something else.
The stat command displays detailed metadata about a file or directory. It can show size, ownership, permissions, inode number and timestamps such as access time, modification time and status change time. It provides more precise information than a basic ls -l listing. stat is useful when troubleshooting permissions, synchronization, backups, deployment issues or unexpected file changes. For example, it can help determine whether a file was modified recently or whether permissions differ from what an application expects. stat does not change the file. It is an inspection command, which makes it safe and useful when gathering evidence before making changes.
The lsof command lists open files. In Linux, many resources are represented as files, including regular files, directories, devices and network sockets. This makes lsof extremely useful for troubleshooting. It can show which process is using a file, which process is keeping a mount point busy or which process still holds a deleted file open. This last case is important when disk space appears full even after a large file was deleted. The file may be gone from the directory listing, but the space may not be released until the process closes it. lsof helps reveal these hidden relationships between processes and files.
The dmesg command displays messages from the kernel ring buffer. These messages often relate to hardware, drivers, boot events, disk detection, USB devices, network interfaces and kernel-level errors. For example, after connecting a USB drive, dmesg may show how the kernel detected it and which device name was assigned. It is useful when troubleshooting hardware recognition, boot problems, disk errors, driver issues and low-level system events. On some systems, access to dmesg may be restricted for security reasons. It should be understood as a kernel diagnostic tool, not as a replacement for application logs or service-specific logs.
The watch command repeatedly runs another command and displays updated output. For example, watch df -h refreshes disk usage every few seconds. This is useful when observing a changing condition, such as disk space during cleanup, process counts during a test, network sockets during service startup or file growth during a backup. watch helps users move from single snapshots to live observation. It is practical during troubleshooting because some problems appear only over time. However, users should avoid running expensive commands too frequently with watch, especially on production systems. Monitoring should provide insight without creating unnecessary load.
To troubleshoot “permission denied,” first identify the exact file, directory or operation that failed. Then check the current user with whoami or id, inspect the file or directory with ls -l or stat, and verify the permissions of parent directories. If a service is involved, identify the user that the service runs as, because the interactive user may not be the relevant one. Avoid immediately using sudo or setting broad permissions such as chmod 777. The correct fix is usually a precise adjustment to ownership, group membership or permission bits. Good permission troubleshooting finds the smallest safe change that allows the required action.
Start with df -h to identify which filesystem is full. Then use du to find which directories are consuming the most space. A typical investigation checks large top-level directories and drills down step by step. Common causes include logs, backups, caches, database files, container images, temporary files and deleted files still held open by running processes. If df shows high usage but du does not explain it, lsof may reveal deleted open files. Do not delete files randomly just because they are large. First understand what they belong to, whether a service is using them and whether there is a safe cleanup method.
Begin with systemctl status service-name to check whether the service is active, inactive, failed or repeatedly restarting. Then inspect detailed logs with journalctl -u service-name. Look for clear causes such as permission errors, invalid configuration, missing files, port conflicts, dependency failures or resource limits. If the service provides a configuration test command, run it before restarting. After making a change, restart or reload the service and verify status again. A strong troubleshooting workflow follows evidence: check status, read logs, test configuration, make one controlled change and verify the result. Restarting blindly may temporarily hide the real cause.
Use a layered approach. First check whether the network interface exists and has an IP address with ip addr. Then check routing with ip route. Test basic reachability with ping to an IP address. Then test name resolution with a hostname. If the issue is with a specific service, check whether it is listening with ss -tulpn. Use curl for HTTP services and verbose SSH output for SSH problems. Also consider firewall rules, DNS configuration, gateway availability and service binding addresses. Network troubleshooting is easier when each layer is tested separately instead of treating every failure as a general internet problem.
A Linux command is safer to run when the user understands what it will affect, whether it reads or modifies data, what privileges it needs and what the possible failure modes are. Inspection commands such as pwd, ls, cat, less, df, du, ps and systemctl status are generally safer because they mainly read information. Destructive or system-changing commands such as rm, mkfs, dd, recursive chmod, recursive chown, package removal and forced service operations require much more caution. Before running an unfamiliar command, read it from left to right, identify options and arguments, check the target path and consult trusted documentation.
Author:
Jan Bielik
CEO & Founder of Webiano Digital & Marketing Agency

| Citing this article? Brief excerpts are welcome. Please credit Webiano.digital, name the author where stated, and include a link to https://webiano.digital and to this original article. Full or substantial republication requires prior written permission. Read our Copyright and Content Use Policy. |
This article was prepared with the assistance of artificial intelligence tools. The content underwent expert human review, and Webiano Digital & Marketing Agency assumes editorial responsibility for its final version and publication.















