Learn, Solve & Master Python, Linux, SQL, ML & DevOps
🐧 Linux The Foundation for Every Developer
🚀 Learn Linux the Right Way – Practical, In-Depth & RHCSA-Focused
Master Linux from the ground up, from basic commands and system navigation to advanced administration, networking, automation, and security.
These tutorials are designed for real-world learning and complete RHCSA exam preparation.
⬇️ Jump To
Introduction to Linux
Linux is a free, open-source operating system that powers everything from mobile devices to servers and supercomputers. It provides a stable, secure, and efficient platform for both personal and enterprise use.
Why Learn Linux?
It’s the backbone of cloud, DevOps, and server administration.
Used by major companies: Google, Amazon, Red Hat, IBM, and more.
Essential for certifications like RHCSA, RHCE, and LFCS.
Key Features
Multi-user and multi-tasking support
Open-source and customizable
Secure and stable
Community-driven development
💡 Pro Tip: Learning Linux gives you the foundation for working with any modern technology stack, from development to deployment.
Linux Architecture Overview
The Linux operating system is structured in layers, each with a specific role in how it operates.
Hardware ⮕ Kernel ⮕ System Libraries ⮕ Utilities ⮕ User Space
Here’s a simplified breakdown of its main components:
| Layer | Description |
|---|---|
| Hardware | The physical components: CPU, memory, disks, and devices. |
| Kernel | The brain of Linux: manages hardware, memory, and processes. |
| System Libraries | Provide functions that interact with the kernel (e.g., glibc). |
| System Utilities | Core tools for managing files, processes, and users. |
| User Space | Where applications and user commands run. |
💡 Pro Tip: The kernel is like a bridge between your hardware and software, it ensures they talk efficiently.
Linux Distributions
Below are some of the most popular Linux distributions widely used across different environments:
Ubuntu: Beginner-friendly and ideal for newcomers, developers, and desktop users.
Fedora: Known for its cutting-edge features and frequent updates.
CentOS Stream: A rolling-release model that tracks just ahead of RHEL.
RHEL (Red Hat Enterprise Linux): Enterprise-grade, stable, and widely adopted in production environments; essential for RHCSA/RHCE certifications.
AlmaLinux: A free, open-source, 1:1 binary-compatible alternative to RHEL, popular after CentOS transitioned to Stream.
Debian: Renowned for its stability, strong community support, and long-term reliability.
💡 Pro Tip: If you’re preparing for RHCSA, focus on RHEL or AlmaLinux, since both share the same commands, structure, and package management system
Accessing the Command Line in Linux
Bash Shell
The Bash (Bourne Again Shell) is the default command-line interface in most Linux systems, used to execute commands, run scripts, and automate administrative tasks.
[user@host ~]$ → Shell Prompt
[user@host ~]# → Superuser Shell Prompt
Login using SSH
SSH (Secure Shell) is a protocol used to securely connect to a remote Linux system over a network. It encrypts all communication, protecting passwords and commands from eavesdropping.
ssh username@remotehostusername→ the user account on the remote systemremotehost→ the hostname or IP address of the remote system
For added security, you can use SSH keys instead of passwords (Click here for more details on SSH Keys):
ssh -i /path/to/private_key username@remotehost-i /path/to/private_key→ specifies your private key fileThe matching public key must be added to the remote user’s
~/.ssh/authorized_keys
Host Verification
The first time you connect to a remote host, SSH may prompt:
The authenticity of host 'remotehost (IP)' can't be established. Are you sure you want to continue connecting (yes/no)?Enter
yesto accept the host key and save it for future connections.
Security Tip
- Never share your private key.
- Use
chmod 600 mykey.pemto restrict access to your key file.
Logout
To safely end your session, type:exit
or press Ctrl + D.
💡 Pro Tip: Using SSH keys is highly recommended for servers and cloud instances, especially for RHCSA exam practice and real-world setups.
Basic Linux commands and Shortcuts
This section introduces some of the most commonly used Linux commands and Bash shortcuts that every user should know.
Common Commands
whoami→ Displays the current logged-in usernamecommand1; command2→ Executes multiple commands sequentiallydate→ Displays the current date and timedate +%R→ Displays the time in HH:MM format (+%Ris a string formatter)date +%x→ Displays the current date in MM/DD/YYYY format (+%xis a string formatter)passwd→ Changes or sets the user’s passwordfile <file_name>/<directory_name>→ Determines and displays the type of file or directory
Viewing File Contents
cat <file_name>→ Displays the content of a filecat <file_name1> <file_name2>→ Displays the contents of multiple files sequentiallyless <file_name>→ Views long files page by page (use ↑ / ↓ keys to scroll, andQto exit)head <file_name>→ By default, displays the first 10 lines of a filehead -n 15 <file_name>→ Displays the first 15 lines of a file. The number 15 can be replaced with any numbertail <file_name>→ By default, displays the last 10 lines of a filetail -n 20 <file_name>→ Displays the last 20 lines of a file. The number 20 can be replaced with any number
Counting File Data
wc <file_name>→ Counts lines, words, and characters in a filewc -l <file_name>→ Counts only the number of lineswc -w <file_name>→ Counts only the number of wordswc -c <file_name>→ Counts only the number of characters
Command History and Continuation
history→ Displays a list of previously executed commands!ls→ Repeats the most recent command starting with “ls”!<number>→ Executes the command corresponding to that number from the history (e.g.,!25)
Command Continuation
When a command is too long to fit on one line, you can use a backslash (\) at the end of the line to continue typing on the next line.
This improves readability and organization in scripts or complex commands.
Always make sure there’s no space after the backslash, or the continuation won’t work.
-
head -n 3 \> /usr/share/dict/words \> /usr/share/dict/linux.words
Other Useful Commands
man -k <keyword/command>→ Searches the manual (man) pages for a given keyword or commandmkdir -p Thesis/Chapter1 Thesis/Chapter2 Thesis/Chapter3→ Creates multiple parent and subdirectories at once
Tab Completion
Tab completion helps you quickly complete commands or file names by pressing the Tab key.
-
- Press Tab once → Auto-completes if the command or file name is unique.
- Press Tab twice → Shows all possible matches.
Useful Command-line Editing Shortcuts
| Shortcut | Description |
|---|---|
| Ctrl + A | Jump to the beginning of the command line. |
| Ctrl + E | Jump to the end of the command line. |
| Ctrl + U | Clear from the cursor to the beginning of the command line. |
| Ctrl + K | Clear from the cursor to the end of the command line. |
| Ctrl + ← (Left Arrow) | Jump to the beginning of the previous word on the command line. |
| Ctrl + → (Right Arrow) | Jump to the end of the next word on the command line. |
| Ctrl + R | Search the history list of commands for a matching pattern. |
SSH Key-Based Authentication
SSH key-based authentication is a more secure and convenient way to log in to remote systems without using passwords.
Instead of typing your password each time, a pair of cryptographic keys (a public key and a private key) is used to establish trust between the client and the server.
The private key acts as your authentication credential and must be kept secret and secure, just like a password.
The public key is copied to the remote system you want to access and is used to verify the private key. It does not need to be confidential.
Generating SSH Keys
To create a private key and matching public key for authentication, use the ssh-keygen command.
[user@host ~]$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/user/.ssh/id_rsa): Enter Enter passphrase (empty for no passphrase): Enter Enter same passphrase again: Enter Your identification has been saved in /home/user/.ssh/id_rsa. Your public key has been saved in /home/user/.ssh/id_rsa.pub.~/.ssh/id_rsa→ Private key~/.ssh/id_rsa.pub→ Public key
If you set a passphrase, you’ll need to enter it when using the key. Else, anyone with your private key file could use it.
Example: Custom Key Name with Passphrase
[user@host ~]$ ssh-keygen -f ~/.ssh/key-with-pass Generating public/private rsa key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in ~/.ssh/key-with-pass Your public key has been saved in ~/.ssh/key-with-pass.pubThe
-foption lets you specify a custom filename.The example above creates a passphrase-protected key pair:
~/.ssh/key-with-pass→ Private key~/.ssh/key-with-pass.pub→ Public key
Sharing the Public Key
ssh-copy-id command copies the public key of the SSH keypair to the destination system.ssh-copy-id -i .ssh/key-with-pass.pub user@remotehost→ Copies the specified public key to the remote host.ssh-copy-id user@remotehost→ Copies the default public key (~/.ssh/id_rsa.pub) to the remote host.ssh -i .ssh/key-with-pass user@remotehost→ Uses the specified private key to connect.ssh user@remotehost→ Uses the default private key (~/.ssh/id_rsa) to connect.
SSH-agent
When using SSH keys protected with a passphrase, you normally need to enter it each time you connect.
To avoid this, you can use ssh-agent, which securely stores your private key’s passphrase in memory, allowing you to enter it only once per session.
eval "$(ssh-agent -s)"→ Start the agentssh-add ~/.ssh/id_rsa→ Add your key (enter passphrase once)ssh user@remotehost→ Login without re-entering it
Identifying Remote Users
[user01@remotehost ~]$ w
16:13:38 up 36 min, 1 user, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
user02 pts/0 172.25.250.10 16:13 7:30 0.01s 0.01s -bash
user01 pts/1 172.25.250.10 16:24 3.00s 0.01s 0.00s wColumn descriptions:
USER: Logged-in username
FROM: Remote IP address
IDLE: Idle time
WHAT: Current command being executed
SSH Host Keys
When connecting to a server:
The client checks the server’s public key against its local known hosts file.
If the key is unknown or changed, the user is prompted for confirmation.
Once accepted, the key is stored in
~/.ssh/known_hosts.
Example:
[user01@host ~]$ ssh newhost
The authenticity of host 'newhost (172.25.250.12)' can't be established.
ECDSA key fingerprint is SHA256:qaS0PToLrqlCO2XGklA0iY7CaP7aPKimerDoaUkv720.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'newhost,172.25.250.12' (ECDSA) to the list of known hosts.SSH Known Hosts File
Public keys of remote servers are saved in:
System-wide:
/etc/ssh/ssh_known_hostsUser-specific:
~/.ssh/known_hosts
Each entry includes:
<hostname> <key_algo> <public_key>Example entry from ~/.ssh/known_hosts:
remotehost,172.25.250.11 ecdsa-sha2-nistp256 AAAAE2VjZH...
If a server’s key changes (due to reinstall or rotation), remove its old entry from this file to avoid SSH warnings.
Configuring the OpenSSH Server
The OpenSSH server is managed by the sshd daemon. Its main configuration file is:
/etc/ssh/sshd_configIt’s generally used with secure defaults, but two common security enhancements are:
- Prohibit Remote
rootLogin- Reason to disable root SSH login:
- Reduces attack surface (root is a known username).
- Avoids misuse of root’s unrestricted privileges.
- Improves accountability (users must log in as themselves first).
- To disable root login:
- Edit
/etc/ssh/sshd_config:PermitRootLogin no
- To allow only key-based root login (optional):
PermitRootLogin without-password
- PermitRootLogin without-password
sudo systemctl reload sshd
- Edit
- Reason to disable root SSH login:
- Disable Password-Based Authentication
Benefits of key-based authentication:
Prevents brute-force password attacks.
Safer if private keys are passphrase-protected.
Can use
ssh-agentfor convenience.
- To disable password authentication:
- In
/etc/ssh/sshd_config:PasswordAuthentication no
Then reload SSH:
sudo systemctl reload sshd
- In
💡 Pro Tips:
The permission modes must be 600 on the Private Key, 644 on the Public Key, and 600 for Authorized Keys
chmod 600 ~/.ssh/id_rsa→ Private Key Permissionchmod 600 ~/.ssh/authorized_keys→ Authorized Keys Permissionchmod 644~/.ssh/id_rsa.pub→ Public Key Permission
When you run the ssh-keygen command again without changing the filename of the existing keys, the old keys are overwritten with new ones. As a result:
- The old public key previously shared with a server is no longer valid.
- You will not be able to log in to that server using the new private key.
- To fix this, you must copy the new public key again to the remote server using the
ssh-copy-idcommand
Before disabling password-based authentication, ensure that every user already has their public key added to the server’s ~/.ssh/authorized_keys file, otherwise they will be locked out of SSH access.
Managing File Systems
On Linux, all files are organized in a single inverted tree called the file-system hierarchy, with / as the root directory.
Subdirectories like /etc and /boot help organize files by type and purpose.
Files and directories are referenced using / as a separator, e.g., /etc/issue refers to the issue file inside /etc.
| Location | Purpose |
|---|---|
| /usr | Installed software, libraries, include files, and read-only program data. Important subdirs: /usr/bin (user commands), /usr/sbin (admin commands), /usr/local (local software). |
| /etc | System-specific configuration files. |
| /var | Variable data that persists across boots, e.g., databases, logs, cache, and website content. |
| /run | Runtime data for processes since last boot, including PID and lock files. Recreated on reboot. |
| /home | Home directories for regular users’ personal data and configs. |
| /root | Home directory for the administrative superuser, root. |
| /tmp | Temporary files. Auto-deleted if not used for 10 days. /var/tmp retains files for 30 days. |
| /boot | Files required for system boot. |
| /dev | Special device files used to access hardware. |
Basic Linux Commands You Should Know
pwd→ Prints current directory.ls→ Lists all files and directories in the current directory.
Note: All ls variations (-l, -la, -R) support specifying one or more directories, e.g., ls /etc /var.
ls -l→ Shows files in long listing format with details like permissions, owner, size, and modification date.ls -la→ Lists all files including hidden files (those starting with.).ls -R→ Recursively lists all files in the current directory and its subdirectories.
cd <directory_path>→ Changes the current directory.cd ..→ Moves up one level to the parent directory.cd -→ Changes back to the previous directory.touch <file_name>→ Updates a file’s timestamp to the current date/time or creates an empty file if it doesn’t exist.
Command-line File Management
To manage files and directories effectively, you can use various commands to create, remove, copy, move, and organize them.
Creating Directories
mkdir <directory1> <directory2>→ Creates one or more directories.mkdir -p <directory1> <directory2>→ Creates parent directories if they don’t already exist.- Example:
mkdir Videos/Watchedmkdir -p Thesis/Chapter1 Thesis/Chapter2 Thesis/Chapter3
Copying Files
cp <filename> <new-filename>→ Copies a file and creates a duplicate with a new name.cp -r <directory> <new-directory>→ Copies an entire directory recursively (all files and subdirectories).If the destination already exists, it will be overwritten.
- Example:
cp blockbuster1.ogg blockbuster3.oggcp -r Thesis ProjectX
Moving Files
mv <filename> <new-filename>→ Moves or renames a file.mv <directory> <new-directory>→ Moves or renames a directory.- Example:
mv thesis_chapter2.txt thesis_chapter2_updated.txtmv Chapter1 Thesis/Chapter1_updated
Removing Files and Directories
rm <filename1> <filename2>→ Removes one or more files.rm -r <directory1> <directory2>→ Removes directories and their contents recursively.Use
-ifor confirmation before each file.Use
-fto force deletion without prompts.
rmdir <directory1> <directory2>→ Removes empty directories.- Example:
rm thesis_chapter2_updated.txtrm -rf Chapter1_updatedrmdir Chapter1
Managing Links Between Files
It’s possible to create multiple names that point to the same file through Hard Links and Soft (Symbolic) Links.
Hard Links
A Hard Link is another name that points directly to the same file data.
Even if the original file is deleted, its contents remain available through the hard link.
Hard links cannot be created for directories.
Hard links must be on the same file system.
ln newfile.txt /tmp/newfile-hlink2.txt→ Command to create hard linkln→ Command used to create a hard linknewfile.txt→ Original file/tmp/newfile-hlink2.txt→ Hard link name
To verify both files point to the same data:
[user@host ~]$ ls -il newfile.txt /tmp/newfile-hlink2.txt 8924107 -rw-rw-r--. 2 user user 12 Mar 11 19:19 newfile.txt 8924107 -rw-rw-r--. 2 user user 12 Mar 11 19:19 /tmp/newfile-hlink2.txtThe same inode number confirms both are hard links to the same file.
- When to use:
You want multiple filenames for the same file on the same file system.
You need backup-like access without extra storage.
Soft (Symbolic) Links
A Soft Link (also called a Symbolic Link) is a special type of file that points to another file or directory.
Can link files across different file systems.
Can link to directories or special files, not just regular files.
If the original file is deleted, the soft link remains but becomes a “dangling link” (A Soft link pointing to a missing file).
ln -s /home/user/newfile-link2.txt /tmp/newfile-symlink.txt→ Command to create soft linkln -s→ Command used to create a soft (symbolic) link/home/user/newfile-link2.txt→ Original file/tmp/newfile-symlink.txt→ Soft link name
- When to use:
You want a shortcut to another location.
You’re linking across partitions or to a directory.
You frequently manage system files, logs, or scripts from one place.
Command-Line Expansions
The Bash shell supports several kinds of expansions that simplify command-line operations. These include pattern matching (globbing), home directory expansion, string and variable expansion, and command substitution.
Pattern Matching (Globbing)
- Globbing expands wildcard patterns into matching file or path names. These metacharacters help you perform operations on multiple files efficiently.
| Pattern | Examples | Explanation |
|---|---|---|
| * | ls a* → matches able, alfa ls *a* → matches able, baker, charlie | Matches any string of zero or more characters. |
| ? | ls ?.txt → matches a.txt, b.txt ls a? → matches ab, ax | Matches exactly one single character. |
| [abc…] | ls [ab]* → matches able, baker ls [ch]* → matches charlie, henry | Matches any one character from the specified set inside brackets. |
| [!abc…] or [^abc…] | ls [!a]* → matches everything except files starting with a ls [^b]* → matches all files not starting with b | Matches any one character not in the specified set. |
| [[:alpha:]] | ls [[:alpha:]]* → matches able, charlie ls *[[:alpha:]] → matches filea, testB | Matches alphabetic characters only (A–Z, a–z). |
| [[:lower:]] | ls [[:lower:]]* → matches able, baker ls *[[:lower:]] → matches filea, datax | Matches lowercase letters only. |
| [[:upper:]] | ls [[:upper:]]* → matches File1, Test ls *[[:upper:]] → matches fileA, dataX | Matches uppercase letters only. |
| [[:alnum:]] | ls [[:alnum:]]* → matches file1, test ls *[[:alnum:]] → matches data1, x9 | Matches letters and digits (A–Z, a–z, 0–9). |
| [[:punct:]] | ls [[:punct:]]* → matches _file, -data ls *[[:punct:]]* → matches data-file, name_test | Matches punctuation or symbols, not spaces or alphanumeric characters. |
| [[:digit:]] | ls [[:digit:]]* → matches 1file, 2025.txt ls *[[:digit:]] → matches file1, log9 | Matches digits only (0–9). |
| [[:space:]] | ls *[[:space:]]* → matches “my file.txt”, “test data.csv” ls *[[:space:]] → matches “data file” | Matches whitespace characters (space, tab, newline, etc.). |
Tilde Expansion (~)
- The tilde (
~) represents the home directory of the current or specified user. - Example:
[user@host home]$ ls ~root /root[user@host home]$ echo ~/home /home/user/home
Brace Expansion ({})
Used to generate multiple strings in one command. Braces contain:
A comma-separated list of strings, or
A sequence expression using
...
- Example:
[user@host home]$ echo {Sunday,Monday,Tuesday,Wednesday}.log Sunday.log Monday.log Tuesday.log Wednesday.log[user@host home]$ echo file{1..3}.txt file1.txt file2.txt file3.txt[user@host home]$ echo file{a..c}.txt filea.txt fileb.txt filec.txt[user@host home]$ echo file{a,b}{1,2}.txt filea1.txt filea2.txt fileb1.txt fileb2.txt[user@host home]$ echo file{a{1,2},b,c}.txt filea1.txt filea2.txt fileb.txt filec.txt
Note: You can replace echo with other commands like mkdir, touch, cat, less, etc., to perform actions on multiple files or directories at once.
Variable Expansion ($VARIABLE)
- Variables store values that can be expanded (substituted) in commands.
- Syntax:
VARIABLENAME=value echo $VARIABLENAME
- Example:
[user@host ~]$ USERNAME=operator [user@host ~]$ echo $USERNAME operator[user@host ~]$ echo ${USERNAME} operator
Command Substitution ($(command))
Replaces a command with its output.
- Example:
[user@host glob]$ echo The time is $(date +%M) minutes past $(date +%l%p). The time is 26 minutes past 11AM.
Protecting Arguments from Expansion
- To prevent unwanted shell expansions, you can use escaping or quoting.
- Escaping: Use
\(backslash) to protect the next character from expansion - Example:
[user@host glob]$ echo The value of \$HOME is your home directory. The value of $HOME is your home directory.
- Quoting: Controls how the shell interprets special characters.
- Single Quotes
' '→ Prevents all expansions. - Double Quotes
" "→ Prevents most expansions, but allows variable and command substitution.
- Single Quotes
- Example:
[user@host glob]$ myhost=$(hostname -s); echo $myhost host[user@host glob]$ echo "***** hostname is ${myhost} *****" ***** hostname is host *****[user@host glob]$ echo "Will variable $myhost evaluate to $(hostname -s)?" Will variable host evaluate to host?[user@host glob]$ echo 'Will variable $myhost evaluate to $(hostname -s)?' Will variable $myhost evaluate to $(hostname -s)?
Redirecting Output to a File or Program
You can send a command’s output to a file or another program instead of the terminal using redirection (>, >>) and pipelines (|). This helps save results or pass data seamlessly between commands.
Standard Input, Standard Output, and Standard Error
Every running program (process) interacts with three standard data streams called file descriptors:
| File Descriptor | Name | Description |
|---|---|---|
| 0 | Standard Input (stdin) | Reads input (usually from the keyboard). |
| 1 | Standard Output (stdout) | Sends normal output to the terminal. |
| 2 | Standard Error (stderr) | Sends error messages to the terminal. |
Redirection allows these streams to be sent to or read from files instead.
Redirecting Output to a File
I/O redirection changes how a process reads or writes data. Instead of showing output on the terminal, it can be saved to a file or discarded.
If the file doesn’t exist → it’s created.
If the file exists → it’s overwritten unless using
>>(append).To discard unwanted data, redirect it to
/dev/null.
| Operator / Command | Explanation | Example |
|---|---|---|
> | Redirects stdout (normal output) to a file, overwriting any existing content. | ls > files.txt |
>> | Redirects stdout and appends output to a file (does not overwrite). | echo "done" >> log.txt |
2> | Redirects stderr (error messages) to a file, overwriting existing content. | ls /root 2> errors.log |
2>> | Redirects stderr and appends to a file. | ls /root 2>> errors.log |
2> /dev/null | Discards error messages completely. | ls /root 2> /dev/null |
> file 2>&1 | Redirects both stdout and stderr to the same file (overwrite mode). | ls /root > all.log 2>&1 |
>> file 2>&1 | Redirects both stdout and stderr to the same file (append mode). | ls /root >> all.log 2>&1 |
&> file | Bash shortcut: redirects both stdout and stderr (overwrite mode). | ls &> output.log |
&>> file | Bash shortcut: redirects both stdout and stderr (append mode). | ls &>> output.log |
< file | Redirects stdin (input) from a file. | sort < names.txt |
Constructing Pipelines
A pipeline is a sequence of one or more commands separated by the pipe symbol (|).
It connects the stdout of one command to the stdin of the next.
Itsend output from one process directly into another.
Example:
ls -t | head -n 10 > /tmp/ten-last-changed-files→ List the 10 most recently modified files and save to a file.
tee combine redirection with a pipeline, it copies its stdin to both stdout and one or more files
Example:
ls -t | head -n 10 | tee /tmp/ten-last-changed-files→ Save final pipeline output and also show it on screen.
💡 Pro Tip: The order matters when redirecting both output and errors. the correct order is first stdout and then stderr.
Editing Text Files
Vim is one of the most powerful and widely used text editors in Linux and UNIX systems. It’s fast, efficient, and ideal for editing configuration files directly from the terminal even on remote servers. Knowing Vim ensures you can manage files without a graphical interface, making it an essential skill for every Linux user.
Vim can be accessed in two ways:
vi filename→ Lightweight version available with thevicommand (core editing features only)vim filename→ Full version with advanced features, syntax highlighting, and help system.
Basic Vim Workflow
- Open a file:
vim filename
-
Press
ito enter insert mode and start editing -
Press
Escto return to command mode -
Save your changes with
:wor save and quit with:wq -
Exit without saving using
:q! - Display absolute line numbers
:set number -
Undo the last change with
u -
Delete a single character with
x
Changing the Shell Environment
You can customize your shell environment by setting shell variables and environment variables. These variables can store values, simplify commands, and modify the behavior of the shell or programs run from it.
Shell Variables
-
Shell variables are local to a particular shell session.
-
Assign values using:
VARIABLENAME=value
- Example:
COUNT=40first_name=John
- Accessing variable values:
echo $COUNTecho ${COUNT}x
Environment Variables
- Environment variables are exported from the shell so programs can access them.
-
Export a variable:
export EDITOR=vim
-
Common environment variables:
-
PATH→ directories searched for executable programs -
HOME→ user’s home directory -
LANG→ locale and language settings
-
- Example:
export PATH=${PATH}:/home/user/sbinexport LANG=fr_FR.UTF-8
Automatic Variable Setup
-
To set variables automatically at shell startup, edit Bash startup scripts like
~/.bashrc:vim ~/.bashrcPATH="$HOME/.local/bin:$HOME/bin:$PATH" export PATH export EDITOR=vim
Unsetting and Unexporting Variables
-
Remove a variable entirely:
unset PS1
-
Remove export without unsetting:
export -n PS1
Managing Users and Groups
Users
A user represents an account on the system that can log in and access resources.
Each user has a unique username and a user ID (UID).
Users can have different levels of privileges:
Root user (
UID 0) → Superuser with full system control (UID 0).Regular users → Created for normal activities, with limited access.
- System User → Used for system processes and services (non-login accounts).
- You can use the
idcommand to show information about the currently logged-in user, also you can pass the username to theidcommand as an argument to information about another user - Example:
[user01@host ~]$ id uid=1000(user01) gid=1000(user01) groups=1000(user01) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023[user01@host]$ id user02 uid=1002(user02) gid=1001(user02) groups=1001(user02) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
- All user account information are stored in /etc/passwd file, where each line represents one user, with fields separated by colons (
:)username:password:UID:GID:GECOS:home_directory:shell→ Syntax of /etc/passwdalice:x:1001:1001:Alice Smith:/home/alice:/bin/bash→ entry in /etc/passwd
getent passwd username→ Displays user info from /etc/passwd.
| Field | Description |
|---|---|
username | Login name |
x | Placeholder for password (actual passwords are stored in /etc/shadow) |
UID | User ID number |
GID | Primary group ID number |
GECOS | User information (full name, contact, etc.) |
home_directory | Path to the user’s home folder |
shell | Default shell for the user (e.g., /bin/bash) |
Groups
A group is a collection of users. Groups are used to manage permissions collectively.
Each group has a group name and a group ID (GID).
A user can belong to one primary group and multiple secondary groups.
implifies file permission management.
Allows multiple users to share access to files and directories without giving them full system access.
groups alice→ Lists all groups user ‘alice’ belongs to- All group information are stored in /etc/group
file, where each line represents one group, with fields separated by colons (:)group_name:password:GID:user_list→ Syntax of /etc/groupdevteam:x:1002:alice,bob,charlie→ entry in /etc/group
getent group groupname→ Displays group info from /etc/group.
| Field | Description |
|---|---|
group_name | Name of the group |
x | Placeholder for group password (rarely used) |
GID | Group ID number |
user_list | Comma-separated list of users in this group |
Superuser Access
The Superuser (Root) user has complete control over a Linux system including files, users, and devices.
Normal users have limited access; for example, they can manage USB devices but cannot modify system files.
The root account is equivalent to the Administrator account in Windows.
For security reasons, it’s recommended not to log in directly as root. Instead, log in as a normal user and use commands like
suorsudowhen administrative privileges are needed.
- Two common ways to start a root shell:
sudo -i→ Starts an interactive root shell with login scripts.sudo su -→ Starts a full root login shell.
- Both achieve similar results, but
sudo -iis generally preferred in Red Hat systems.
Switching Users
The
su(substitute user) command allows switching to another user account.su - username→ Syntax- Example:
[user1@host ~]$ su - user2 Password: [user2@host ~]$su -→ switches to root
Running Commands with sudo
The
sudocommand allows a permitted user to run commands as another user (usually root).Unlike
su, it asks for the user’s own password, not the root password.Example:
[user01@host ~]$ sudo usermod -L user02 [sudo] password for user01:
- Every sudo action is recorded in
/var/log/secure.
Configuring Sudo Access
The
sudocommand allows users to run commands with the privileges of another user, typically the root user. Proper configuration ensures secure and controlled administrative access.The main configuration file for sudo is /etc/sudoers. Always edit with
visudoto prevent syntax errors.%wheel ALL=(ALL) ALL%wheel→ applies to the wheel group.ALL=(ALL)→ can run commands as any user on any host.Final
ALL→ can run any command.
Files in
/etc/sudoers.d/are automatically included by/etc/sudoers. The permission for those files should be 440- To grants sudo to a specific user, you would create a file
/etc/sudoers.d/user01with the following content:user01 ALL=(ALL) ALL
- To grants sudo to a group, you would create a file
/etc/sudoers.d/group01with the following content:%group01 ALL=(ALL) ALL
ansible ALL=(ALL) NOPASSWD:ALL→ To allow a user to run commands without a password (commonly used in automation or cloud setups)
Managing Users
- The useradd and usermod commands in Linux are used to create and manage user accounts.
- They allow administrators to define user details such as home directories, shells, group memberships, and account permissions efficiently.
- When a user is added, a home directory is usually created, a primary group with the same name is assigned, and any additional groups the user belongs to are called supplementary groups.
| Command | Option | Description |
|---|---|---|
| useradd, usermod | -c "comment" | Add or update the user’s description or comment field (real name, etc.). |
| useradd, usermod | -d /path/to/home | Specify a custom home directory for the user account. |
| useradd, usermod | -m | Create the home directory if it doesn’t exist (useradd) or move it to a new locationwith usermod -d. |
| useradd, usermod | -s /bin/bash | Set or change the user’s login shell. |
| useradd | -u UID | Assign a specific User ID number. |
| useradd, usermod | -g group | Assign a primary group for the user account. |
| useradd, usermod | -G group1,group2 | Add or set additional (supplementary) groups for the user. |
| usermod | -a | Append the user to supplementary groups instead of replacing existing ones (used with-G). |
| useradd | -e YYYY-MM-DD | Set an account expiration date. |
| useradd | -p password | Assign an encrypted password for the user account. |
| usermod | -L | Lock the user account (disables login). |
| usermod | -U | Unlock a previously locked user account. |
- Example:
useradd user01useradd -m -c "Dev User" -s /bin/bash -G devteam johnusermod -aG docker john→ -aG command adds user to a supplementary groupusermod -g group01 user02→ -g option here with usermod command create a different primary group for the user
- The passwd command is used to set the password for the user, Only root can change other users’ passwords.
- Example:
passwd user01→ This will prompt the user to enter the password for user01
- The userdel command is used to delete a user account from the system. It should be used with the
-roption to ensure that the user’s home directory and mail spool are also removed. - Example:
userdel user01→ Deletes the user, but the home directory remains.userdel -r john→ Deletes the user and their home directory.
- Red Hat Enterprise Linux uses specific UID numbers and ranges for different purposes:
UID 0 → Always assigned to the superuser account (
root).UID 1–200 → “System users” assigned statically to system processes by Red Hat.
UID 201–999 → “System users” used by processes that do not own files; typically assigned dynamically when software is installed. These unprivileged users have limited access to system resources.
UID 1000+ → Available for assignment to regular users.
Managing Groups
- A group must exist before a user can be added to that group.
- Groups help manage permissions and access control for multiple users efficiently.
- The groupadd command is used to create new group in the system.
| Command | Option | Description |
|---|---|---|
groupadd | -g GID | Assign a specific Group ID. |
groupadd | -r | Create a system group (typically with a GID < 1000). |
- Example:
groupadd developersgroupadd -g 1050 adminsgroupadd -r group02
- The groupmod is used to change the group details, such as name of GID
| Command | Option | Description |
|---|---|---|
groupmod | -g GID | Change the group ID. |
groupmod | -n new_name old_name | Rename the group. |
- Example:
groupmod -g 1200 developersgroupmod -n devteam developers
- The groupdel command is used to remove the group from the system.
- A group cannot be deleted if it is the primary group of any existing user.
- You must modify or delete the user first before removing the group.
- Example:
groupdel devteam
Managing User Passwords
- User password management in Linux is critical for security and access control. Passwords, aging policies, and account restrictions are managed through files like
/etc/shadowand commands such aspasswd,chage, andusermod. - The passwords stored in
/etc/shadoware encrypted and accessible only by root. - Example: Entry of
/etc/shadowfileuser01:$6$CSsXcYG1L/4ZfHr/$2W6evvJahUfzfHpc9X.45Jc6H30E...:19500:0:90:7:14:20000:
- Each line in
/etc/shadowcontains nine colon-separated fields, including:
| Field Name | Example Value | Description |
|---|---|---|
Username | user01 | The account name. |
Encrypted Password | $6$CSsXcYG1L/4ZfHr/$2W6evvJahUfzfHpc9X.45Jc6H30E... | Hashed password using SHA-512. |
Last Password Change | 19500 | Days since 1970-01-01 when the password was last changed. |
Minimum Days | 0 | Minimum number of days before the password can be changed again. |
Maximum Days | 90 | Maximum number of days before the password must be changed. |
Warning Period | 7 | Number of days before expiration that the user is warned. |
Inactivity Period | 14 | Days after password expiry before the account is disabled. |
Account Expiration Date | 20000 | Date (in days since 1970-01-01) when the account will expire. |
Reserved Field | (empty) | Reserved for future use; usually left blank. |
An encrypted password entry looks like:
$6$CSsXcYG1L/4ZfHr/$2W6evvJahUfzfHpc9X.45Jc6H30E...$6$→ SHA-512 algorithmCSsXcYG1L/4ZfHr/→ Salt (random data to strengthen security)Remainder → Encrypted password hash
You can manage password aging with the
chagecommand:
| Command Example | Description |
|---|---|
sudo chage -m 0 -M 90 -W 7 -I 14 user01 | Set minimum (0), maximum (90), warning (7), and inactivity (14) days. |
sudo chage -d 0 user01 | Force user to change password at next login. |
sudo chage -E 2025-12-31 user01 | Expire account on a specific date. |
sudo chage -l user01 | Display password aging information. |
- Default password aging rules are set in
/etc/login.defsusing:PASS_MAX_DAYS→ Max password agePASS_MIN_DAYS→ Min password agePASS_WARN_AGE→ Warning period before expiration
- Restricting account access (locking) is preferred over deletion when temporarily disabling a user, such as during employee offboarding.
| Command | Description |
|---|---|
sudo usermod -L username | Lock user account (disable password login). |
sudo usermod -U username | Unlock user account. |
sudo usermod -L -e 2025-12-31 username | Lock and expire the account simultaneously. |
- For accounts that should not log in interactively (like service accounts), assign the
nologinshell:sudo usermod -s /sbin/nologin username
- This enhances security while still allowing background processes (like mail or services) to use the account.
Controlling Access to Files
Linux permissions determine what users can do with files and directories. Permissions are divided into three types: read (r), write (w), and execute (x), and apply to owner, group, and others.
| Permission | Effect on Files | Effect on Directories |
|---|---|---|
| r | Can read the file contents | Can list file names in the directory |
| w | Can modify the file contents | Can create or delete files in the directory |
| x | Can execute the file as a command | Can access directory contents and traverse it if file permissions allow |
The ls -l command shows detailed information about files and directories:
-
[user@host ~]$ ls -l test -rw-rw-r-- 1 student student 0 Feb 8 17:36 test -
[user@host ~]$ ls -ld /home drwxr-xr-x 5 root root 4096 Jan 31 22:00 /home
Below is a breakdown of the ls -l command output to help you understand what each part represents.
- First character: File type
-
-→ Regular file -
d→ Directory -
l→ Symbolic link -
b/c→ Block/character device -
p/s→ Special-purpose files
-
- Next nine characters: Permissions
-
1st set of 3 characters → Owner
-
2nd set of 3 characters→ Group
-
3rd set of 3 characters→ Others
-
r= read,w= write,x= execute -
-= permission not granted
-
- Number: Number of hard links
- Owner and group: File ownership
- Example:
-
-rw-rw-r-- 1 student student-
Owner → read & write
-
Group → read & write
-
Others → read only
-
-
drwxr-xr-x 5 root root-
Owner → read, write, execute
-
Group → read & execute
-
Others → read & execute
-
-
ls -l output (_ in _rwxrwxrwx), can vary depending on file type or special flags.
Numeric Permissions
- Each permission has a numeric value:
-
r = 4→ Readw = 2→ Writex = 1→ Execute
- Numeric permission calculation:
-
- Example:
rwx→ 4 + 2 + 1 = 7 - Example:
rw-→ 4 + 2 + 0 = 6 - Example:
r--→ 4 + 0 + 0 = 4 - Example:
r-x→ 4 + 0 + 1 = 5
- Example:
File Permissions and Ownership
The chmod command is used to change file and directory permissions. It stands for “change mode”, since permissions are also known as the mode of a file.
The command accepts a permission instruction followed by one or more files or directories.
Permissions can be changed in two ways:
-
Symbolic method
-
Numeric (octal) method
Symbolic Method
chmod [Who][What][Which] file|directory
| Part | Meaning | Values |
|---|---|---|
| Who | Whose permissions to change | u = user (owner), g = group, o = others, a = all |
| What | Action to perform | + = add, - = remove, = = set exactly |
| Which | Which permissions | r = read, w = write, x = execute |
You can modify one or more existing permissions without resetting all of them.
Use:
-
+to add -
-to remove -
=to set exact permissions
chmod go-rw file1→ Remove read and write permission for group and others onfile1chmod a+x file2→ Add execute permission for everyone onfile2
Using an uppercase X instead of lowercase x adds execute permission only if the file is a directory or already executable for user, group, or others.
The -R option applies changes recursively to all files and subdirectories.
Example:
-
chmod -R g+rwX demodir -
Adds read and write permissions for the group on
demodirand all its contents. -
Adds execute permission only to directories or files that already have execute permission.
Numeric(Octal) Method
chmod ### file|directory
Each digit represents permissions for user, group, and others, in that order.
| Level | Permissions | Calculation | Value |
|---|---|---|---|
| User | rwx |
4+2+1 |
7 |
| Group | r-x |
4+0+1 |
5 |
| Others | --- |
0+0+0 |
0 |
Numeric code: 750
chmod 750 filename
Example:
chmod 644 samplefile→ User: read/write, Group: read, Others: readchmod 750 sampledir→ User: read/write/execute, Group: read/execute, Others: none
Changing File and Directory Ownership
When a file is created, it is owned by:
-
The user who created it.
-
The user’s primary group (usually a private group for that user).
Sometimes, ownership must be changed to grant access to another user or group.
The chown command is used to changed the ownership of files and directories
chown [OPTION] OWNER:GROUP FILE|DIRECTORY
-
Only root can change file ownership.
-
Both root and file owner can change group ownership (if the owner is a member of that group).
- We can mention just the Owner or Group or Both
chown student test_file→ Changes owner oftest_fileto userstudentchown -R student test_dir→ Recursively changes ownership oftest_dirand its contents tostudentchown :admins test_dir→ Changes the group oftest_dirtoadminschown visitor:guests test_dir→ Changes both owner tovisitorand group toguests
Alternatively, group ownership can be changed using chgrp command
-
chgrp groupname file|directory -
chgrp admins test_dir
Special Permissions in Linux
Special permissions are advanced access features that extend beyond basic user, group, and other permissions. They modify how files and directories behave when accessed or executed.
| Special Permission | Effect on Files | Effect on Directories |
|---|---|---|
| u+s (setuid) | File executes as the file owner, not the user running it. | No effect. |
| g+s (setgid) | File executes as the file’s group, not the user’s group. | Files created inside inherit the directory’s group ownership. |
| o+t (sticky bit) | No effect. | Users can only delete their own files even if they have write access to the directory. |
ls -l command and examining the output.
Refer to the table below for examples of how special permission bits appear in listings.
| Example | Meaning |
|---|---|
-rwsr-xr-x → s replaces x in owner field |
setuid active (e.g., /usr/bin/passwd) |
drwxr-sr-x → s replaces x in group field |
setgid active (e.g., /run/log/journal) |
drwxrwxrwt → t replaces x in others field |
sticky bit active (e.g., /tmp) |
- Symbolic
u+s,g+s,o+t→ Syntax-
chmod g+s directory→ Example
- Numeric (4th digit)
4= setuid,2= setgid,1= sticky → Syntaxchmod 2770 directory→ Example
Default Permissions & umask
When a file or directory is created, it’s assigned default permissions determined by two factors:
-
Type of item (file or directory)
-
User’s umask (user file-creation mode mask)
The default permission before applying umask for file is 0666 (-rw-rw-rw-) and for directory is 0777 (drwxrwxrwx)
umask defines which permission bits should be removed (masked) from the default permission set when new files or directories are created.
Example:
[user@host ~]$ umask 0002-
This clears the write bit for others, resulting in:
-
New file →
-rw-rw-r-- -
New directory →
drwxrwxr-x
-
| umask | File Permissions | Directory Permissions | Description |
|---|---|---|---|
0002 |
-rw-rw-r-- |
drwxrwxr-x |
Default for collaborative environments |
0000 |
-rw-rw-rw- |
drwxrwxrwx |
Everyone has full access |
007 |
-rw-rw---- |
drwxrwx--- |
Restricts access to others |
027 |
-rw-r----- |
drwxr-x--- |
Secure default; no access for others |
umask 027, where 027 represents the permission mask value and can be changed to any valid octal value as needed.
To make the umask configuration persistent, modify the global settings in /etc/profile or /etc/bashrc. For user-specific overrides, make changes in the ~/.bashrc or ~/.bash_profile files.
You must log out and back in for global umask changes to take effect. Access Control List (ACL)
Standard Linux file permissions work well when files are used by a single owner and one group.
However, when files must be accessed by multiple users or groups with different permission sets, Access Control Lists (ACLs) come into play.
ACLs allow administrators to grant permissions to multiple named users or groups (by username, group name, UID, or GID) using the same permission flags as standard file permissions:
- r – read
- w – write
- x – execute
Named users and groups are not visible in a simple ls -l output, they are stored within the ACL structure.
Who Can Set ACLs
File owners can set ACLs on their own files or directories.
Privileged users with the
CAP_FOWNERcapability can set ACLs on any file or directory.Inheritance: New files and subdirectories inherit ACLs from their parent directory’s default ACL, if one exists.
Note: The parent directory must have the execute (search) permission set for named users/groups to access the contents.
File-System ACL Support
File systems must have ACL support enabled.
XFS: ACLs are built-in.
ext3/ext4 (RHEL 8 and later): ACL option enabled by default.
Older systems: Verify ACL support manually.
You can enable ACL support by mounting the filesystem with the acl option:
mount -o acl /dev/sdX /mountpoint
Or, persist it in /etc/fstab:
UUID=<id> /data ext4 defaults,acl 0 2
Viewing and Interpreting ACL Permissions
The ls -l command gives a minimal hint of ACLs:
[user@host content]$ ls -l reports.txt -rwxrw----+ 1 user operators 130 Mar 19 23:56 reports.txt
The + at the end of the permission string means extended ACLs exist.
| Field | Meaning |
|---|---|
| user: | User ACL (same as file owner permissions) |
| group: | Current ACL mask, not group owner permissions |
| other: | Same as standard “other” file permissions |
Viewing File ACLs
Use getfacl to display ACLs:
[user@host content]$ getfacl reports.txt # file: reports.txt # owner: user # group: operators user::rwx user:consultant3:--- user:1005:rwx #effective:rw- group::rwx #effective:rw- group:consultant1:r-- group:2210:rwx #effective:rw- mask::rw- other::---
Breakdown:
- Commented Entries
# file: File name
# owner: File owner
# group: Group owner
- User Entries
user::rwx→ File owner permissionsuser:consultant3:---→ Named user (no permissions)user:1005:rwx #effective:rw-→ Named user limited by mask
- Group Entries
group::rwx #effective:rw-→ Group owner limited by maskgroup:consultant1:r--→ Named group (read-only)group:2210:rwx #effective:rw-→ Named group limited by mask
- Mask Entry
mask::rw-→ Maximum allowed permissions for named users/groups
- Other Entry
other::---→ No permissions for anyone else
Viewing Directory ACLs
Use getfacl to display ACLs:
[user@host content]$ getfacl . # file: . # owner: user # group: operators # flags: -s- user::rwx user:consultant3:--- group::rwx group:consultant1:r-x mask::rwx other::--- default:user::rwx default:group::rwx default:mask::rwx default:other::---
The execute (x) permission allows directory traversal/search.
Default ACLs determine permissions for newly created files or subdirectories.
Breakdown:
default:user::rwx→ Default owner permissionsdefault:user:name:rx→ Default named user permissionsdefault:group::rwx→ Default group permissionsdefault:mask::rwx→ Maximum default permissionsdefault:other::---→ No permissions for others
Default ACLs do not grant access to the directory itself, they define inheritance rules for new files/subdirectories.
ACL Mask
The ACL mask defines the maximum permissions possible for:
Named users
The group owner
Named groups
It does not affect the file owner or “others”.
View mask:
getfacl fileSet mask:
setfacl -m m::perms file
When ACLs change, the mask is recalculated automatically unless you use the -n flag or explicitly redefine it.
ACL Permission Precedence
When a process accesses a file:
If user = owner → use file owner ACL
If user matches named user entry → use that entry (limited by mask)
If group matches group owner or named group → use that group’s ACL (limited by mask)
Else → apply the
otherACL
Changing ACL Permissions
The setfacl command is used to add, modify, or remove ACLs.
| Command | Purpose |
|---|---|
setfacl -m u:name:rX file | Add or modify a user ACL |
setfacl -m g:name:rw file | Add or modify a group ACL |
setfacl -m o::- file | Modify other permissions |
setfacl -m u::rwx,g:consultants:rX,o::- file | Combine multiple entries in one command |
getfacl file-A | setfacl --set-file=- file-B | Use getfacl output as input to copy ACLs between files |
setfacl -m m::r file | Set explicit ACL mask (restrict named users and groups to read-only) |
setfacl -R -m u:name:rX directory | Apply ACL recursively; uppercase X sets execute only on directories |
setfacl -x u:name,g:name file | Remove specific ACL entries |
setfacl -b file | Remove all ACLs from a file |
setfacl -m d:u:name:rx directory | Set default ACLs on directories (files inherit permissions; directories get execute if x included) |
setfacl -x d:u:name directory | Delete a specific default ACL entry |
setfacl -k directory | Delete all default ACLs on a directory |
getfacl file | View ACLs |
setfacl -m | Modify/add ACLs |
setfacl -x | Delete specific ACL entries |
setfacl -b | Remove all ACLs |
setfacl -k | Remove all default ACLs |
setfacl -R | Apply changes recursively |
mount -o acl | Enable ACL support on mount |
Monitoring and Managing Linux Processes
Linux Process
A process is a running instance of a program. When you launch an executable, Linux creates a process for it.
A process includes:
Allocated memory space
Security details (user, group, privileges)
One or more execution threads
A unique Process ID (PID)
Every process is started by another process, called its parent, identified by the Parent Process ID (PPID).
The first process on a Linux system is systemd, and all others are its descendants.
When a new process is created, the parent duplicates itself using fork(), and the new child may replace its program using exec().
After finishing, the child exits, and the parent removes its record from the process table.
Process States
In a multitasking OS, each CPU runs only one process at a time.
Linux assigns every process a state based on what it’s doing.
| State | Flag | Description |
|---|---|---|
| Running | R | The process is currently running or ready to run. |
| Sleeping | S | Waiting for an event, resource, or signal. |
| Uninterruptible Sleep | D | Waiting for hardware or I/O; cannot be interrupted. |
| Stopped | T | Suspended (for debugging or via signal like Ctrl+Z). |
| Zombie | Z | Process finished but not yet removed from the process table. |
You can view process states in the S column of top or the STAT column of ps
Viewing Processes in Linux
The ps command (Process Status) displays details about currently running processes which includes user, PID, parent process, CPU/memory usage, state, and command name.
| Command | Description |
|---|---|
ps aux | BSD-style full listing (shows all users and processes). |
ps -ef | UNIX/POSIX-style full listing of all processes. |
ps --forest | Displays processes in a tree view (parent → child relationships). |
ps -eo pid,user,%cpu,%mem,cmd | Shows custom columns (script-friendly). |
--sort | Sorts output (e.g., --sort=-%mem for top memory consumers). |
pstree | Displays all running processes in a tree-like hierarchical structure. |
top | Displays real-time, dynamic process information including CPU and memory usage. |
Example:
$ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 17:45 ? 0:03 /usr/lib/systemd/systemd root 2 0 0 17:45 ? 0:00 [kthreadd] root 2149 1 0 18:07 pts/0 0:00 bash user 2205 2149 0 18:08 pts/0 0:00 ps -ef- UNIX/POSIX-style with UID, PID, PPID, STIME, CMD columns.
$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 51648 7504 ? Ss 17:45 0:03 /usr/lib/systemd/systemd root 2 0.0 0.0 0 0 ? S 17:45 0:00 [kthreadd] student 2149 0.0 0.2 266904 3836 pts/0 R+ 18:07 0:00 ps aux- BSD-style format with
%CPU,%MEM, VSZ/RSS columns.
- BSD-style format with
$ ps --forest PID TTY STAT TIME COMMAND 2149 pts/0 Ss 0:00 bash 2205 pts/0 R+ 0:00 \_ ps --forestShows hierarchical (parent → child) relationships.
$ pstree systemd─┬─NetworkManager ├─sshd───bash───ps └─cronDisplays process hierarchy as a tree.
$ ps -eo pid,user,%mem,cmd --sort=-%mem | head -n 10Shows the top 10 memory-consuming processes.
Jobs and Sessions
Job control is a feature of the shell that allows a single shell instance to run and manage multiple commands.
What is a Job?
A job is associated with each pipeline entered at a shell prompt.
All processes in that pipeline belong to the same process group.
A single command is treated as a minimal pipeline, creating a job with only one member.
Foreground vs Background Processes
Only one job can read input and keyboard-generated signals from a terminal at a time.
Processes in this job are foreground processes of that terminal.
Background processes:
Cannot read input from the terminal.
May still write output to the terminal.
Can be running or stopped (suspended).
A background process attempting to read input will automatically be suspended.
Sessions
Each terminal is its own session with:
A foreground process.
Any number of background processes.
A job belongs to exactly one session (its controlling terminal).
Processes started by the system (e.g., daemons) have no controlling terminal. In
ps, they show?in the TTY column.
Running Jobs in the Background
Append an ampersand
&to run a command or pipeline in the background:[user@host ~]$ sleep 10000 &[1] 5947 [user@host ~]$
Listing Jobs
- Use the
jobscommand to see jobs being tracked by Bash: [user@host ~]$ jobs [1]+ Running sleep 10000 [user@host ~]$
Foreground a background job
[user@host ~]$ fg %1 sleep 10000
Suspend a foreground process
- Press
Ctrl+Z sleep 10000 ^Z [1]+ Stopped sleep 10000- The shell warns users who try to exit a terminal with suspended jobs. Exiting again kills suspended jobs.
Resume suspended process in background
[user@host ~]$ bg %1 [1]+ sleep 10000 &
Viewing Jobs
ps jcommand shows process, job, and session info[user@host ~]$ ps j PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 2764 2768 2768 2768 pts/0 6377 Ss 1000 0:00 /bin/bash 2768 5947 5947 2768 pts/0 6377 T 1000 0:00 sleep 10000 2768 6377 6377 2768 pts/0 6377 R+ 1000 0:00 ps j
| Field | Meaning |
|---|---|
| PID | Process ID |
| PPID | Parent Process ID |
| PGID | Process Group ID (job leader) |
| SID | Session ID (usually the interactive shell) |
| STAT | Process state (e.g., T = stopped) |
Killing Processes
A signal is a software interrupt delivered to a process, reporting events such as errors, external events (I/O requests, expired timers), or explicit commands/keyboard sequences.
| Signal No | Short Name | Definition / Purpose |
|---|---|---|
| 1 | HUP | Hangup; report termination of the controlling process of a terminal or request process reinitialization (e.g., config reload). |
| 2 | INT | Keyboard interrupt; causes program termination. Sent via Ctrl+C. |
| 3 | QUIT | Keyboard quit; similar to SIGINT but generates a core dump. Sent via Ctrl+\. |
| 9 | KILL | Unblockable; causes abrupt program termination. Cannot be blocked, ignored, or handled. |
| 15 | TERM | Default terminate; polite way to ask a program to exit. Can be blocked or handled by the process. |
| 18 | CONT | Continue; resume a stopped process. Cannot be blocked. |
| 19 | STOP | Stop; unblockable suspend of a process. |
| 20 | TSTP | Keyboard stop; similar to STOP but can be blocked or handled. Sent via Ctrl+Z. |
Default Signal Actions
Term – terminate process immediately.
Core – terminate process and save memory image (core dump).
Stop – suspend process, wait to continue (resume).
Sending Signals to Processes using Keyboard
Ctrl+C – SIGINT
Ctrl+Z – SIGTSTP (suspend)
Ctrl+\ – SIGQUIT (core dump)
Sending Signals to Processes using Commands
kill – send a signal to a process by PID.
[user@host ~]$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
Example
[user@host ~]$ ps aux | grep job 5194 0.0 0.1 222448 2980 pts/1 S 16:39 0:00 /bin/bash control job1 5199 0.0 0.1 222448 3132 pts/1 S 16:39 0:00 /bin/bash control job2 5205 0.0 0.1 222448 3124 pts/1 S 16:39 0:00 /bin/bash control job3 [user@host ~]$ kill 5194 [1] Terminated control job1 [user@host ~]$ kill -9 5199 [2]- Killed control job2 [user@host ~]$ kill -SIGTERM 5205 [3]+ Terminated control job3
killall – signal multiple processes by command name.
killall control→ This is an example command, here control is the command name
pkill – signal processes based on command name, UID, GID, terminal, parent PID, etc.
[user@host ~]$ pkill control [1] Terminated control pkill1 [2]- Terminated control pkill2 [3]+ Terminated control pkill3
Logging Users Out Administratively
Identify login sessions with
w:[user@host ~]$ w USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root tty2 12:26 14:58 0.04s 0.04s -bash bob tty3 12:28 14:42 0.02s 0.02s -bash user pts/1 desk.example.com 12:41 2.00s 0.03s 0.03s w
Terminate processes by user:
[root@host ~]# pgrep -l -u bob 6964 bash 6998 sleep 6999 sleep [root@host ~]# pkill -SIGKILL -u bob [root@host ~]# pgrep -l -u bob # No processes remain
Terminate processes by terminal:
[root@host ~]# pkill -t tty3 [root@host ~]# pkill -SIGKILL -t tty3
- Terminate child processes using parent PID:
[root@host ~]# pstree -p bob bash(8391)─┬─sleep(8425) ├─sleep(8426) └─sleep(8427) [root@host ~]# pkill -P 8391 # Kill all children of bash [root@host ~]# pkill -SIGKILL -P 8391
Monitoring Process Activity
Load average is a measurement provided by the Linux kernel that represents the perceived system load over time. It gives a rough gauge of how many system resource requests are pending and whether system load is increasing or decreasing.
Every five seconds, the kernel collects the current load number based on the number of processes in runnable (R) and uninterruptible (D) states.
This number is then reported as an exponential moving average over the most recent 1, 5, and 15 minutes.
Commands That Show Load Average
uptimewtop
Load Average Calculation
The load average represents how many processes are ready to run or waiting for I/O (disk/network).
The value includes:
Processes waiting for CPU (state R)
Processes waiting for I/O completion (state D)
If load averages are high while CPU activity is low, check for heavy disk or network activity.
You can view the current load average using the
uptimecommand:[user@host ~]$ uptime 15:29:03 up 14 min, 2 users, load average: 2.92, 4.48, 5.20
The three numbers represent the load over the last 1, 5, and 15 minutes.
Per-CPU Load
- If load mainly comes from CPU-bound processes, divide the load average by the number of logical CPUs to check whether the system is overloaded.
- Use
lscputo determine CPU count:[user@host ~]$ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 2 Core(s) per socket: 2 Socket(s): 1 NUMA node(s): 1 ...output omitted...
- This is a 4 logical CPU system (2 cores × 2 threads).
- Now divide the load average by 4:
- Load average: 2.92, 4.48, 5.20
- Number of logical CPUs: 4
- Per-CPU load (Load average ÷ CPUs): 0.73, 1.12, 1.30
- Interpretation:
- – 0.73 (1 min): CPUs ~73% utilized, normal.
- – 1.12 (5 min): ~112% utilized, ~12%overloaded.
- – 1.30 (15 min): ~130% utilized, ~30%overloaded.
A load value of 0 = idle CPU queue (no waiting processes).
A load value of 1 = one process actively using a CPU (no wait).
Load increases as more processes wait for CPU or I/O.
- Processes waiting for I/O also increase the load average, even if CPU usage is low, since they represent blocked tasks.
- A load average below 1 (per CPU) means the system is performing efficiently.
- Once utilization approaches 100%, new processes must wait, and load numbers climb above 1.
Real-Time Process Monitoring
- The
topprogram provides a continuously updating view of active processes. - Unlike
ps,topupdates dynamically at a configurable interval.
| Column | Description |
|---|---|
| PID | Process ID |
| USER | Owner of the process |
| VIRT | Total virtual memory used (includes code, data, shared libs, and swapped pages), same as VSZ in ps |
| RES | Physical resident memory (same as RSS in ps) |
| S | Process state: • D = Uninterruptible sleep • R = Running • S = Sleeping • T = Stopped/Traced • Z = Zombie |
| TIME | Total CPU time used since process start |
| COMMAND | Process command name |
Fundamental Keystrokes in top
| Key | Purpose |
|---|---|
? or h | Help for interactive keystrokes |
l, t, m | Toggle load, threads, or memory header lines |
1 | Show individual CPU stats or a combined summary |
s | Change refresh rate (e.g., 0.5, 1, 5 seconds) |
b | Toggle reverse highlight for running processes |
Shift + b | Enable bold display for header and running processes |
Shift + h | Toggle between processes and threads view |
u, Shift + u | Filter by user name |
Shift + m | Sort by memory usage (descending) |
Shift + p | Sort by CPU usage (descending) |
k | Kill a process (prompt for PID and signal) (not available in secure mode) |
r | Renice a process (prompt for PID and nice value) (not available in secure mode) |
Shift + w | Save display configuration for next run |
q | Quit top |
f | Manage columns (enable/disable fields and change sort order) |
Process Scheduling
Different processes have different levels of importance.
Scheduling policies: Most processes run under
SCHED_OTHER(orSCHED_NORMAL).Nice value: Determines relative priority.
Nice level range:
-20→ highest priority0→ default19→ lowest priority
Rules:
Higher nice level → less CPU priority (process gives up CPU easily).
Lower nice level → higher CPU priority.
If there are fewer processes than CPUs, even high nice level processes get full CPU time.
Nice Levels
Only root may reduce a process nice level.
Unprivileged users can only increase the nice level of their own processes(max 19.
- Reporting Nice Levels Using
top- The NI column → process nice value
- The PR column → scheduled priority
- Example:
- Nice
-20→ PR0 - Nice
19→ PR39
- Nice
- Reporting Nice Levels Using
ps [user@host ~]$ ps axo pid,comm,nice,cls --sort=-nice PID COMMAND NI CLS 30 khugepaged 19 TS 29 ksmd 5 TS 1 systemd 0 TS 2 kthreadd 0 TS 9 ksoftirqd/0 0 TS 10 rcu_sched 0 TS 11 migration/0 - FF 12 watchdog/0 - FF
TS → SCHED_NORMAL
– → other scheduling policies
Starting Processes with Different Nice Levels
- Inherit nice level from shell
[user@host ~]$ sha1sum /dev/zero & [1] 3480 [user@host ~]$ ps -o pid,comm,nice 3480 PID COMMAND NI 3480 sha1sum 0
- Using
nicecommand (default 10)[user@host ~]$ nice sha1sum /dev/zero & [1] 3517 [user@host ~]$ ps -o pid,comm,nice 3517 PID COMMAND NI 3517 sha1sum 10
- Using
nice -nfor custom nice level[user@host ~]$ nice -n 15 sha1sum & [1] 3521 [user@host ~]$ ps -o pid,comm,nice 3521 PID COMMAND NI 3521 sha1sum 15
- Changing the Nice Level of an Existing Process Using
renice[user@host ~]$ renice -n 19 3521 3521 (process ID) old priority 15, new priority 19
Controlling Services and Daemons
Introduction To Systemd
systemd manages startup and services in Linux, including activation of system resources, daemons, and processes.
Daemons run in the background, usually starting automatically at boot.
Many daemon names end with ‘d’ (e.g.,
sshd,crond).A service in systemd may be a daemon or a one-time action (called oneshot).
| Unit Type | Extension | Description |
|---|---|---|
| Service | .service | Represents a system service or daemon managed by systemd. |
| Socket | .socket | Monitors an IPC socket and automatically starts the corresponding service when a connection request arrives. |
| Path | .path | Starts a service when a specific file or directory is created, modified, or deleted. |
- To view available unit types:
systemctl -t help
Listing Services
- List all loaded and active services
[root@host ~]# systemctl list-units --type=service UNIT LOAD ACTIVE SUB DESCRIPTION atd.service loaded active running Job spooling tools auditd.service loaded active running Security Auditing Service chronyd.service loaded active running NTP client/server crond.service loaded active running Command Scheduler dbus.service loaded active running D-Bus System Message Bus getty@tty1.service loaded active running Getty on tty1 NetworkManager.service loaded active running Network Manager systemd-journald.service loaded active running Journal Service systemd-logind.service loaded active running Login Service systemd-udevd.service loaded active running Rule-based Manager for Device Events and Files ...output truncated...
UNIT – Service unit name
LOAD – Whether systemd loaded it into memory
ACTIVE – High-level activation state
SUB – Detailed low-level state
DESCRIPTION – Short explanation of the service
- List all services including inactive
systemctl list-units --type=service --all
- List installed service unit files and their enablement state
[root@host ~]# systemctl list-unit-files --type=service UNIT FILE STATE arp-ethers.service disabled atd.service enabled auditd.service enabled auth-rpcgss-module.service static autovt@.service enabled blk-availability.service disabled ...output omitted...Common states in the
STATEcolumn:enabled– Starts at bootdisabled– Doesn’t start automaticallystatic– Cannot be enabled; started by dependencymasked– Completely disabled
Viewing Service Status
- To check the status of the specific service,
systemctl status <service_name>[root@host ~]# systemctl status sshd.service ● sshd.service - OpenSSH server daemon Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled) Active: active (running) since Fri 2025-10-24 12:07:45 IST; 2h 13min ago Main PID: 1073 (sshd) Tasks: 1 (limit: 2366) Memory: 3.4M CGroup: /system.slice/sshd.service └─1073 /usr/sbin/sshd -D Oct 24 12:07:45 host.example.com systemd[1]: Started OpenSSH server daemon. Oct 24 12:07:45 host.example.com sshd[1073]: Server listening on 0.0.0.0 port 22. Oct 24 12:07:45 host.example.com sshd[1073]: Server listening on :: port 22.Loaded: Whether the service unit file is parsed and loaded.
Active: Indicates if the service is running and for how long.
Main PID: Process ID of the main daemon.
Status Messages: Show recent service logs or events.
| Status Keyword | Meaning |
|---|---|
loaded | Unit configuration file processed. |
active (running) | Service is currently running. |
active (exited) | One-time task completed successfully. |
inactive | Service is not running. |
enabled | Service is configured to start at boot. |
disabled | Service is not configured to start at boot. |
static | Service is automatically started by another unit. |
Verifying Service State
| Command | Description | Example Output |
|---|---|---|
systemctl is-active sshd.service | Check if service is running | active |
systemctl is-enabled sshd.service | Check if starts at boot | enabled |
systemctl is-failed sshd.service | Check if failed during startup | active / failed |
systemctl --failed --type=service | List all failed services | Displays failed unit names |
Starting and Stopping Services
Services may need to be manually started or stopped for updates, configuration changes, or other administrative reasons.
Start a service:
[root@host ~]# systemctl start sshd.service
- Stop a service:
[root@host ~]# systemctl stop sshd.service
Restarting and Reloading Services
- Restart a service: stops and then starts the service (new PID assigned)
[root@host ~]# systemctl restart sshd.service
- Reload a service: applies configuration changes without changing the PID
[root@host ~]# systemctl reload sshd.service
- Reload or restart if reload not supported:
[root@host ~]# systemctl reload-or-restart sshd.service
Listing Unit Dependencies
Some services depend on others to function. Use the following commands:
List dependencies for a service:
[root@host ~]# systemctl list-dependencies sshd.service sshd.service ● ├─system.slice ● ├─sshd-keygen.target ● │ ├─sshd-keygen@ecdsa.service ● │ ├─sshd-keygen@ed25519.service ● │ └─sshd-keygen@rsa.service ● └─sysinit.target
List reverse dependencies:
[root@host ~]# systemctl list-dependencies --reverse sshd.service
Masking and Unmasking Services
Masking prevents a service from starting manually or automatically.
A disabled service can be started manually.
A masked service cannot start manually or automatically.
Mask a service:
[root@host ~]# systemctl unmask sendmail Removed /etc/systemd/system/sendmail.service.
- Unmask a service:
[root@host ~]# systemctl unmask sendmail Removed /etc/systemd/system/sendmail.service.
Enabling and Disabling Services at Boot
Enable a service to start at boot:
[root@host ~]# systemctl enable sshd.service Created symlink /etc/systemd/system/multi-user.target.wants/sshd.service → /usr/lib/systemd/system/sshd.service
- Disable a service from starting at boot:
[root@host ~]# systemctl disable sshd.service Removed /etc/systemd/system/multi-user.target.wants/sshd.service
Useful systemctl Commands
| Task | Command |
|---|---|
| View detailed unit state | systemctl status UNIT |
| Stop a service | systemctl stop UNIT |
| Start a service | systemctl start UNIT |
| Restart a service | systemctl restart UNIT |
| Reload a service configuration | systemctl reload UNIT |
| Mask a service (disable manually and at boot) | systemctl mask UNIT |
| Unmask a service | systemctl unmask UNIT |
| Enable a service at boot | systemctl enable UNIT |
| Disable a service at boot | systemctl disable UNIT |
| List dependencies of a unit | systemctl list-dependencies UNIT |
Adjusting Tuning Profiles
The tuned service optimizes system performance by applying predefined tuning profiles. These profiles adjust kernel parameters, CPU behavior, disk settings, and network tuning based on workload type.
Static Tuning
Applies predefined kernel and system settings.
Settings do not change based on system load.
Suitable for predictable workloads.
Dynamic Tuning
The tuned daemon monitors system activity.
Adjusts settings dynamically at runtime.
Useful when workloads vary (e.g., storage bursts, busy hours).
Installing and Enabling tuned
To install manually:
yum install tuned systemctl enable --now tuned
Check service status:
systemctl status tuned
Available Tuning Profiles
Tuned includes multiple profiles optimized for different workloads.
| Profile | Purpose |
|---|---|
| balanced | Default. Good balance between performance & power saving. |
| desktop | Faster response for interactive desktop workloads. |
| throughput-performance | Maximum I/O and CPU throughput. |
| latency-performance | Low-latency tuning for servers; higher power usage. |
| network-latency | Low latency for network workloads (derived from latency-performance). |
| network-throughput | Maximum network throughput. |
| powersave | Aggressive power saving. |
| oracle | Optimized for Oracle databases. |
| virtual-guest | Optimizes performance for virtual machines. |
| virtual-host | Optimizes systems acting as hypervisors. |
Managing Tuning Profiles
View the active profile
[root@host ~]# tuned-adm active Current active profile: virtual-guest
List all available profiles
[root@host ~]# tuned-adm list Available profiles: - balanced - desktop - latency-performance - network-latency - network-throughput - powersave - sap - throughput-performance - virtual-guest - virtual-host Current active profile: virtual-guest
Switch to a different profile
[root@host ~]$ tuned-adm profile throughput-performance [root@host ~]$ tuned-adm active Current active profile: throughput-performance
Let tuned recommend the best profile
[root@host ~]$ tuned-adm recommend virtual-guest
Disable tuned
[root@host ~]$ tuned-adm off [root@host ~]$ tuned-adm active No current active profile.
Managing SELinux
SELinux (Security-Enhanced Linux) adds an extra layer of security by controlling access to files, directories, and ports at a granular level. Unlike standard file permissions, SELinux policies prevent processes from misusing resources even if a user has read/write access.
SELinux enforces rules through policies, which define what actions are allowed for each process and file.
Policies are enforced using labels (user, role, type, sensitivity).
SELinux Modes
SELinux can operate in three modes:
Enforcing: Access control rules are enforced (default).
Permissive: Rules are not enforced; violations are logged.
Disabled: SELinux is completely turned off (not recommended).
Check current mode:
[user@host ~]# getenforce Enforcing
Change mode temporarily:
[user@host ~]# setenforce 0 # Permissive [user@host ~]# setenforce 1 # Enforcing
Set default SELinux mode permanently in /etc/selinux/config:
SELINUX=enforcing # options: enforcing, permissive, disabled SELINUXTYPE=targeted # targeted processes are protected
SELinux Security Concepts
SELinux implements Mandatory Access Control (MAC), complementing traditional user/group permissions (Discretionary Access Control, DAC).
Every file, process, directory, and port has an SELinux context that determines access permissions.
The targeted policy is default in RHEL, enforcing rules primarily based on the type context.
Working with SELinux Contexts
Display SELinux contexts with the -Z option
Check all processes:
[user@host ~]# ps axZ LABEL PID TTY STAT TIME COMMAND system_u:system_r:init_t:s0 1 ? Ss 0:09 /usr/lib/systemd/... system_u:system_r:kernel_t:s0 2 ? S 0:00 [kthreadd] system_u:system_r:kernel_t:s0 3 ? S 0:00 [ksoftirqd/0]
Check Apache processes:
[user@host ~]# ps -ZC httpd LABEL PID TTY TIME CMD system_u:system_r:httpd_t:s0 1608 ? 00:00:05 httpd system_u:system_r:httpd_t:s0 1609 ? 00:00:00 httpd
Check SELinux labels on files:
[user@host ~]# ls -Z /var/www drwxr-xr-x. root root system_u:object_r:httpd_sys_script_exec_t:s0 cgi-bin drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 html drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 icons
SELinux File Context Example:
system_u:object_r:httpd_sys_content_t:s0 html
| SELinux Context Field | Value | Description |
|---|---|---|
| SELinux User | system_u | System-managed SELinux user; applied to system files and processes. |
| Role | object_r | Role of the object; determines which domains can access this object. Default for most files. |
| Type (Domain) | httpd_sys_content_t | Type defines the kind of object; here it represents web server content files. Apache (httpd_t) can access it. |
| Level / Sensitivity | s0 | Security level for MLS (mandatory access control). Default level in standard RHEL policies. |
| File/Directory | html | The actual file or directory being labeled. |
Initial SELinux Context
All processes and files on SELinux-enabled systems are labeled with a SELinux context.
New files inherit the SELinux context from the parent directory.
Pitfalls:
Moving a file retains the context of the original directory.
Copying a file with
cp -apreserves the original context.
- Example:
# Files created in /tmp [root@host ~]# touch /tmp/file1 /tmp/file2 [root@host ~]# ls -Z /tmp/file* unconfined_u:object_r:user_tmp_t:s0 /tmp/file1 unconfined_u:object_r:user_tmp_t:s0 /tmp/file2 # Move and copy to /var/www/html [root@host ~]# mv /tmp/file1 /var/www/html/ [root@host ~]# cp /tmp/file2 /var/www/html/ [root@host ~]# ls -Z /var/www/html/file* unconfined_u:object_r:user_tmp_t:s0 /var/www/html/file1 # moved file retains old context unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/file2 # copied file inherits directory context
Changing SELinux Context
chcon: Temporarily changes the context of a file (does not survive restorecon or relabeling).restorecon: Resets file context to the default based on policy.semanage fcontext: Defines default contexts for files and directories. Use withrestoreconto apply.- Example:
# Create a directory [root@host ~]# mkdir /virtual [root@host ~]# ls -Zd /virtual drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /virtual # Change context temporarily [root@host ~]# chcon -t httpd_sys_content_t /virtual [root@host ~]# ls -Zd /virtual drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 /virtual # Restore default context [root@host ~]# restorecon -v /virtual Relabeled /virtual from unconfined_u:object_r:httpd_sys_content_t:s0 to unconfined_u:object_r:default_t:s0
Defining Default File Context Rules
semanage fcontextmanages rules thatrestoreconuses.Common syntax:
/path(/.*)?→ applies to the directory and everything inside recursively.Basic Operations:
| Option | Description |
|---|---|
-a | Add a new file context rule. |
-d | Delete a file context rule. |
-l | List existing file context rules. |
Example: Applying Default Context to Files in /var/www/html
# Check current file contexts [root@host ~]# ls -Z /var/www/html/file* unconfined_u:object_r:user_tmp_t:s0 /var/www/html/file1 unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/file2 # List fcontext rules [root@host ~]# semanage fcontext -l /var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 # Apply rules [root@host ~]# restorecon -Rv /var/www/ Relabeled /var/www/html/file1 from unconfined_u:object_r:user_tmp_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0 [root@host ~]# ls -Z /var/www/html/file* unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/file1 unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/file2
Example: Adding a New Directory
[root@host ~]# mkdir /virtual [root@host ~]# touch /virtual/index.html [root@host ~]# ls -Zd /virtual/ drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /virtual/ [root@host ~]# semanage fcontext -a -t httpd_sys_content_t '/virtual(/.*)?' [root@host ~]# restorecon -RFvv /virtual [root@host ~]# ls -Zd /virtual/ drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 /virtual/ [root@host ~]# ls -Z /virtual/ -rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0 index.html
SELinux Booleans
SELinux booleans are configurable switches that allow administrators to adjust how SELinux policies behave without rewriting the policies themselves.
They make it possible to enable or disable specific SELinux features dynamically.
| Command | Description |
|---|---|
getsebool -a | Lists all SELinux booleans and their current states (on or off). |
getsebool <boolean_name> | Displays the state of a specific boolean. |
setsebool <boolean_name> on/off | Temporarily changes a boolean’s state (until reboot). |
setsebool -P <boolean_name> on/off | Permanently changes a boolean’s state (persists across reboots). |
semanage boolean -l | Lists all booleans with descriptions and persistence status. |
semanage boolean -l -C | Lists only booleans whose current state differs from the default. |
Example:
[user@host ~]$ getsebool httpd_enable_homedirs httpd_enable_homedirs --> off [user@host ~]$ sudo setsebool httpd_enable_homedirs on [user@host ~]$ sudo semanage boolean -l | grep httpd_enable_homedirs httpd_enable_homedirs (on , off) Allow httpd to enable homedirs [user@host ~]$ sudo setsebool -P httpd_enable_homedirs on [user@host ~]$ sudo semanage boolean -l | grep httpd_enable_homedirs httpd_enable_homedirs (on , on) Allow httpd to enable homedirs [user@host ~]$ sudo semanage boolean -l -C SELinux boolean State Default Description cron_can_relabel (off , on) Allow cron to relabel
Troubleshooting SELinux Issues
When SELinux prevents access to files or services that should be accessible, follow these steps:
1. Verify Legitimate Denial
Before changing SELinux settings, confirm whether the denial is valid.
Example: If Apache (httpd) tries to access /home, SELinux may be correctly preventing access to user directories.
2. Check File Contexts
The most common issue is incorrect file labeling (context mismatch).
If a file was moved from one directory to another, its context might be wrong.
Fix using:
restorecon -Rv /path/to/file_or_directory
This command resets the context to the correct default.
3. Adjust SELinux Booleans
If access is still denied, you may need to enable a boolean that allows the desired behavior.
Example:
sudo setsebool -P ftpd_anon_write on
This enables anonymous FTP uploads.
4. Policy Bug (Rare)
If you suspect an SELinux policy bug is preventing legitimate access, contact Red Hat Support.
Using setroubleshoot
Install and use the setroubleshoot-server tool to analyze SELinux denials:
sudo dnf install setroubleshoot-server
This service monitors /var/log/audit/audit.log and sends summaries to /var/log/messages.
| Command | Description |
|---|---|
sealert -l <UUID> | View detailed info for a specific SELinux violation. |
sealert -a /var/log/audit/audit.log | Analyze all audit logs for SELinux issues. |
ausearch -m AVC -ts recent | Search for recent SELinux denials. |
Example Scenario
A file was moved into the web root, but Apache can’t access it:
[root@host ~]# touch /root/file3 [root@host ~]# mv /root/file3 /var/www/html [root@host ~]# systemctl start httpd [root@host ~]# curl http://localhost/file3Result: 403 Forbidden
Inspect Audit Logs
[root@host ~]# tail /var/log/audit/audit.log ...output omitted... type=AVC msg=audit: avc: denied { getattr } for pid=1609 comm="httpd" path="/var/www/html/file3" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file ...output omitted...
Check Human-Readable Message
[root@host ~]# tail /var/log/messages ...output omitted... host setroubleshoot: SELinux is preventing /usr/sbin/httpd from getattr access on the file . For complete SELinux messages. run sealert -l 613ca624-248d-48a2-a7d9-d28f5bbe2763
Analyze with sealert
[root@host ~]# sealert -l 613ca624-248d-48a2-a7d9-d28f5bbe2763
Output suggests generating a local policy module or restoring context.
Fix the file context:
[root@host ~]# restorecon /var/www/html/file3
Or recursively:
[root@host ~]# restorecon -R /var/www/
Analyzing and Storing Logs
Linux records important system and application events as logs. These logs help in auditing, monitoring, and troubleshooting.
RHEL uses systemd-journald and rsyslog together:
systemd-journald is a central logging service which collects logs from Kernel, Boot process, Daemon stdout/stderr and Syslog messages. It stores logs in a structured journal, which by default does not persist across reboots.
rsyslog reads syslog messages from
journald. It sorts and writes persistent logs under /var/log. It routes logs to files based on Facility (type of program) and Priority (error level).
Some applications create their own logs directly under /var/log/<appname>/.
Example: Apache writes logs in /var/log/httpd/
| Log File | Purpose |
|---|---|
/var/log/messages | General system messages (except auth, mail, cron). |
/var/log/secure | Authentication & security-related messages. |
/var/log/maillog | Mail server logs. |
/var/log/cron | Scheduled job execution logs. |
/var/log/boot.log | Boot-time console messages. |
Reviewing Syslog Files
Syslog messages have priorities (severity levels) from 0 to 7:
| Code | Priority | Meaning |
|---|---|---|
| 0 | emerg | System is unusable |
| 1 | alert | Immediate action required |
| 2 | crit | Critical condition |
| 3 | err | Error condition |
| 4 | warning | Warning |
| 5 | notice | Significant normal event |
| 6 | info | Informational |
| 7 | debug | Debugging messages |
How rsyslog Handles Logs
rsyslog receives messages from systemd-journald.
Messages are sorted based on:
Facility (auth, cron, mail, etc.)
Priority (emerg–debug)
- Rules stored in:
/etc/rsyslog.conf→ The main rsyslog configuration file that defines core logging rules and global settings./etc/rsyslog.d/*.conf→ A directory for additional module-based or service-specific logging rules. Files here override or extend the main configuration.
- Each rule has:
facility.priority action
Common rsyslog Rules:
#### RULES ####
# Log anything (except mail) with level info or higher.
# Don't log authentication or cron messages here.
*.info;mail.none;authpriv.none;cron.none /var/log/messages
# Authentication logs (restricted access)
authpriv.* /var/log/secure
# Mail server logs
mail.* -/var/log/maillog
# Cron logs
cron.* /var/log/cron
# Emergency messages → all logged-in users
*.emerg :omusrmsg:*
# News/spooler critical messages
uucp,news.crit /var/log/spooler
# Boot messages
local7.* /var/log/boot.logThese rules determine where different system logs are stored.
Log Rotation
logrotate manages rotating and compressing log files to save space.
- Old logs are renamed (e.g.,
/var/log/messages-20240101) - New log file is created
- Oldest logs are removed after a set number of rotations
- Runs daily via a scheduled job
Understanding a Syslog Entry
Example from /var/log/secure:
Feb 11 20:11:48 localhost sshd[1433]: Failed password for student from 172.25.0.10 port 59344 ssh2| Component | Meaning |
|---|---|
Feb 11 20:11:48 | Timestamp |
localhost | Hostname |
sshd[1433] | Program name & PID |
Failed password… | Actual log message |
Monitoring Logs in Real Time
Use tail -f to view new log entries as they appear:
tail -f /var/log/secureUseful for watching ssh logins, failed attempts, cron jobs, etc.
Sending Manual Syslog Messages
Use the logger command to generate log entries (useful for testing):
Example: send a message to /var/log/boot.log
logger -p local7.notice "Log entry created on host"Systemd-journald & Journalctl
systemd-journald
systemd-journald stores logs in structured, indexed binary files called journals.
For syslog messages, extra metadata is stored (facility, priority, etc.).
Default RHEL journal location:
/run/logThis directory is cleared on reboot.
You can configure persistent logging later.
Viewing Logs with journalctl
| Feature | Description | Example Command |
|---|---|---|
| View all logs | Shows entire system journal | journalctl |
| Show last 10 logs | Default tail-like view | journalctl -n |
| Show last N logs | Specify number of lines | journalctl -n 5 |
| Follow logs | Live log streaming (Ctrl+C to stop) | journalctl -f |
| Filter by priority | Show logs at a priority (debug, info, notice, warning, err, crit, alert, emerg) and above | journalctl -p err |
| Logs since today | Show all logs from today’s date | journalctl --since today |
| Since specific date/time | Format: “YYYY-MM-DD HH:MM:SS” | journalctl --since "2019-02-10 20:30:00" |
| Until specific date/time | Limits the end time | journalctl --until "2019-02-13 12:00:00" |
| Relative time | Time expressions like last 1 hour | journalctl --since "-1 hour" |
| Verbose output | Show detailed journal metadata | journalctl -o verbose |
| Filter by command name | _COMM = process name | journalctl _COMM=sshd |
| Filter by executable path | _EXE = full path to binary | journalctl _EXE=/usr/sbin/sshd |
| Filter by PID | _PID = process ID | journalctl _PID=1182 |
| Filter by UID | _UID = user ID | journalctl _UID=0 |
| Filter by systemd unit | _SYSTEMD_UNIT = unit name | journalctl _SYSTEMD_UNIT=sshd.service |
| Combine filters | Use multiple fields for granular search | journalctl _SYSTEMD_UNIT=sshd.service _PID=1182 |
Preserving the System Journal
By default, RHEL stores system journals in /run/log/journal. This directory is volatil, where logs are cleared on reboot.
To make system journal entries persistent, modify the systemd-journald configuration.
Configuring Persistent Journals
Edit /etc/systemd/journald.conf and modify the Storage= parameter:
| Storage Option | Behavior |
|---|---|
| persistent | Stores journals in /var/log/journal (survive reboot). Creates the directory if missing. |
| volatile | Stores journals in /run/log/journal (lost on reboot). |
| auto | Default. Uses persistent storage only if /var/log/journal exists. |
| none | No local journal storage; logs only forwarded. |
- Edit the config:
vim /etc/systemd/journald.conf [Journal] Storage=persistent
Restart the service:
systemctl restart systemd-journald
After restart, persistent journal files appear in:
/var/log/journal/[machine-id]/
Journal Size Limits
- Journals rotate automatically (monthly).
- Journals cannot exceed:
- 10% of filesystem size, or
- Reduce free space below 15%.
- Limits configurable in:
/etc/systemd/journald.conf
- Current limits can be checked with:
journalctl | grep -E 'Runtime|System journal'
Viewing Logs from Specific Boots
Only meaningful if the system has been rebooted multiple times.
Limit journalctl output by boot ID:
| Purpose | Command |
|---|---|
| Show logs from current boot | journalctl -b |
| Show logs from first boot | journalctl -b 1 |
| Show logs from second boot | journalctl -b 2 |
Maintaining Time Zones and Clocks
Accurate and synchronized time is essential for:
Log correlation between systems
Scheduled tasks
Security auditing
Systems typically sync time using NTP (Network Time Protocol) from public NTP servers or internal NTP servers.
NTP Synchronization controls the chronyd service. It can be Enabled and Disabled using the below command:
timedatectl set-ntp truetimedatectl set-ntp false
Checking Time and Time Zones
- The
timedatectlcommand shows an overview of the current time-related system settings, including current time, time zone, and NTP synchronization settings of the system.[user@host ~]$ timedatectl Local time: Mon 2025-03-10 14:25:32 IST Universal time: Mon 2025-03-10 08:55:32 UTC RTC time: Mon 2025-03-10 08:55:29 Time zone: Asia/Kolkata (IST, +0530) System clock synchronized: yes NTP service: active RTC in local TZ: no
- The
timedatectl list-timezonescommand list all the time zones. If usure usetzselectthis shows the correct time zone but does not change system settings.[user@host ~]$ timedatectl list-timezones Africa/Abidjan Africa/Accra Africa/Addis_Ababa Africa/Algiers Africa/Asmara Africa/Bamako ...
Changing Time Zone
- The
timedatectl set-timezone <ZoneName>command is used to set the time zone - Example:
timedatectl set-timezone Africa/Accra
- To set UTC:
timedatectl set-timezone UTC
- The system time can be changed manually using the command
timedatectl set-time "YYYY-MM-DD HH:MM:SS" - Example:
timedatectl set-time 09:00:00
Chronyd
chronyd is the daemon responsible for:
Syncing the system clock with NTP servers
Adjusting hardware clock drift
Maintaining accurate time when offline
NTP servers in chrony is configured using the configuration file /etc/chrony.conf
In
/etc/chrony.conf:server ntppool.example.com iburst
iburstenables fast initial synchronization.Restart chronyd:
systemctl restart chronyd
NTP synchronization can be verified using the command chronyc sources or chronyc sources -v
Archiving and Transferring Files
Compressed TAR Archives
The tar command is used to:
Archive multiple files/directories into a single file.
Compress archives using gzip, bzip2, or xz.
List or extract the contents of archives.
A tar archive contains File data, Metadata, and Index allowing selective extraction
TAR Command Options
Operations and General Options
| Option | Description |
|---|---|
-c, --create | Create a new archive. |
-x, --extract | Extract files from an archive. |
-t, --list | List the contents of an archive. |
-v, --verbose | Show processed files (verbose output). |
-f, --file=<name> | Specify archive file name. |
-p, --preserve-permissions | Preserve permissions when extracting. |
Compression Options
| Option | Description |
|---|---|
-c, --create | Create a new archive. |
-x, --extract | Extract files from an archive. |
-t, --list | List the contents of an archive. |
-v, --verbose | Show processed files (verbose output). |
-f, --file=<name> | Specify archive file name. |
-p, --preserve-permissions | Preserve permissions when extracting. |
Creating, Listing and Extracting TAR Archives
| Command / Pattern | Description |
|---|---|
tar -cf archive.tar file1 dir1 ... | Basic format to create a TAR archive containing multiple files or directories. |
tar -cf /root/etc.tar /etc | Archiving system directories that may require root access (e.g., /etc). |
tar --xattrs -cf archive.tar directory/ | Includes extended attributes such as ACLs and SELinux labels in the archive. |
tar -tf archive.tar | Lists the contents of a TAR archive. |
mkdir /root/etcbackupcd /root/etcbackuptar -xf /root/etc.tar | Extracts a TAR archive into a specific directory. |
tar -xpf archive.tar | Extracts files while preserving original permissions. |
Creating, Listing and Extracting Compressed TAR Archives
| Command / Pattern | Description |
|---|---|
tar -czf /root/etcbackup.tar.gz /etc | Creates a gzip-compressed TAR archive. |
tar -cjf /root/logbackup.tar.bz2 /var/log | Creates a bzip2-compressed TAR archive. |
tar -cJf /root/sshconfig.tar.xz /etc/ssh | Creates an xz-compressed TAR archive. |
tar -tf archive.tar.gz | Lists contents of any compressed TAR archive (.gz, .bz2, .xz). |
mkdir /tmp/etcbackupcd /tmp/etcbackuptar -xzf /root/etcbackup.tar.gz | Extracts a gzip compressed archive. |
mkdir /tmp/logbackupcd /tmp/logbackuptar -xjf /root/logbackup.tar.bz2 | Extracts a bzip2 compressed archive. |
mkdir /tmp/sshbackupcd /tmp/sshbackuptar -xJf /root/sshbackup.tar.xz | Extracts an xz compressed archive. |
Secure File Transfer Between Systems
Secure file transfer in Linux is commonly performed using:
scp (Secure Copy)
sftp (Secure File Transfer Program)
Both use SSH for authentication and Encrypted data transfer
Transferring Files Using scp
Copy files from local to remote
scp /etc/yum.conf /etc/hosts remoteuser@remotehost:/home/remoteuser
Copy file from remote to local
scp remoteuser@remotehost:/etc/hostname /home/user
Copy directories recursively
scp -r root@remotehost:/var/log /tmp
Transferring Files Using sftp
- Start an SFTP session:
sftp remoteuser@remotehost
- You will get a prompt:
sftp>
Common SFTP Commands
| Command | Description |
|---|---|
ls | List remote directory |
cd | Change remote directory |
mkdir | Create remote directory |
pwd | Show remote working directory |
put <file> | Upload file to remote |
get <file> | Download file from remote |
exit | Quit session |
Example: Uploading a File in sftp
Create a directory and upload
/etc/hostssftp> mkdir hostbackup sftp> cd hostbackup sftp> put /etc/hosts
- Example: Downloading a File in sftp
- Download
/etc/yum.conffrom remote to local sftp> get /etc/yum.conf sftp> exit
- Download
Secure File Synchronization Between Systems
rsync is a powerful and efficient tool for synchronizing files and directories between:
Local ↔ Local
Local ↔ Remote
Remote ↔ Local
Advantages:
Transfers only differences between source and destination.
Uses SSH for secure authentication and encrypted transfer.
Faster updates after the first copy.
Important rsync Options
| Option | Meaning / Description |
|---|---|
-n | Dry Run Shows what changes would occur without actually executing them. |
-v | Verbose Output Displays detailed progress. |
-a | Archive Mode Enables many important options (recursive, preserve metadata, etc.). |
-r | Recursive directory copy (part of -a) |
-l | Copy symlinks (part of -a) |
-p | Preserve permissions (part of -a) |
-t | Preserve timestamps (part of -a) |
-g | Preserve group (part of -a) |
-o | Preserve owner (part of -a) |
-D | Preserve device files (part of -a) |
-A | Preserve ACLs |
-X | Preserve SELinux contexts |
-H | Preserve hard links |
Synchronizing Local Directories
rsync -av /var/log /tmpThis creates
/tmp/logcontaining/var/logcontents.Sync contents only (no log directory created)
rsync -av /var/log/ /tmp
Trailing slash (
/) affects behavior:No slash → copy directory itself
Slash → copy only directory contents
Synchronizing with Remote Systems
To preserve file ownership on the destination, run rsync as root on the destination system.
Local → Remote
rsync -av /var/log remotehost:/tmp
Remote → Local
rsync -av remotehost:/var/log /tmp
Installing and Updating Software Packages
Red Hat Subscription Management
Red Hat Subscription Management enables you to:
Register systems to your Red Hat account
Attach software subscriptions
Enable repositories
Track consumed and available entitlements
Tools like yum, dnf, and PackageKit rely on these subscriptions to access Red Hat’s software repositories and updates.
| Task | Description |
|---|---|
| Register system | Associates a machine with a Red Hat account. |
| Attach subscriptions | Grants access to updates & support services. |
| Enable repositories | Allows package installation from supported repos. |
| Review entitlements | Check subscriptions in use or available. |
Registering a System
The subscription-manager utility is used to register a system with Red Hat Customer Portal.
- Register system
subscription-manager register --username=yourusername --password=yourpassword
- List available subscriptions
subscription-manager list --available | less
- Auto-attach the best subscription
subscription-manager attach --auto
- Attach a specific subscription (pool ID)
subscription-manager attach --pool=poolID
- List consumed subscriptions
subscription-manager list --consumed
- Unregister system
subscription-manager unregister
Entitlement Certificates
Once a system is registered, entitlement data is stored in:
| Directory | Purpose |
|---|---|
/etc/pki/product | Certificates for installed Red Hat products. |
/etc/pki/consumer | Identifies the Red Hat account the system is registered to. |
/etc/pki/entitlement | Certificates for attached subscriptions. |
Certificates can be inspected with rct, but subscription-manager is recommended for easier management.
RPM Software Packages
RPM (Red Hat Package Manager) provides:
A standard method for distributing software.
Tracking of installed files.
Metadata for dependencies, licensing, changelogs, scripts, etc.
A local database storing all installed package information.
All Red Hat packages are delivered as RPM files.
An RPM filename uses the format name-version-release.architecture.rpm
Example:
coreutils-8.30-4.el8.x86_64.rpm| Part | Meaning |
|---|---|
| NAME | Software name (coreutils) |
| VERSION | Upstream version (8.30) |
| RELEASE | Build release number (4.el8) |
| ARCH | CPU architecture (x86_64, aarch64, noarch) |
RPM Package Components
Every RPM includes:
Installed files
Metadata (version, summary, dependencies, changelog, license, etc.)
Scripts that run during install/update/removal
RPMs are usually digitally signed (GPG) so package integrity can be verified.
Updating Software with RPM
Installing an update RPM replaces the old version.
Only one version of most packages can be installed at a time.
Kernel packages are an exception (multiple versions allowed).
Examining RPM Packages
The rpm command is used for querying installed packages or package files.
| Command | Purpose | Example |
|---|---|---|
rpm -qa | List all installed packages | rpm -qa |
rpm -q NAME | Show installed version of a package | rpm -q yum |
rpm -qi NAME | Display detailed package information | rpm -qi openssh-server |
rpm -ql NAME | List all files installed by a package | rpm -ql bash |
rpm -qc NAME | List only configuration files | rpm -qc ssh |
rpm -qd NAME | List documentation files | rpm -qd coreutils |
rpm -q --changelog NAME | Show change history of a package | rpm -q --changelog audit |
rpm -q --scripts NAME | Show install/uninstall scripts inside the RPM | rpm -q --scripts httpd |
rpm -qf FILE | Show which installed package owns a file | rpm -qf /etc/yum.repos.d |
rpm -qlp FILE.rpm | List files contained in an RPM file (not installed) | rpm -qlp vim-8.0.rpm |
rpm -ivh package.rpm | Install a local RPM package | rpm -ivh docker.rpm |
rpm2cpio file.rpm | cpio -id | Extract all files from an RPM without installing | rpm2cpio app.rpm | cpio -id |
rpm2cpio file.rpm | cpio -id "<pattern>" | Extract only files matching a pattern from an RPM | rpm2cpio sample.rpm | cpio -id "*.conf" |
Managing Software Packages with Yum
rpm handles individual packages, but does not resolve dependencies or use repositories.
yum provides:
Automatic dependency resolution
Access to multiple repositories
Install/update/remove operations
Search and info features
Transaction history
Finding Software with Yum
| Command / Pattern | Purpose |
|---|---|
yum help | Display help and general usage information for yum. |
yum list | List all installed and available packages. |
yum list 'http*' | List installed/available packages matching a pattern. |
yum search KEYWORD | Search for packages related to a keyword. |
yum search all "web server" | Search package names and descriptions for the phrase “web server”. |
yum info httpd | Show detailed information about the httpd package. |
yum provides /path/file | Find which package provides the specified file or path. |
yum history | Show transaction history |
yum history undo <ID> | Undo a transaction |
Installing & Removing Software
| Action | Command | Example |
|---|---|---|
| Install a package | yum install PACKAGENAME | yum install httpd |
| Update a specific package | yum update PACKAGENAME | yum update nginx |
| Update kernel | yum update kernel | yum update kernel |
| Update all packages | sudo yum update | sudo yum update |
| Remove a package | yum remove PACKAGENAME | yum remove git |
Removing a package may also remove packages depending on it.
Managing Kernels with Yum
Yum installs new kernels alongside existing ones so you can boot back into an older version if needed.
List installed and available kernels
yum list kernel
Check currently running kernel
uname -runame -a
Software Groups
Two types of groups:
Environment groups (e.g., Server with GUI)
Regular groups (e.g., RPM Development Tools)
| Action | Command | Example |
|---|---|---|
| List groups | yum group list | yum group list |
| List hidden groups | yum group list hidden | yum group list hidden |
| Get information about a group | yum group info "GROUPNAME" | yum group info "RPM Development Tools" |
| Install a group | yum group install "GROUPNAME" | yum group install "RPM Development Tools" |
| Mark a group as installed | yum group mark install GROUPNAME | yum group mark install "RPM Development Tools" |
Enabling Yum Software Repositories
Registering a system to Red Hat Subscription Management normally configures access to Red Hat repositories automatically.
Third-party repositories (e.g., EPEL, Adobe) can be added and enabled as needed.
Repo definitions live under:
/etc/yum.repos.d/*.repo
Each repo file can contain one or more repository stanzas (each stanza starts with
[name]).Repo files must end in
.repo.A single
.repofile may contain multiple[repo-id]stanzas (e.g.,epel,epel-debuginfo,epel-source).
| Command Syntax | Explanation | Example |
|---|---|---|
yum repolist all | Shows all enabled/disabled repositories and package counts | yum repolist all |
yum config-manager --enable REPO_NAME | Permanently enables a repository in the system repo config | yum config-manager --enable rhel-8-server-debug-rpms |
yum config-manager --disable REPO_NAME | Permanently disables a repository | yum config-manager --disable rhel-8-server-debug-rpms |
yum --enablerepo=REPO install PACKAGE | Enables a repository for a single Yum command | yum --enablerepo=epel install htop |
yum --disablerepo=REPO update | Disables a repository for a single Yum command | yum --disablerepo=rhel-* update |
rpm --import URL_TO_KEY | Imports the GPG key needed to verify packages | rpm --import http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-8 |
yum install URL_TO_REPO_RPM | Installs repository configuration and keys | yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm |
Create / Add a Third-Party Repo
- Use
yum config-manager --add-repo, This creates a file under /etc/yum.repos.d/yum config-manager --add-repo="https://dl.fedoraproject.org/pub/epel/8/Everything/x86_64/"
- Edit the created
.repofile to setname,baseurl,enabled,gpgcheck, andgpgkey. - Import the RPM GPG key before installing signed packages so yum can verify signatures.
- Use
gpgkey=file:///path/to/keyto point to a locally stored GPG key.
Example repo stanza (EPEL)
[epel] name=Extra Packages for Enterprise Linux $releasever - $basearch baseurl=https://dl.fedoraproject.org/pub/epel/$releasever/Everything/$basearch enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8- To keep a repo defined but not searched by default, set
enabled=0
Managing Package Module Streams
Application Streams allows multiple versions of user-space applications to be installed and also independent lifecycle updates separate from the BaseOS.
Two main repositories:
- BaseOS → Core OS packages; long-term stability
- AppStream → User-space apps via Modules or traditional RPMs
Modules
A module is a set of related RPM packages (app + libraries + docs + tools).
A module contains:
Module Streams
Different versions of the software.
Only one stream can be enabled at a time.
Module Profiles
Predefined package sets for use cases (e.g., common, minimal, server, development).
Installing a module installs a profile.
Default profile applies if none is specified.
| Command Syntax | Explanation | Example |
|---|---|---|
yum module list | Shows all available modules and streams | yum module list |
yum module list MODULE | Shows available streams for a module | yum module list perl |
yum module info MODULE | Displays stream, profiles, summary, and packages | yum module info perl |
yum module info --profile MODULE:STREAM | Shows packages included in a specific profile | yum module info --profile perl:5.24 |
yum module enable MODULE:STREAM | Enables a specific module stream | sudo yum module enable perl:5.26 |
yum module install MODULE | Installs module and its profile packages | sudo yum module install perl |
yum install @MODULE | Equivalent method to install module groups | sudo yum install @perl |
yum module list MODULE | Shows whether the module is enabled or installed | yum module list perl |
yum module remove MODULE | Removes installed profile packages of the module | sudo yum module remove perl |
yum module disable MODULE | Disables a module so it cannot be installed | sudo yum module disable perl |
yum module info MODULE | Used to identify packages in the current stream | yum module info postgresql | grep module+el8 |
yum module remove MODULE | Required before resetting or switching module streams | sudo yum module remove postgresql |
yum module reset MODULE | Clears enabled or disabled state of the module | sudo yum module reset postgresql |
yum module install MODULE:STREAM | Enables and installs a new module stream | sudo yum module install postgresql:10 |
yum distro-sync | Aligns installed packages with the selected stream | sudo yum distro-sync |
yum remove PACKAGE | Removes leftover packages from old streams | sudo yum remove <package> |
Managing Networking
Linux networking is managed using tools that configure IP addresses, network interfaces, routing, DNS, and services. Commands like ip, ifconfig, nmcli, and systemctl help control how the system connects to other networks and the internet.
Gather network interface information
ip link showcommand list all interfaces and their MAC addresses. Useful to map names (ens3, ens4) to physical/virtual ports.[user@host ~]$ ip link show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 link/ether 52:54:00:00:00:0a brd ff:ff:ff:ff:ff:ff 3: ens4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 link/ether 52:54:00:00:00:1e brd ff:ff:ff:ff:ff:ff
Display IP addresses
ip addr show <interface>command shows configured IPv4/IPv6 addresses, prefix lengths, and whether addresses are link/global scope.[user@host ~]$ ip addr show ens3 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 link/ether 52:54:00:00:00:0b brd ff:ff:ff:ff:ff:ff inet 192.0.2.2/24 brd 192.0.2.255 scope global ens3 inet6 2001:db8:0:1:5054:ff:fe00:b/64 scope global inet6 fe80::5054:ff:fe00:b/64 scope link
Display interface performance statistics
ip -s link show <interface>command prints packet/byte counts and error/dropped counters useful for diagnosing link issues.- Look for non-zero
errorsordroppedcounters to detect physical or driver problems. [user@host ~]$ ip -s link show ens3 RX: bytes packets errors dropped overrun mcast 269850 2931 0 0 0 0 TX: bytes packets errors dropped carrier collsns 300556 3250 0 0 0 0
Check connectivity between hosts
ping verifies reachability and measures round-trip time; specify interface scope for IPv6 link-local addresses.
ping -c <count> <destination>command tests latency and packet loss for IPv4[user@host ~]$ ping -c 3 192.0.2.254 3 packets transmitted, 3 received, 0% packet loss rtt min/avg/max/mdev = 3.485/4.885/6.837/1.424 ms
ping6 -c <count> <destination>command tests latency and packet loss for IPv6[user@host ~]$ ping6 -c 3 2001:db8:0:1::1 PING 2001:db8:0:1::1(2001:db8:0:1::1) 56 data bytes 64 bytes from 2001:db8:0:1::1: icmp_seq=1 ttl=64 time=18.4 ms 64 bytes from 2001:db8:0:1::1: icmp_seq=2 ttl=64 time=0.178 ms 64 bytes from 2001:db8:0:1::1: icmp_seq=3 ttl=64 time=0.180 ms --- 2001:db8:0:1::1 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2001ms rtt min/avg/max/mdev = 0.178/6.272/18.458/8.616 ms
When pinging link-local addresses, append the interface scope:
fe80::...%ens3[user@host ~]$ ping6 -c 1 fe80::f482:dbff:fe25:6a9f%ens4 PING fe80::f482:dbff:fe25:6a9f%ens4(fe80::f482:dbff:fe25:6a9f) 56 data bytes 64 bytes from fe80::f482:dbff:fe25:6a9f: icmp_seq=1 ttl=64 time=22.9 ms --- fe80::f482:dbff:fe25:6a9f%ens4 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 22.903/22.903/22.903/0.000 ms
Inspect routing table
Routing table entries map destination prefixes to interfaces and next-hop routers; longest-prefix match is used.
- Show IPv4 routes
[user@host ~]$ ip route default via 192.0.2.254 dev ens3 proto static metric 1024 192.0.2.0/24 dev ens3 proto kernel scope link src 192.0.2.2 10.0.0.0/8 dev ens4 proto kernel scope link src 10.0.0.11
- Show IPv6 routes
[user@host ~]$ ip -6 route 2001:db8:0:1::/64 dev ens3 proto kernel metric 256 fe80::/64 dev ens3 proto kernel metric 256 default via 2001:db8:0:1::ffff dev ens3 proto static metric 1024
Trace path taken by packets
tracepath/traceroute reveal each hop and latency so you can identify where latency or packet loss occurs.
Trace IPv4 path
[user@host ~]$ tracepath access.redhat.com ...output omitted... 4: 71-32-28-145.rcmt.qwest.net 48.853ms asymm 5 5: dcp-brdr-04.inet.qwest.net 100.732ms asymm 7 6: 206.111.0.153.ptr.us.xo.net 96.245ms asymm 7 7: 207.88.14.162.ptr.us.xo.net 85.270ms asymm 8 8: ae1d0.cir1.atlanta6-ga.us.xo.net 64.160ms asymm 7 9: 216.156.108.98.ptr.us.xo.net 108.652ms 10: bu-ether13.atlngamq46w-bcr00.tbone.rr.com 107.286ms asymm 12 ...output omitted...
Trace IPv6 path
[user@host ~]$ tracepath6 2001:db8:0:2::451 1?: [LOCALHOST] 0.091ms pmtu 1500 1: 2001:db8:0:1::ba 0.214ms 2: 2001:db8:0:1::1 0.512ms 3: 2001:db8:0:2::451 0.559ms reached
Troubleshoot ports and services
ssornetstatshows which services are listening, on which interfaces/ports, and any established connections.[user@host ~]$ ss -ta State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:sunrpc *:* LISTEN 0 128 *:ssh *:* LISTEN 0 100 127.0.0.1:smtp *:* LISTEN 0 128 *:36889 *:* ESTAB 0 0 172.25.250.10:ssh 172.25.254.254:59392 LISTEN 0 128 :::sunrpc :::* LISTEN 0 128 :::ssh :::* LISTEN 0 100 ::1:smtp :::* LISTEN 0 128 :::34946 :::*The SSH port is listening on all IPv4 addresses, shown by the
*wildcard for IPv4.The SMTP service is listening only on the 127.0.0.1 IPv4 loopback address.
The ESTABLISHED SSH connection shows the server using
172.25.250.10and the client coming from172.25.254.254.For IPv6, the SSH service is listening on all IPv6 interfaces, represented by
::.The SMTP IPv6 listener is bound to the IPv6 loopback interface
::1.
| Option | Meaning (one line) |
|---|---|
-n | Show numeric addresses/ports (no name resolution). |
-t | Show TCP sockets. |
-u | Show UDP sockets. |
-l | Show only listening sockets. |
-a | Show all sockets (listening + established). |
-p | Show process using the socket (requires privileges). |
-A inet | Show IPv4/IPv6 network sockets (exclude UNIX). |
- Combine these to narrow output (e.g., ss -tunlp shows numeric TCP/UDP listening sockets and their processes).
Configuring Networking from the Command Line
NetworkManager is the service responsible for managing network interfaces on RHEL/CentOS systems. It:
Monitors network devices
Activates and manages connections
Applies IP/DNS settings
Writes configuration files to:
/etc/sysconfig/network-scripts/ifcfg-*>/re>
Tools like nmcli, nmtui, and the GNOME network settings dialog all communicate with NetworkManager.
The nmcli tool allows you to configure and control network settings directly from the command line.
A device is a physical or virtual network interface (e.g.,
ens3,wlp2s0).
- Connection is a set of network settings applied to a device. A device can have multiple connections, but only one active at a time.
- Connection ID is a human-readable name used to identify a connection.
Viewing Network Information
- The
nmcli dev statuscommand displays the status of all network devices:[user@host ~]$ nmcli dev status DEVICE TYPE STATE CONNECTION eno1 ethernet connected eno1 ens3 ethernet connected static-ens3 eno2 ethernet disconnected -- lo loopback unmanaged --
- The
nmcli con showcommand displays a list of all connections. To list only the active connections, add the--activeoption.[user@host ~]$ nmcli con show NAME UUID TYPE DEVICE eno2 ff9f7d69-db83-4fed-9f32-939f8b5f81cd 802-3-ethernet -- static-ens3 72ca57a2-f780-40da-b146-99f71c431e2b 802-3-ethernet ens3 eno1 87b53c56-1f5d-4a29-a869-8a7bdaf56dfa 802-3-ethernet eno1 [user@host ~]$ nmcli con show --active NAME UUID TYPE DEVICE static-ens3 72ca57a2-f780-40da-b146-99f71c431e2b 802-3-ethernet ens3 eno1 87b53c56-1f5d-4a29-a869-8a7bdaf56dfa 802-3-ethernet eno1
Adding a New Connection
The nmcli con add command is used to add new network connections.
- Connection with auto IPv4 with DHCP and auto IPv6
nmcli con add con-name eno2 type ethernet ifname eno2
- The eno2 is the interface name (connection name) and it create
/etc/sysconfig/network-scripts/ifcfg-eno2file which contains all the details of this particular interface/connection. - Connection with static IPv4 address with it’s gateway and subnet
nmcli con add con-name eno2 type ethernet ifname eno2 \ ipv4.address 192.168.0.5/24 ipv4.gateway 192.168.0.254
- Connection with static IPv4 and IPv6 address with it’s gateway and subnet
nmcli con add con-name eno2 type ethernet ifname eno2 \ ipv6.address 2001:db8:0:1::c000:207/64 ipv6.gateway 2001:db8:0:1::1 \ ipv4.address 192.0.2.7/24 ipv4.gateway 192.0.2.1
Activating & Deactivating Connections
Activate (bring up) a connection using the command
nmcli con up <connection-name>nmcli con up static-ens3
Deactivate a device using the command
nmcli dev dis <connection-name>it’s Best method to bring down an interface.nmcli dev dis ens3
Viewing Connection Settings
- The details of a particular connection can be seen by command
nmcli <connection-name> - It shows Static properties (editable) and Active properties (from DHCP/SLAAC)
[user@host ~]$ nmcli con show static-ens3 connection.id: static-ens3 802-3-ethernet.mac-address: CA:9D:E9:2A:CE:F0 ipv4.method: manual ipv4.addresses: 192.168.0.2/24 ipv4.gateway: 192.168.0.254 ipv6.method: manual ipv6.addresses: 2001:db8:0:1::7/64
Modifying Connection Settings
The nmcli con mod command is used to modify the existing connections
- Changing IPv4 settings
nmcli con mod static-ens3 ipv4.address 192.0.2.2/24 \ ipv4.gateway 192.0.2.254
- Changing IPv6 settings
nmcli con mod static-ens3 ipv6.address 2001:db8:0:1::a00:1/64 \ ipv6.gateway 2001:db8:0:1::1
If switching from DHCP to static config:
ipv4.method auto → manual ipv6.method auto → manual
Reload Connection Files
This will be useful if you edited /etc/sysconfig/network-scripts/ifcfg-* manually
nmcli con reload
Delete a Connection
The nmcli con del command deletes the particular connection and also it’s config file
nmcli con del static-ens3
Who Can Modify Network Settings?
root: Full control
Local logged-in users (physical console): Allowed to manage connections
SSH users: Must become root
- To check current permissions:
nmcli gen permissions
| Command | Purpose |
|---|---|
nmcli dev status | Show device status |
nmcli con show | List all connections |
nmcli con show <name> | View settings for a connection |
nmcli con add ... | Create a new connection |
nmcli con mod ... | Modify a connection |
nmcli con up <name> | Activate a connection |
nmcli dev dis <dev> | Disable a device |
nmcli con del <name> | Delete a connection |
nmcli con reload | Reload configuration files |
Editing Network Configuration Files
Network configuration on RHEL-based systems can be modified in two ways:
- Using
nmclicommands - Editing the connection files in
/etc/sysconfig/network-scripts/
NetworkManager loads these files automatically. If you edit them manually, run nmcli con reload to apply changes.
Connection Configuration Files (ifcfg Files)
When you modify a connection using nmcli con mod <name> ... NetworkManager saves these settings to /etc/sysconfig/network-scripts/ifcfg-<name>
These files use older ifcfg syntax, so some settings differ from the nmcli version.
Below is a mapping of nmcli settings → ifcfg directives → effect.
| nmcli Setting | ifcfg Directive | Meaning / Effect |
|---|---|---|
ipv4.method manual | BOOTPROTO=none | Use static IPv4 configuration. |
ipv4.method auto | BOOTPROTO=dhcp | Get IPv4 settings via DHCP. |
ipv4.addresses 192.0.2.1/24 | IPADDR=192.0.2.1PREFIX=24 | Sets static IPv4 address. Multiple addresses use IPADDR1, PREFIX1, etc. |
ipv4.gateway 192.0.2.254 | GATEWAY=192.0.2.254 | Sets default IPv4 gateway. |
ipv4.dns 8.8.8.8 | DNS1=8.8.8.8 | Adds DNS server. |
ipv4.dns-search example.com | DOMAIN=example.com | Adds DNS search domain. |
ipv4.ignore-auto-dns true | PEERDNS=no | Ignore DNS info from DHCP. |
ipv6.method manual | IPV6_AUTOCONF=no | Static IPv6 configuration. |
ipv6.method auto | IPV6_AUTOCONF=yes | Enable IPv6 SLAAC. |
ipv6.method dhcp | IPV6_AUTOCONF=no + DHCPV6C=yes | Use DHCPv6 only. |
ipv6.addresses 2001:db8::a/64 | IPV6ADDR=2001:db8::a/64 | Sets static IPv6 address. Additional addresses use IPV6ADDR_SECONDARIES. |
ipv6.gateway 2001:db8::1 | IPV6_DEFAULTGW=2001:db8::1 | Sets IPv6 default gateway. |
connection.autoconnect yes | ONBOOT=yes | Bring connection up at boot. |
connection.id ens3 | NAME=ens3 | Name of the connection. |
connection.interface-name ens3 | DEVICE=ens3 | Binds connection to device. |
802-3-ethernet.mac-address | HWADDR=xx:xx:xx... | Bind connection to a MAC address. |
Editing ifcfg Files Manually
Connection config files live here
/etc/sysconfig/network-scripts/ifcfg-<name>
Example: Static IPv4 Configuration
BOOTPROTO=none
IPADDR0=172.25.250.10
PREFIX0=24
GATEWAY0=172.25.250.254
DEFROUTE=yes
DNS1=172.25.254.254
DEVICE=ens3
NAME="static-ens3"
ONBOOT=yes
Example: DHCP IPv4 Configuration
BOOTPROTO=dhcp
DEVICE=ens3
NAME="dhcp-ens3"
ONBOOT=yes
After modifying an ifcfg file, for the changes to reflect follow the below steps:
nmcli con reload nmcli con down "static-ens3" nmcli con up "static-ens3"
This reloads the config and restarts the connection.
Configuring Host Names and DNS Resolution
The hostname command is used to check the current hostname
The permanent hostname is stored in the /etc/hostname file. The hostnamectl set-hostname command is used to set the permanent hostname and it can be verified using hostnamectl status command.
[root@host ~]# hostname
host.example.com
[root@host ~]# hostnamectl set-hostname host.example.com
[root@host ~]# hostnamectl status
Static hostname: host.example.com
Icon name: computer-vm
Chassis: vm
Machine ID: f874df04639f474cb0a9881041f4f7d4
Boot ID: 6a0abe03ef0b4a97bcb8afb7b281e4d3
Virtualization: kvm
Operating System: Red Hat Enterprise Linux 8.2 (Ootpa)
CPE OS Name: cpe:/o:redhat:enterprise_linux:8.2:GA
Kernel: Linux 4.18.0-193.el8.x86_64
Architecture: x86-64
[root@host ~]# cat /etc/hostname
host.example.comLocal Name Resolution
The system first checks /etc/hosts for hostname → IP mappings.
[root@host ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.254 classroom.example.com
172.25.254.254 content.example.com
The getent hosts <hostname> command can be used to test host name resolution using the /etc/hosts file.
DNS Resolution
If a hostname is not found in /etc/hosts, the system queries DNS servers defined in /etc/resolv.conf
[root@host ~]# cat /etc/resolv.conf # Generated by NetworkManager domain example.com search example.com nameserver 172.25.254.254- search: a list of domain names to try with a short host name.
- nameserver: the IP address of a nameserver to query.
NetworkManager overwrites this file using connection settings.
Setting DNS Servers with nmcli
| Command | Purpose |
|---|---|
nmcli con mod <ID> ipv4.dns "8.8.8.8 1.1.1.1" | Replace existing DNS servers with new ones. |
nmcli con mod <ID> +ipv4.dns 8.8.4.4 | Add/append an additional IPv4 DNS server. |
nmcli con mod static-ens3 +ipv6.dns 2001:4860:4860::8888 | Add an IPv6 DNS server. |
nmcli con down <ID> | Bring the connection down (apply changes). |
nmcli con up <ID> | Bring the connection back up. |
nmcli con mod "static-ens3" ipv4.ignore-auto-dns yes | Prevent DHCP from replacing DNS settings. |
Testing DNS Name Resolution
The host <hostname> command can be used to test DNS server connectivity.
[root@host ~]# host sample.example.com
sample.example.com has address 172.25.254.254
[root@host ~]# host 172.25.254.254
254.254.25.172.in-addr.arpa domain name pointer sample.example.com.
Scheduling Future Tasks
Scheduling Deferred User Jobs
Deferred jobs are commands scheduled to run once at a specific point in the future. These jobs persist after a reboot
They’re useful for tasks like:
Running a script later
Resetting firewall rules after 10 minutes
Sending a reminder or email
Running maintenance tasks at a specific time
Linux provides the at utility for this purpose. The atd daemon runs jobs in the background.
Scheduling a Task with at
The basic syntax for this command is at TIMESPEC After running the command type the commands you want executed and press Ctrl + D to save & exit
- Examples of valid TIMESPEC formats
now + 5 min noon + 4 days 5pm august 3 2026 teatime tomorrow # 16:00 02:30pm 15:45
Example:
echo "echo 'Backup done'" | at now + 5 minat now + 10 min < myscript.sh
Viewing Scheduled Jobs
Use atq (or at -l) to list pending jobs for the current user.
[user@host ~]$ atq 28 Mon Feb 2 05:13:00 2025 a user 29 Mon Feb 3 16:00:00 2025 a user 27 Tue Feb 4 12:00:00 2025 a user- Fields:
Job ID (e.g., 28)
Scheduled date & time
Queue (default =
a)User who owns the job
Regular users see only their jobs. root can see & manage all jobs.
To view the job contents run the command at -c <Job-ID>This shows the environment + the exact commands scheduled.
To remove the scheduled job run the command atrm <Job-ID>
Scheduling Recurring User Jobs
Recurring tasks in Linux are handled by the crond daemon (from the cronie package).
Users schedule repeated jobs using their personal crontab file, managed via the crontab command.
When a cron job runs, any output (STDOUT/STDERR) is sent to the user via local email unless redirected.
Managing User Cron Jobs
| Command | Description |
|---|---|
crontab -l | List current user’s cron jobs |
crontab -r | Remove all cron jobs for current user |
crontab -e | Edit cron jobs (opens editor) |
crontab filename | Replace user’s crontab with jobs from a file |
Root can manage another user’s cron jobs using the command crontab -u username -e
Crontab File Format
A crontab entry contains six fields MIN HOUR DOM MON DOW COMMAND
- Field Meanings:
| Field | Meaning |
|---|---|
| MIN | 0–59 |
| HOUR | 0–23 |
| DOM (Day of Month) | 1–31 |
| MON | 1–12 or Jan–Dec |
| DOW (Day of Week) | 0–7 (0/7 = Sunday) or Mon–Sun |
| COMMAND | Command or script to run |
Syntax Options:
| Syntax | Meaning |
|---|---|
* | Every value |
5 | Specific minute/hour/day |
1-5 | Range |
1,3,7 | List of specific values |
*/10 | Every 10 units |
If Day of Month (DOM) and Day of Week (DOW) are both non-*, the job runs when either field matches.
Examples of Recurring Jobs
- Run a backup every year on Feb 2 at 9:00 AM
0 9 2 2 * /usr/local/bin/yearly_backup
- Run every 5 minutes from 9 AM to 5 PM on Fridays in July
*/5 9-16 * Jul 5 echo "Chime"
- Run a report at 23:58 on weekdays
58 23 * * 1-5 /usr/local/bin/daily_report
- Send an email every weekday at 9 AM
0 9 * * 1-5 mutt -s "Checking in" boss@example.com % Hi there boss, just checking in.
Scheduling Recurring System Jobs
System administrators often schedule jobs that must run repeatedly at the system level.
Instead of using user crontabs, system jobs should be placed in system-wide cron files.
System Cron Locations
Recurring system jobs are defined in:
1. /etc/crontab (System Crontab)
- This file allows scheduling tasks with an additional field specifying the user.
- Syntax:
# ┌──────── minute (0 - 59) # │ ┌────── hour (0 - 23) # │ │ ┌──── day of month (1 - 31) # │ │ │ ┌── month (1 - 12) # │ │ │ │ ┌ day of week (0 - 7) # │ │ │ │ │ # * * * * * user command
2. /etc/cron.d/ (Recommended for custom system jobs)
Create your own cron files here.
Safer than modifying
/etc/crontab, which may be overwritten during updates.Packages also place their cron definitions here.
Cron Script Directories
These directories contain executable scripts run at fixed intervals:
| Directory | Runs |
|---|---|
/etc/cron.hourly/ | Every hour |
/etc/cron.daily/ | Daily |
/etc/cron.weekly/ | Weekly |
/etc/cron.monthly/ | Monthly |
Any script which is added here should be executable, make them executable using chmod +x <filename> command
Understanding /etc/anacrontab
anacron (now integrated into crond) ensures important jobs run even if the system was powered off during their scheduled time.
Purpose
- Guarantee daily/weekly/monthly jobs run at least once in the required interval.
- Track last run time via files in
/var/spool/anacron/.
Entry Format (4 Fields)
period delay job-id command| Field | Meaning |
|---|---|
| period | Days between runs (1, 7, 30, or @daily, @weekly, etc.) |
| delay | Minutes to wait before starting |
| job-id | Identifier logged in system logs |
| command | Script or command to run |
systemd Timer Units
It’s a modern alternative to cron, systemd timers provide a newer, more flexible way to run scheduled jobs.
A timer unit triggers a corresponding service unit.
Example timer:
/usr/lib/systemd/system/sysstat-collect.timer[Unit] Description=Run system activity accounting tool every 10 minutes [Timer] OnCalendar=*:00/10 [Install] WantedBy=sysstat.serviceThis runs every 10 minutes.
OnCalendar=2024-03-* 12:35,37,39:16Runs at 12:35:16, 12:37:16, and 12:39:16 every day in March 2024.
OnUnitActiveSec=15min- Trigger 15 minutes after last activation.
Never edit files in /usr/lib/systemd/system/. Instead, copy the unit into /etc/systemd/system/ then edit the copy
After modifying a timer:
systemctl daemon-reload systemctl enable --now mytimer.timer
Managing Temporary Files
Modern Linux systems create and store many temporary files under:
/tmp→ general temporary files/run→ volatile runtime files (stored in RAM, cleared on reboot)
To automate the creation, cleanup, and permission management of temporary files, Red Hat Enterprise Linux (RHEL) uses systemd-tmpfiles, this creates required temp directories at boot, cleans up old temporary files, and ensures correct permissions/ownership.
It reads configuration from:
/usr/lib/tmpfiles.d/*.conf← Vendor defaults/run/tmpfiles.d/*.conf← Runtime (volatile)/etc/tmpfiles.d/*.conf← Admin/custom settings (highest priority)
Systemd manages temporary files at boot, the service systemd-tmpfiles-setup.service runs systemd-tmpfiles --create --remove this creates directories/files specified in tmpfiles configuration, fixes permissions, deletes items marked for removal.
Automatic Cleaning with systemd Timer
Temporary file cleanup happens using this timer systemd-tmpfiles-clean.timer
View the timer configuration:
[user@host ~]$ systemctl cat systemd-tmpfiles-clean.timer [Unit] Description=Daily Cleanup of Temporary Directories [Timer] OnBootSec=15min OnUnitActiveSec=1d
OnBootSec=15min → run cleanup 15 minutes after boot
OnUnitActiveSec=1d → run cleanup every 1 day afterwards
If changes in done in the frequency reload systemctl daemon-reload and enable systemctl enable --now systemd-tmpfiles-clean.timer
Cleaning Temporary Files Manually
To run cleanup immediately, run the command systemd-tmpfiles --clean. This deletes files older than the configured Age parameter in tmpfiles configuration files.
The basic syntax of systemd-tmpfiles consists of seven columns: Type, Path, Mode, UID, GID, Age, and Argument.
| Type | Meaning | Example Entry |
|---|---|---|
d | Create directory if missing | d /run/systemd/seats 0755 root root - |
D | Create directory and remove existing contents | D /home/student 0700 student student 1d |
L | Create symbolic link | L /run/fstablink - root root - /etc/fstab |
Z | Reset SELinux permissions recursively | Z /some/path - - - (example placeholder) |
Configuration File Precedence
When files share the same name:
- /etc/tmpfiles.d/ → highest priority (admin overrides)
- /run/tmpfiles.d/ → runtime/volatile
- /usr/lib/tmpfiles.d/ → vendor defaults (lowest priority)
This ensures:
- Your custom changes in
/etc/tmpfiles.d/override system defaults safely. - Package updates do not overwrite admin-managed temp file rules.
Accessing Linux File Systems
Linux presents all data through a single unified file-system hierarchy. Even though your system may have multiple disks, partitions, or logical volumes, they all appear as directories under /.
To make a storage device accessible, its file system must be mounted on a directory called a mount point. Once mounted, the directory shows the contents of that file system.
Block Devices and Naming
Low-level access to storage in Linux is provided through block device files, found under /dev.
These device files are created automatically by the OS and represent entire disks or partitioned sections of disks.
| Storage Type | Example Names | Description |
|---|---|---|
| SATA/SAS/SCSI/USB Drives | /dev/sda, /dev/sdb | Each letter represents a whole disk. |
| virtio-blk Disks | /dev/vda, /dev/vdb | Used in VMs (paravirtualized storage). |
| NVMe SSDs | /dev/nvme0, /dev/nvme1 | High-speed NVMe devices. |
| SD/MMC/eMMC | /dev/mmcblk0, /dev/mmcblk1 | SD card / embedded storage. |
Partitions
Partitions slice a disk into smaller usable sections.
Typical partition naming:
SATA/SAS/SCSI/USB:
/dev/sda1,/dev/sda2
NVMe:
/dev/nvme0p1,/dev/nvme0p2
Example block device listing:
[root@host ~]$ ls -l /dev/sda1 brw-rw----. 1 root disk 8, 1 Feb 22 08:00 /dev/sda1bindicates a block device.
Logical Volumes (LVM)
LVM allows multiple physical devices to be grouped into a Volume Group (VG). Within a VG, storage is allocated to Logical Volumes (LVs).
Physical Volumes → Volume Group → Logical Volumes
LV device naming:
/dev/<VG_name>/<LV_name>- Example:
/dev/myvg/mylv
These are actually symbolic links pointing to the underlying device-mapper entries.
Viewing File Systems and Disk Usage
Use
dfto view mounted filesystems and available space:[root@host ~]$ df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 892M 0 892M 0% /dev tmpfs 915M 0 915M 0% /dev/shm tmpfs 915M 17M 899M 2% /run tmpfs 915M 0 915M 0% /sys/fs/cgroup /dev/vda3 8.0G 1.4G 6.7G 17% / /dev/vda1 1014M 166M 849M 17% /boot tmpfs 183M 0 183M 0% /run/user/1000
- Use
duto check usage per directory[root@host ~]# du -h /var/log ...output omitted... 176K /usr/share/smartmontools 184K /usr/share/nano 8.0K /usr/share/cmake/bash-completion 8.0K /usr/share/cmake 369M /usr/share
Flags:
-h= human-readable (GiB, MiB)-H= SI units (GB, MB)
Mounting and Unmounting File Systems
In Linux, a file system must be mounted before you can access its contents. Likewise, you must unmount it safely before removing the device.
Identifying Block Devices
Use lsblk to list available disks and partitions:
[root@host ~]$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT vda 253:0 0 12G 0 disk ├─vda1 253:1 0 1G 0 part /boot ├─vda2 253:2 0 1G 0 part [SWAP] └─vda3 253:3 0 11G 0 part / vdb 253:16 0 64G 0 disk └─vdb1 253:17 0 64G 0 part
If a new 64 GB storage device was just attached, /dev/vdb1 is likely the target partition.
Mounting a File System Manually
Mount a filesystem onto a directory using the command mount <device> <mountpoint>
The mount point must already exist. Files previously in that directory become hidden until unmounted.
Example:
mkdir -p /mnt/data mount /dev/vdb1 /mnt/data
Mounting Using UUID
This is the most reliable method, device names like /dev/vdb1 may change depending on detection order, but UUIDs remain constant.
List UUIDs:
[root@host ~]$ lsblk -fp NAME FSTYPE LABEL UUID MOUNTPOINT /dev/vda ├─/dev/vda1 xfs 23ea8803-a396-494a-8e95-1538a53b821c /boot ├─/dev/vda2 swap cdf61ded-534c-4bd6-b458-cab18b1a72ea [SWAP] └─/dev/vda3 xfs 44330f15-2f9d-4745-ae2e-20844f22762d / /dev/vdb └─/dev/vdb1 xfs 46f543fd-78c9-4526-a857-244811be2d88
Mount using UUID:
[root@host ~]# mount UUID="46f543fd-78c9-4526-a857-244811be2d88" /mnt/data
Unmounting File Systems
To unmount safely use the command umount <mountpoint>
[root@host ~]# umount /mnt/data
Always unmount before disconnecting/removing the storage device.
Unmounting fails if processes are using the filesystem.
[root@host data]# umount /mnt/data umount: /mnt/data: target is busy.
Identify the process which is using the filesystem using the lsof command.
[root@host ~]$ lsof /mnt/data COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 1593 root cwd DIR 253,17 6 128 /mnt/data lsof 2532 root cwd DIR 253,17 19 128 /mnt/data lsof 2533 root cwd DIR 253,17 19 128 /mnt/data- To fix:
Close the file(s)
Move out of the directory (
cd ~)Stop or kill the process if necessary
After fix it unmount again.
[root@host data]# umount /mnt/data
Locating Files on a Linux System
Searching for files is a common task for system administrators. Linux provides two primary tools:
locate– very fast; searches a prebuilt index (not real-time).find– searches in real time; accurate but slower.
Finding Files Using locate
locate searches a database of filenames and paths. This database is updated daily, but you can update manually updatedb
locate only shows files in directories the user has permission to access.
| Command | Explanation |
|---|---|
locate passwd | Search for files whose name contains passwd |
locate -i messages | Case-insensitive search for filenames containing messages |
locate -n 5 snow.png | Limit results to the first 5 matches |
Searching Files in Real Time with find
find walks the filesystem directly, making it accurate and flexible.
| Command | Explanation |
|---|---|
find / -name sshd_config | Search for a file by exact name |
find / -name '*.txt' | Search using wildcard patterns (must quote patterns) |
find /etc -iname '*pass*' | Case-insensitive name search |
find /home/user -user user | Files owned by a specific user |
find /home/user -group user | Files owned by a specific group |
find /home/user -uid 1000 | Files owned by UID 1000 |
find /home/user -gid 1000 | Files owned by GID 1000 |
find / -user root -group mail | Files owned by root AND group mail |
find /home -perm 764 | Files with exact permission 764 |
find /home -perm -324 | User has ≥ wx, group ≥ w, others ≥ r |
find /home -perm /444 | Anyone has at least read access |
find -perm -002 | Others have write access |
find -size 10M | Files exactly 10 MB |
find -size +10G | Files larger than 10 GB |
find -size -10k | Files smaller than 10 KB |
find / -mmin 120 | Modified exactly 120 minutes ago |
find / -mmin +200 | Modified more than 200 minutes ago |
find / -mmin -150 | Modified less than 150 minutes ago |
find /etc -type d | All directories under /etc |
find / -type l | All symbolic links |
find /dev -type b | All block devices under /dev |
find / -type f -links +1 | Files with more than one hard link |
Managing Basic Storage
Disk partitioning divides a physical disk into logical sections. Common reasons:
Limit space for users or applications
Separate OS files from user data
Provide space for swap
Improve performance for tools like backups and images
Partitioning Schemes
MBR (Master Boot Record):
- Old standard (BIOS systems) and Uses 32-bit addressing
Max disk size 2 TiB and Supports 4 primary partitions (or 3 primary + extended + logicals).
GPT (GUID Partition Table)
Modern standard (UEFI systems) and Provides corruption checking (CRC)
Supports 128 partitions and Max disk size 8 ZiB
Stores primary + backup partition table
Managing Partitions with parted
parted supports both MBR and GPT disks.
View Partition Table
[root@host ~]# parted /dev/vda print Model: Virtio Block Device (virtblk) Disk /dev/vda: 53.7 GB Sector size (logical/physical): 512B/512B Partition Table: msdos Disk Flags: Number Start End Size Type File system Flags 1 1049kB 10.7GB 10.7GB primary xfs boot 2 10.7GB 53.7GB 42.9GB primary xfs- If you do not provide a subcommand
print, parted opens an interactive session for issuing commands and there you can enter the subcommand and to exit from it, typequitand press enter.
Writing a Disk Label
Before creating partitions on a new disk we need to create a disk label.
- MBR Label
[root@host ~]# parted /dev/vdb mklabel msdos
- GPT Label
[root@host ~]# parted /dev/vdb mklabel gpt
mklabel erases existing partition tables along with the existing data
Creating MBR Partitions
- Specify the disk device to create the partition on
- Use the mkpart subcommand to create a new primary or extended partition
- Choose the file-system type, such as xfs or ext4. To get the list of the supported file-system type, use the command
parted /dev/vdb help mkpart - Specify the sector on the disk that the new partition starts on. You can also use the MiB, GiB, TiB, MB, GB,
or TB suffixes. the default suffix is MB - Specify the disk sector where the new partition should end.
- Size = End – Start
- Exit parted
- Run udevadm settle This command waits for the system to detect the new partition and to create the associated device file under the /dev directory. It only returns when it is done.
[root@host ~]# parted /dev/vdb (parted) mkpart Partition type? primary/extended? primary File system type? [ext2]? xfs Start? 2048s End? 1000MB (parted) quit Information: You may need to update /etc/fstab. [root@host ~]# udevadm settle [root@host ~]#- This partition can be created using a one line command
[root@host ~]# parted /dev/vdb mkpart primary xfs 2048s 1000MB
Creating GPT Partitions
- Specify the disk device to create the partition on
- Use the mkpart subcommand to create a new partition, With the GPT scheme, each partition is given a name.
- Choose the file-system type, such as xfs or ext4. To get the list of the supported file-system type, use the command
parted /dev/vdb help mkpart - Specify the sector on the disk that the new partition starts on. You can also use the MiB, GiB, TiB, MB, GB,
or TB suffixes. the default suffix is MB - Specify the disk sector where the new partition should end.
- Size = End – Start
- Exit parted
- Run udevadm settle This command waits for the system to detect the new partition and to create the associated device file under the /dev directory. It only returns when it is done.
[root@host ~]# parted /dev/vdb (parted) mkpart Partition name? []? usersdata File system type? [ext2]? xfs Start? 2048s End? 1000MB (parted) quit Information: You may need to update /etc/fstab. [root@host ~]# udevadm settle [root@host ~]#- This partition can be created using a one line command
[root@host ~]# parted /dev/vdb mkpart usersdata xfs 2048s 1000MB
Deleting Partitions (MBR or GPT)
- Specify the disk that contains the partition to be removed.
- Identify the partition number of the partition to delete.
- Delete the partition.
- Exit parted
[root@host ~]# parted /dev/vdb (parted) print Model: Virtio Block Device (virtblk) Disk /dev/vdb: 5369MB Sector size (logical/physical): 512B/512B Partition Table: gpt Disk Flags: Number Start End Size File system Name Flags 1 1049kB 1000MB 999MB xfs usersdata (parted) rm 1 (parted) quit Information: You may need to update /etc/fstab. [root@host ~]#
Creating File Systems
After creating a partition, we need to format it by adding a file system to it. The two most commonly used file systems are xfs and ext4.
As root, use the mkfs.xfs command to apply an XFS file system to a block device. For ext4, use mkfs.ext4.
[root@host ~]# mkfs.xfs /dev/vdb1[root@host ~]# mkfs.ext4 /dev/vdb1
Mounting File Systems
After you have added the file system, the last step is to mount the file system to a directory in the directory structure.
Temporary Mount
- The
mount <device> <mountpoint>command is used to temporarily mount the file system, this will not be present after restart.[root@host ~]# mount /dev/vdb1 /mnt
- The mounted file systems can be viewed using the same mount command
[root@host ~]# mount | grep vdb1 /dev/vdb1 on /mnt type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
Persistent Mounts
To make sure that the system automatically mounts the file system at system boot, add an entry to the /etc/fstab file. This file systems persists even after the reboot.
- Get UUID
[root@host ~]# lsblk --fs vdb1 xfs 7a20315d-ed8b-4e75-a5b6-24ff9e1f9838
- Edit
/etc/fstaband add the below entryUUID=7a20315d-ed8b-4e75-a5b6-24ff9e1f9838 /data xfs defaults 0 0
- Reload ssytemd, when you add or remove an entry in the
/etc/fstab[root@host ~]# systemctl daemon-reload
- Test and validate fstab entry, If no error, then system is safe to reboot.
[root@host ~]# mount -a[root@host ~]# findmnt --verify
- Understanding
/etc/fstabFields
| Field | Description |
|---|---|
| 1. Device | UUID recommended (stable) |
| 2. Mount point | Directory must exist/swap |
| 3. Filesystem type | xfs/ext4/swap/others |
| 4. Options | defaults for common settings |
| 5. Dump | Usually 0 |
| 6. fsck order | 1 for root ext4, 2 for others, 0 for XFS |
Managing Swap Space
Swap space acts as an overflow area for RAM. When memory pressure increases:
Inactive pages are moved from RAM → swap
Active pages are accessed from swap → RAM
Swap is much slower than RAM.
It is only a fallback, not a replacement for insufficient memory.
Swap Space Sizing
| RAM Size | Recommended Swap | If Using Hibernation |
|---|---|---|
| ≤ 2 GiB | 2 × RAM | 3 × RAM |
| 2–8 GiB | Same as RAM | 2 × RAM |
| 8–64 GiB | At least 4 GiB | 1.5 × RAM |
| > 64 GiB | At least 4 GiB | Hibernation not recommended |
Steps to Create Swap Space
Use parted to create a partition and set its type to
linux-swap.[root@host ~]# parted /dev/vdb (parted) print Model: Virtio Block Device (virtblk) Disk /dev/vdb: 5369MB Sector size (logical/physical): 512B/512B Partition Table: gpt Disk Flags: Number Start End Size File system Name Flags 1 1049kB 1001MB 1000MB data (parted) mkpart Partition name? []? swap1 File system type? [ext2]? linux-swap Start? 1001MB End? 1257MB (parted) print Model: Virtio Block Device (virtblk) Disk /dev/vdb: 5369MB Sector size (logical/physical): 512B/512B Partition Table: gpt Disk Flags: Number Start End Size File system Name Flags 1 1049kB 1001MB 1000MB data 2 1001MB 1257MB 256MB linux-swap(v1) swap1 (parted) quit Information: You may need to update /etc/fsta [root@host ~]# udevadm settle- Format the partition using the mkswap command applies a swap signature to the device.
[root@host ~]# mkswap /dev/vdb2 Setting up swapspace version 1, size = 244 MiB (255848448 bytes) no label, UUID=39e2667a-9458-42fe-9665-c5c854605881
Activating and Deactivating Swap Space
- The swapon command with the device as a parameter is used to activate a particular swap space, or use
swapon -ato activate all the swap spaces listed in the/etc/fstabfile. Use theswapon --showandfreecommands to inspect the available swap spaces. [root@host ~]# free total used free shared buff/cache available Mem: 1873036 134688 1536436 16748 201912 1576044 Swap: 0 0 0 [root@host ~]# swapon /dev/vdb2 [root@host ~]# free total used free shared buff/cache available Mem: 1873036 135044 1536040 16748 201952 1575680 Swap: 249852 0 249852- The swapoff command with the device as a parameter is used to deactivate a particular swap space.
[root@host ~]# swapoff /dev/vdb2
Activating Swap Space Persistently
- To activate a swap space at every boot, place an entry in the /etc/fstab file.
- Entry to add in /etc/fstab:
UUID=39e2667a-9458-42fe-9665-c5c854605881 swap swap defaults 0 0
- After adding reload the systemd
[root@host ~]# systemctl daemon-reload
Setting Swap Priority
Linux chooses which swap space to use first based on priority.
Default priority: −2
Higher value = higher priority
Example /etc/fstab with priorities:
UUID=af30cbb0-3866-466a-825a-58889a49ef33 swap swap defaults 0 0 UUID=39e2667a-9458-42fe-9665-c5c854605881 swap swap pri=4 0 0 UUID=fbd7fa60-b781-44a8-961b-37ac3ef572bf swap swap pri=10 0 0
Use swapon –show to display the swap space priorities.
Managing Logical Volumes
LVM easily resize filesystems, migrate extents to new disks, and manage flexible storage pools.
Physical devices: disks, partitions, RAID or SAN devices used by LVM.
Physical volumes (PVs): devices initialized for LVM (
pvcreate).Volume groups (VGs): pools of PV storage (
vgcreate).Logical volumes (LVs): block devices carved from VGs (
lvcreate) and used like normal partitions.
Creating a Logical Volume
Prepare the physical device.
Use a whole disk (e.g.
/dev/sdb) or create a partition and set its type to Linux LVM (MBR type0x8e) withparted,gdisk, orfdisk.Register partitions if required:
[root@host ~]# parted -s /dev/vdb mkpart primary 1MiB 769MiB [root@host ~]# parted -s /dev/vdb set 1 lvm on [root@host ~]# partprobe
Create a physical volume (PVs)
pvcreateprepares the device(s) for use by LVM. It label the devices as LVM physical volumes.[root@host ~]# pvcreate /dev/vdb2 /dev/vdb1- This labels the devices /dev/vdb2 and /dev/vdb1 as PVs, ready for allocation into a volume group.
Create a Volume Group (VG)
vgcreatemakes a pooled storage area (VG) for LVs. It creates a VG from one or more PVs[root@host ~]# vgcreate vg01 /dev/vdb2 /dev/vdb1- This creates a VG called vg01 that is the combined size, in PE units, of the two PVs /dev/vdb2 and /dev/vdb1.
Create a Logical Volume (LV)
lvcreatemakes a block device (/dev/vg01/lv01) you can format. It creates an LV from free space in a VG.[root@host ~]# lvcreate -n lv01 -L 700M vg01- This creates an LV called lv01, 700 MiB in size, in the VG vg01.
-nsets LV name,-Lsets size (e.g.,700M).Use
-lto specify size in extents instead of bytes.
Format and Mount the LV
- Use mkfs to create an XFS file system on the new logical volume.
[root@host ~]# mkfs -t xfs /dev/vg01/lv01
- Create a mount point directory
[root@host ~]# mkdir /mnt/data
- Add an entry to /etc/fstab
/dev/vg01/lv01 /mnt/data xfs defaults 1 2
- Run mount command to mount the file system added in /etc/fstab
[root@host ~]# mount /mnt/data
Removing a Logical Volume
- Removing a logical volume destroys any data stored on the logical volume. Back up or move your data before you remove the logical volume.
- Unmount and edit /etc/fstab to deleted the LV entry
[root@host ~]# umount /mnt/data
- Remove LV:
[root@host ~]# lvremove /dev/vg01/lv01
- Remove VG:
[root@host ~]# vgremove vg01
- Remove PV(s):
[root@host ~]# pvremove /dev/vdb2 /dev/vdb1
Inspecting LVM Status
- The pvdisplay command displays information about physical volumes. To list information about all physical volumes, use the command without arguments. To list information about a specific physical volume, pass that device name to the command.
[root@host ~]# pvdisplay /dev/vdb1 --- Physical volume --- PV Name /dev/vdb1 VG Name vg01 PV Size 768.00 MiB / not usable 4.00 MiB Allocatable yes PE Size 4.00 MiB Total PE 191 Free PE 16 Allocated PE 175 PV UUID JWzDpn-LG3e-n2oi-9Etd-VT2H-PMem-1ZXwP1- The vgdisplay command displays information about volume groups. To list information about all volume groups, use the command without arguments. To list information about a specific volume group, pass that VG name to the command.
[root@host ~]# vgdisplay vg01 --- Volume group --- VG Name vg01 System ID Format lvm2 Metadata Areas 2 Metadata Sequence No 2 VG Access read/write VG Status resizable MAX LV 0 Cur LV 1 Open LV 1 Max PV 0 Cur PV 2 Act PV 2 VG Size 1016.00 MiB PE Size 4.00 MiB Total PE 254 Alloc PE / Size 175 / 700.00 MiB Free PE / Size 79 / 316.00 MiB VG UUID 3snNw3-CF71-CcYG-Llk1-p6EY-rHEv-xfUSez- The lvdisplay command displays information about logical volumes. If you provide no argument to the command, it displays information about all LVs; if you provide an LV device name as an argument, the command displays information about that specific device.
[root@host ~]# lvdisplay /dev/vg01/lv01 --- Logical volume --- LV Path /dev/vg01/lv01 LV Name lv01 VG Name vg01 LV UUID 5IyRea-W8Zw-xLHk-3h2a-IuVN-YaeZ-i3IRrN LV Write Access read/write LV Creation host, time host.lab.example.com, 2019-03-28 17:17:47 -0400 LV Status available # open 1 LV Size 700 MiB Current LE 175 Segments 1 Allocation inherit Read ahead sectors auto - current set to 256 Block device 252:0- Use these commands to check allocation, sizes, and free extents.
Extending Logical Volumes and File Systems
You can add more disk space to a volume group by adding additional physical volumes or You can remove unused physical volumes from a volume group.
Extending a Volume Group
- Prepare the physical device and create the physical volume.
[root@host ~]# parted -s /dev/vdb mkpart primary 1027MiB 1539MiB[root@host ~]# parted -s /dev/vdb set 3 lvm on[root@host ~]# pvcreate /dev/vdb3- A PV only needs to be created if there are no PVs free to extend the VG.
vgextend increases the VG pool by the PV size. This extends the vg01 VG by the size of the /dev/vdb3 PV.
[root@host ~]# vgextend vg01 /dev/vdb3
- Use vgdisplay to confirm the additional physical extents are available. Free PE / Size shows how much space is now available to allocate to LVs.
[root@host ~]# vgdisplay vg01 --- Volume group --- VG Name vg01 ...output omitted... Free PE / Size 178 / 712.00 MiB ...output omitted...
Reducing a Volume Group
Before removing a PV from a VG, back up data stored on all logical volumes in the volume group.
pvmove migrates extents from the source physical volume to other physical volume in the VG.
[root@host ~]# pvmove /dev/vdb3
- vgreduce removes a physical volume from volume group
[root@host ~]# vgreduce vg01 /dev/vdb3
- pvremove can be used to permanently stop using the device as a PV
Extending a Logical Volume
- vgdisplay verifies that there are sufficient physical extents available.
- lvextend extends the logical volume to a new size.
[root@host ~]# lvextend -L +300M /dev/vg01/lv01
| Command | Result / Meaning |
|---|---|
lvextend -l 128 <LV> | Resize the logical volume to exactly 128 extents. |
lvextend -l +128 <LV> | Add 128 extents to the current size of the logical volume. |
lvextend -L 128M <LV> | Resize the logical volume to exactly 128 MiB. |
lvextend -L +128M <LV> | Add 128 MiB to the current size of the logical volume. |
lvextend -l +50%FREE <LV> | Add 50% of the current free space in the VG to the logical volume. |
Extend XFS file system
- Use
xfs_growfs mountpointto expand the file system to occupy the extended LV. - XFS must be mounted while growing.
[root@host ~]# xfs_growfs /mnt/data
- Verify the new size of the mounted file system.
[root@host ~]# df -h /mountpoint
- Use
lvextend -rto automatically resize filesystem after LV extension (works for many filesystems) - XFS cannot be reduced: you cannot shrink an XFS filesystem or its LV while data remains.
Extend ext4 file system
After lvextend run resize2fs. Itoperates on the block device (LV), not the mount point. Use
-pto show progress.[root@host ~]# resize2fs /dev/vg01/lv01
Extend a logical volume and swap space
Ensure enough RAM or other swap before swapoff to avoid OOM
- Use vgdisplay vgname to verify that a sufficient number of free physical extents are available.
[root@host ~]$ vgdisplay vg01
- Use swapoff -v /dev/vgname/lvname to deactivate the swap space on the logical volume.
[root@host ~]$ swapoff -v /dev/vg01/lv_swap
- lvextend -l +extents /dev/vgname/lvname extends the logical volume /dev/vgname/lvname by the extents value.
[root@host ~]$ lvextend -L +1G /dev/vg01/lv_swap
- mkswap /dev/vgname/lvname formats the entire logical volume as swap space.
[root@host ~]$ mkswap /dev/vg01/lv_swap
- Use swapon -va /dev/vgname/lvname to activate the swap space on the logical volume.
[root@host ~]$ swapon -va
- Verify the changes
[root@host ~]$ free -h[root@host ~]$ swapon --show
Implementing Advanced Storage Features
Managing Layered Storage with Stratis
- Stratis is a modern Linux storage-management solution that simplifies pooling block devices, creating flexible thin-provisioned filesystems, snapshots & layered storage.
-
Stratis is built on top of standard Linux technologies. It uses the Device Mapper framework (similar to how LVM works internally) and formats all its filesystems using XFS.
-
It also supports thin provisioning, meaning a filesystem can appear very large (such as 1 TiB) while consuming only the real physical storage it actually needs.
- You can create multiple pools, up to 224 filesystems per pool.
Installing and Enabling Stratis
- To manage file systems with the Stratis storage management solution, install the stratis-cli and stratisd packages.
-
[root@host ~]# yum install stratis-cli stratisd
-
- Activate the stratisd service using the systemctl command.
-
[root@host ~]# systemctl enable --now stratisd
-
Creating and Managing Stratis Pools
- Create pools of one or more block devices using the stratis pool create command.
-
[root@host ~]# stratis pool create pool1 /dev/vdb
-
- Each pool is a subdirectory under the /stratis directory.
- Use the
stratis pool listcommand to view the list of available pools.-
[root@host ~]# stratis pool list Name Total Physical Size Total Physical Used pool1 5 GiB 52 MiB</pre
-
-
Monitor Total Physical Used,this is real allocated storage.
- Use the stratis pool add-data command to add additional block devices to a pool.
-
[root@host ~]# stratis pool add-data pool1 /dev/vdc
-
- Use the stratis blockdev list command to view the block devices of a pool.
-
[root@host ~]# stratis blockdev list pool1 Pool Name Device Node Physical Size State Tier pool1 /dev/vdb 5 GiB In-use Data pool1 /dev/vdc 5 GiB In-use Data
-
Managing Stratis Filesystems
- Use the stratis filesystem create command to create a file system from a pool.
-
[root@host ~]# stratis filesystem create pool1 fs1
-
- The links to the Stratis file systems are in the /stratis/pool1 directory.
- Use the stratis filesystem list command to view the list of available file systems.
-
[root@host ~]# stratis filesystem list Pool Name Name Used Created Device UUID pool1 fs1 546 MiB Sep 23 2020 13:11 /stratis/pool1/fs1 31b9363badd...
-
dfwill always show 1 TiB size for Stratis filesystems (thin provisioning). Always check real space withstratis pool list- You can create a snapshot of a Stratis-managed file system with the
stratis filesystem snapshotcommand.-
[root@host ~]# stratis filesystem snapshot pool1 fs1 snapshot1
-
Persistently Mounting Stratis Filesystems
- The below command displays the UUID of the file system.
-
[root@host ~]# lsblk --output=UUID /stratis/pool1/fs1 UUID 31b9363b-add8-4b46-a4bf-c199cd478c55
-
- Add the entry in the
/etc/fstabfile to persistently mount a Stratis file system.-
UUID=31b9363b-add8-4b46-a4bf-c199cd478c55 /dir1 xfs defaults,x-systemd.requires=stratisd.service 0 0
-
x-systemd.requires=stratisd.serviceensures Stratis is started before mounting. Without it, system boot may fail.
Compressing and Deduplicating Storage with VDO
- VDO (Virtual Data Optimizer) is a Linux device-mapper layer designed to reduce storage usage by automatically eliminating zero-filled blocks, removing duplicate blocks through UDS-based deduplication, and compressing data using the LZ4 compression engine provided by the
kvdomodule. - You place VDO on top of a block device (disk/RAID/LVM/etc.) and then put Filesystems, LVM, Encryption on top of the VDO volume, similar to a normal disk.
-
This reduces physical disk footprint while increasing space efficiency.
VDO processes data in 3 stages:
- Zero-Block Elimination
-
Removes all-zero blocks and stores them only in metadata and Enables thin provisioning.
-
-
Deduplication (UDS Module)
-
Detects repeated blocks and Stores 1 original block, references it everywhere else.
-
- Compression (kvdo Module)
-
Compresses data using LZ4 and Works in 4 KB block groups.
-
VDO Volumes
- A VDO volume behaves like a disk, Format it with any filesystem (XFS/ext4/etc.), Mount it normally, and Can also be used as an LVM PV.
- VDO supports thin provisioning, so logical size can be larger than physical size (e.g., 50G logical on a 10G disk). Monitor usage carefully.
Implementing VDO
- Install the vdo and kmod-kvdo packages to enable VDO in the system.
-
[root@host ~]# yum install vdo kmod-kvdo
-
- Create the VDO volume by running the vdo create command.
-
[root@host ~]# vdo create --name=vdo1 --device=/dev/vdd --vdoLogicalSize=50G
-
-
-
--device→ underlying physical block device -
--vdoLogicalSize→ logical (visible) size for users
-
- If logical size is omitted → VDO uses same size as physical device (best performance).
- After creation, format it and mount it.
-
[root@host ~]# mkfs.xfs /dev/mapper/vdo1 [root@host ~]# mkdir /mnt/vdo1 [root@host ~]# mount /dev/mapper/vdo1 /mnt/vdo1
-
- To analyze a VDO volume, run the vdo status command. Use the –name= option to specify the name of a particular volume.
[root@host ~]# vdo status [root@host ~]# vdo status --name=vdo1
- The vdo list command displays the list of VDO volumes that are currently started.
[root@host ~]$ vdo list
- The VDO volume can be stopped and started using the vdo start and vdo stop commands.
[root@host ~]$ vdo start --name=vdo1 [root@host ~]$ vdo stop --name=vdo1
- The physical usage is monitored using the vdostats command, especially when thin provisioning is used.
[root@host ~]$ vdostats --verbose
Accessing Network-Attached Storage
Mounting Network-Attached Storage with NFS
NFS (Network File System) allows Linux/UNIX systems to share directories over the network and mount them just like local filesystems.
Mounting NFS Shares
Identify Available NFS Shares. The NFS server admin may provide export details, or you can browse the server’s NFS root.
[user@host ~]$ sudo mkdir mountpoint [user@host ~]$ sudo mount serverb:/ mountpoint [user@host ~]$ sudo ls mountpoint
Create a mount point directory on the client system.
[user@host ~]$ mkdir -p mountpoint
- Temporarily mounting the NFS share using the mount command. This mount does not survive reboot, it’s only ideal for testing.
[user@host ~]$ sudo mount -t nfs -o rw,sync serverb:/share mountpoint
-t nfs→ filesystem typerw,sync→ read-write, synchronous writes- Persistently mounting the NFS share by adding an entry in the
/etc/fstaband mount the NFS share.[user@host ~]$ sudo vim /etc/fstab ... serverb:/share /mountpoint nfs rw,soft 0 0[user@host ~]$ sudo mount /mountpoint
Unmounting an NFS Share
- As root (or using sudo), unmount an NFS share using the umount command.
[user@host ~]$ sudo umount mountpoint
- Unmounting does not remove the
/etc/fstabentry. If it remains, the NFS share will auto-mount again on the next boot.
Automounting Network-Attached Storage
The automounter (autofs) automatically mounts NFS shares on-demand and unmounts them when not in use. This reduces network load and removes the need for users to run mount or umount.
Benefits of Using the Automounter
No root privileges required by users.
Shares mount only when accessed → saves system & network resources.
Client-side configuration only; no changes needed on NFS server.
Supports direct and indirect maps, including wildcards.
Works with any filesystem supported by manual
mount.Automatically creates/removes mount directories for indirect maps.
Create an automount
- Install the autofs package
[user@host ~]$ sudo yum install autofs
Create a Master Map File. It live in
/etc/auto.master.d/and must end with.autofs.[user@host ~]$ sudo vim /etc/auto.master.d/demo.autofs
- Add the master map entry in the
.autofsfile/shares /etc/auto.demo/shares→ base directory for indirect automounts/etc/auto.demo→ mapping file (must be created next)
Create the Mapping File. It define mount points, options, and NFS source paths.
[user@host ~]$ sudo vim /etc/auto.demo work -rw,sync serverb:/shares/work
Components:
Key (mount point):
work→ becomes/shares/workOptions:
-rw,syncSource:
serverb:/shares/work
The directory
/shares/workis automatically created and removed by autofs.- Use
systemctlto start and enable theautofsservice.[user@host ~]$ sudo systemctl enable --now autofs
Direct Maps
- Used when mounting an NFS share to an existing absolute path.
- To use directly mapped mount points, the master map file might appear as follows:
/- /etc/auto.direct
- The content for the /etc/auto.direct file might appear as follows:
/mnt/docs -rw,sync serverb:/shares/docs/mnt/docsis the exact mount point (absolute path)./mntmust already exist.
Indirect Wildcard Maps
Use when the NFS server exports many subdirectories with the same options.
Example
/etc/auto.demoentry:* -rw,sync serverb:/shares/&
*→ wildcard key (matches any directory name)&→ replaced by the actual key accessed
Controlling the Boot Process
Selecting the Boot Target
A modern RHEL system boots through the following major stages:
System firmware (UEFI/BIOS) runs POST and initializes hardware.
Firmware loads the bootloader (GRUB2) from the configured boot device.
GRUB2 reads
/boot/grub2/grub.cfgand lets you choose a kernel.GRUB2 loads the kernel + initramfs into memory.
The kernel initializes hardware and runs /sbin/init → systemd.
systemd runs units from initrd.target, then mounts the real root filesystem.
Root switches from initramfs to real system.
systemd loads the default target (graphical or multi-user) and starts required services.
Rebooting & Shutting Down
- To power off or reboot a running system from the command line
systemctl powerofforpoweroffstops all running services, unmounts all file systems, and then powers down the system.systemctl rebootorrebootstops all running services, unmounts all file systems, and then reboots the system.
Understanding systemd Targets
A target defines the system state (what services should run).
| Target | Purpose |
|---|---|
| graphical.target | Multi-user + graphical login |
| multi-user.target | Text-based multi-user mode |
| rescue.target | Minimal mode with root shell (sulogin) |
| emergency.target | Very minimal, root filesystem read-only |
- To list all the taegets
[user@host ~]$ systemctl list-units --type=target --all
- To view the dependencies
[user@host ~]$ systemctl list-dependencies graphical.target
Selecting a Target at Runtime
- On a running system, administrators can switch to a different target using the systemctl isolate command. It stops all the services not required by the target and starts the services which ware required.
[root@host ~]# systemctl isolate multi-user.target
Only targets with
AllowIsolate=yescan be isolated. It can be checked by viewing the target.[user@host ~]$ systemctl cat graphical.target
Setting a Default Target
- When the system starts, systemd activates the default.target target. The
get-defaultandset-defaultsubcommands are used to view and set the default target.[root@host ~]# systemctl get-default multi-user.target [root@host ~]# systemctl set-default graphical.target Removed /etc/systemd/system/default.target. Created symlink /etc/systemd/system/default.target -> /usr/lib/systemd/system/graphical.target. [root@host ~]# systemctl get-default graphical.target
Selecting a Different Target at Boot Time
Useful for troubleshooting or single-use changes. This affects only that boot.
- Steps:
- Boot or reboot the system.
- Interrupt the boot loader menu countdown by pressing any key (except Enter which would initiate a normal boot).
- Move the cursor to the kernel entry that you want to start.
- Press e to edit the current entry.
- Move the cursor to the line that starts with linux. This is the kernel command line.
- Append systemd.unit=target.target. For example,
systemd.unit=emergency.target - Press Ctrl+x to boot with these changes.
Resetting the Root Password
If the root password is lost and you cannot log in, you can reset it from the boot loader without using external media. RHEL allows dropping into an initramfs debug shell using rd.break.
Resetting Root Password Using rd.break
The system will stop before switching to the real root filesystem and provide a root shell.
- Reboot the system.
- Interrupt the boot loader countdown by pressing any key, except Enter.
- Move the cursor to the kernel entry to boot.
- Press e to edit the selected entry.
- Move the cursor to the kernel command line (the line that starts with linux).
- Append
rd.break - Press Ctrl+x to boot with the changes.
Remount and Change Root Password
The real root filesystem is available at /sysroot but mounted read-only, so:
- Remount /sysroot as read/write.
switch_root:/# mount -o remount,rw /sysroot
- Switch into a chroot jail, where /sysroot is treated as the root of the file-system tree.
switch_root:/# chroot /sysroot
- Set a new root password.
sh-4.4# passwd root
- Make sure that all unlabeled files, including /etc/shadow at this point, get relabeled during boot.
sh-4.4# touch /.autorelabel
- Type exit twice. The first command exits the chroot jail, and the second command exits the initramfs debug shell.
The system will reboot, relabel all files for SELinux, and then apply the new password.
Inspecting Logs
- By default, RHEL stores logs in
/run/log/journal(cleared on reboot). - To persist logs across reboots, edit the journald.conf, make the stoage to persistent and restart the journald service.
[root@host ~]# vim /etc/systemd/journald.conf ...output omitted... [Journal] Storage=persistent ...output omitted... [root@host ~]# systemctl restart systemd-journald.service
View logs from the previous boot:
[root@host ~]# journalctl -b -1 -p err
Debugging Boot Problems
Enable Early Debug Shell
- By enabling the debug-shell service with
systemctl enable debug-shell.service, the system spawns a root shell on TTY9 (Ctrl+Alt+F9) early during the boot sequence. - Disable it after use, or anyone with console access can get root.
- By enabling the debug-shell service with
Emergency & Rescue Targets
- By appending either
systemd.unit=rescue.targetorsystemd.unit=emergency.targetto the kernel command line from the boot loader, the system spawns into a rescue or emergency shell instead of starting normally. - Both requires the root password.
Emergency target → minimal environment, rootfs is read-only.
Rescue target → basic services running, logs available.
To remount root read-write execute
mount -o remount,rw /
- By appending either
Checking Stuck systemd Jobs
If the system freezes during boot execute
systemctl list-jobsIt shows running or waiting jobs that may block the boot sequence.
Repairing File System Issues at Boot
Problems in /etc/fstab or corrupted file systems can prevent a Linux system from booting. When this happens, systemd usually drops the system into an emergency shell, where the root password is required to perform repairs.
| Problem | What Happens During Boot |
|---|---|
| Corrupted filesystem | systemd attempts automatic repair; if it fails, you get an emergency shell. |
Wrong device/UUID in /etc/fstab | systemd waits for the device; on timeout, drops to emergency shell. |
| Mount point does not exist | systemd drops to emergency shell immediately. |
| Invalid mount option | systemd drops to emergency shell. |
nofail Option
- Adding
nofailin/etc/fstaballows boot to continue even if the filesystem fails to mount. - Do NOT use
nofailfor critical filesystems, applications may start without their required storage, leading to data loss.
Managing Network Security
Managing Server Firewalls
Linux firewalls rely on netfilter, a kernel framework that allows packet filtering, NAT, and routing decisions.
RHEL enhances netfilter through nftables, providing:
Faster packet processing
Faster rule updates
Unified IPv4/IPv6 handling
A single management tool: nft
Older tools like iptables, ip6tables, arptables, and ebtables are deprecated.
What is firewalld?
firewalld is a dynamic firewall manager that uses nft under the hood. It organizes network traffic using zones, each representing a trust level. Each zone defines which services, ports, and protocols are allowed.
Firewalld selects a zone based on:
Source IP
Incoming network interface
Default zone (if no other mapping applies)
The default zone is initially public.
Predefined Zones
| Zone | Default Behavior |
|---|---|
| trusted | Allow all traffic |
| home / internal | Allow common client services (ssh, mdns, samba-client, ipp-client, dhcpv6-client) |
| work | Allow ssh, ipp-client, dhcpv6-client |
| public (default) | Allow ssh, dhcpv6-client only |
| external | Masquerading enabled for IPv4; allow ssh |
| dmz | Allow ssh |
| block | Reject all incoming traffic |
| drop | Drop all incoming traffic silently |
Predefined Services
Firewalld includes built-in service definitions for common protocols.
| Service | Ports |
|---|---|
| ssh | 22/tcp |
| dhcpv6-client | 546/udp |
| ipp-client | 631/udp |
| samba-client | 137/udp, 138/udp |
| mdns | 5353/udp |
To list all the service execute the command firewall-cmd --get-services
Managing Firewalld from the Command Line
The firewall-cmd command interacts with the firewalld dynamic firewall manager.
| Command | Explanation |
|---|---|
firewall-cmd --get-default-zone | Query the current default zone. |
firewall-cmd --set-default-zone=ZONE | Set the default zone for both runtime and permanent configuration. |
firewall-cmd --get-zones | List all available zones. |
firewall-cmd --get-active-zones | List all zones currently in use (interfaces or sources assigned). |
firewall-cmd --add-source=CIDR [--zone=ZONE] | Route all traffic from a specific IP/network into the specified zone. If no zone is provided, the default zone is used. |
firewall-cmd --remove-source=CIDR [--zone=ZONE] | Remove a source-based zone assignment. |
firewall-cmd --add-interface=INTERFACE [--zone=ZONE] | Assign a network interface to a zone. If no zone is provided, the default zone is used. |
firewall-cmd --change-interface=INTERFACE [--zone=ZONE] | Move an interface to a different zone. If no zone is provided, the default zone is used. |
firewall-cmd --list-all [--zone=ZONE] | Show all settings for a zone (interfaces, sources, services, ports). If no zone is provided, the default zone is used. |
firewall-cmd --list-all-zones | Retrieve configuration details for every zone. |
firewall-cmd --add-service=SERVICE [--zone=ZONE] | Allow traffic for a specific service. If no zone is provided, the default zone is used. |
firewall-cmd --remove-service=SERVICE [--zone=ZONE] | Remove an allowed service from a zone. If no zone is provided, the default zone is used. |
firewall-cmd --add-port=PORT/PROTOCOL [--zone=ZONE] | Allow traffic for a specific port/protocol. If no zone is provided, the default zone is used. |
firewall-cmd --remove-port=PORT/PROTOCOL [--zone=ZONE] | Remove an allowed port/protocol from a zone. If no zone is provided, the default zone is used. |
firewall-cmd --reload | Reload firewalld and apply permanent configuration. |
Example:
These commands set the default firewalld zone to dmz, route all traffic from the 192.168.0.0/24 network to the internal zone, and enable the MySQL service within that zone.
[root@host ~]# firewall-cmd --set-default-zone=dmz [root@host ~]# firewall-cmd --permanent --zone=internal \ --add-source=192.168.0.0/24 [root@host ~]# firewall-cmd --permanent --zone=internal --add-service=mysql [root@host ~]# firewall-cmd --reload
Managing SELinux Port Labeling
SELinux not only controls files and processes, it also enforces rules on network ports.
Every port has an SELinux type label, and services are allowed to bind only to ports with permitted labels.
Example SELinux port labels:
ssh_port_t → ports 22/tcp
http_port_t → ports 80, 443, 8008, etc.
If a service tries to use a non-standard port, SELinux may block it unless you update the port label.
Viewing SELinux Port Labels
- To get an overview of all the current port label assignments, run the
semanage port -lcommand. - The output will consist of 3 columns. port_label_t, tcp|udp, and comma separated list of ports
[root@host ~]# semanage port -l ...output omitted... http_cache_port_t tcp 8080, 8118, 8123, 10001-10010 http_cache_port_t udp 3130 http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000 ...output omitted...
- To refine the search, use the grep command
Adding a New SELinux Port Label
- If you want a service to listen on a custom port, add the port to an existing SELinux type.
- To add a port to an existing port label (type), use the following syntax.
semanage port -a -t port_label -p tcp|udp PORTNUMBERThe -a adds a new port label, the -t denotes the type, the -p denotes the protocol. - Example:
[root@host~]# semanage port -a -t gopher_port_t -p tcp 71
- To list only custom changes:
[root@host~]# semanage port -l -C SELinux Port Type Proto Port Number gopher_port_t tcp 71
Removing a Port Label
- The syntax for removing a custom port label is the same as the syntax for adding a port label, but instead of using the -a option (for Add), use the -d option (for Delete).
- Example, to remove the binding of port 71/TCP to gopher_port_t:
[root@host ~]# semanage port -d -t gopher_port_t -p tcp 71
Modifying an Existing Port Label
More efficient than deleting and re-adding.
Example: Change port 71/tcp from gopher_port_t → http_port_t
[root@server ~]# semanage port -m -t http_port_t -p tcp 71
- view the modification using the
semanage port -l -ccommand
Running Containers
Introducing Containers
Containers offer a lightweight, isolated way to package and deploy applications along with their required libraries and dependencies. They eliminate conflicts caused by differing software versions on a host system and make applications portable across environments.
Why Containers?
Traditional deployments install applications and required libraries directly on the OS, causing issues such as:
Conflicting software versions
Applications depending on outdated or incompatible libraries
Difficulty moving applications between environments
A container solves this by packaging the application with its runtime environment, isolating it from the host and other applications.
Containers vs Virtual Machines
Virtual Machines
Run multiple full operating systems on one physical system
Require a hypervisor
Each VM contains its own OS → heavier, slower to boot
Containers
Share the host OS kernel
Only package the application + dependencies
Lightweight, fast to start, low resource usage
Provide process, network, and filesystem isolation
Not all applications fit well in containers, especially those requiring very low-level hardware access.
How Containers Work
RHEL uses core Linux technologies:
cgroups → control and limit CPU, memory, I/O
namespaces → isolate process IDs, networking, mounts
SELinux & Seccomp → strong security boundaries
Container Images
Containers run from container images, which are:
Immutable templates bundling the application + dependencies
Built using standards such as OCI (Open Container Initiative)
Portable across compatible hosts
Images typically include Application code, Runtime (Python, Java, Node.js, etc.), Required libraries, Configuration files, and Metadata
Container Architecture Best Practices
Instead of placing all components in One large container with web server + database + messaging, keep Separate containers for each component
This improves maintainability, scalability, and security.
Tools for Managing Containers in RHEL
RHEL provides OCI-compatible tools:
| Tool | Purpose |
|---|---|
| podman | Run and manage containers/images (daemonless) |
| skopeo | Inspect, copy, delete, and sign container images |
| buildah | Build new container images |
These tools replace Docker for most use cases and work without a background daemon.
Rootless Containers
Containers can be run:
As root → full privileges but less secure
As unprivileged users (rootless) → safer but cannot bind to ports <1024
- A rootless container cannot expose port 80 unless mapped to a higher unprivileged port.
Containers at Scale
Kubernetes orchestrates large container environments, providing automated scaling, load balancing, self-healing, and controlled rollouts/rollbacks.
It manages containers across an entire cluster, ensuring reliability and efficient resource usage.
OpenShift, Red Hat’s enterprise Kubernetes platform, adds enhanced security, multi-tenancy, developer-friendly workflows, monitoring, and a web-based management interface.
Together, they provide a complete solution for deploying, managing, and scaling containerized applications in production.
Running a Basic Container
This section demonstrates how to install container tools and run a simple rootless container on Red Hat Enterprise Linux.
Installing Container Tools
Install the recommended container tools module:
[root@host ~]# yum module install container-tools
This installs the core utilities:
podman – run and manage containers
skopeo – inspect and copy container images
Understanding Container Registries
- A container registry stores container images that can be pulled and run locally.
- Red Hat provides trusted registries:
registry.redhat.io– Red Hat supported imagesregistry.connect.redhat.com– Certified third-party images
- Always use trusted registries to avoid running unverified code.
Container Image Naming Format
Container images follow this fully qualified naming format:
registry_name/user_name/image_name:tag
registry_name: The container registry where the image is stored, usually a fully qualified domain name (for example,
registry.redhat.io).user_name: The user or organization namespace that owns the image.
image_name: The unique name of the image within the user or organization namespace.
tag: Identifies the image version. If no tag is specified, the
latesttag is used by default.
Pulling a Container Image
- Use Podman to pull an image from a registry.
- The podman pull command pulls the image you specify from the registry and saves it locally
[user@host ~]$ podman pull registry.access.redhat.com/ubi8/ubi:latest Trying to pull registry.access.redhat.com/ubi8/ubi:latest... Getting image source signatures Copying blob 77c58f19bd6e: 70.54 MiB / 70.54 MiB [=========================] 10s Copying blob 47db82df7f3f: 1.68 KiB / 1.68 KiB [===========================] 10s Copying config a1f8c9699786: 4.26 KiB / 4.26 KiB [==========================] 0s Writing manifest to image destination Storing signatures a1f8c969978652a6d1b2dfb265ae0c6c346da69000160cd3ecd5f619e26fa9f3
- Podman stores images locally and you can list them using the
podman imagescommand[user@host ~]$ podman images REPOSITORY TAG IMAGE ID CREATED SIZE registry.access.redhat.com/ubi8/ubi latest a1f8c9699786 5 weeks ago 211 MB
Running an Interactive Container
- The podman run command creates and starts a new container from the image.
- Use the -it options to interact with the container if required. It allocates terminal to the container.
[user@host ~]$ podman run -it registry.access.redhat.com/ubi8/ubi:latest [root@8b032455db1a /]#
- Type exit and press enter to exit from the container
Running a Named Container
- Along with the podman run command use the –name option to provide a name to the container
[user@host ~]$ podman run -it --name=rhel8 registry.access.redhat.com/ubi8/ubi /bin/bash [root@c20631116955 /]# cat /etc/os-release NAME="Red Hat Enterprise Linux" VERSION="8.2 (Ootpa)" ID="rhel" ID_LIKE="fedora" VERSION_ID="8.2" PLATFORM_ID="platform:el8" PRETTY_NAME="Red Hat Enterprise Linux 8.2 (Ootpa)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:redhat:enterprise_linux:8.2:GA" HOME_URL="https://www.redhat.com/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8" REDHAT_BUGZILLA_PRODUCT_VERSION="8.2" REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" REDHAT_SUPPORT_PRODUCT_VERSION="8.2" [root@c20631116955 /]# exit exit [user@host ~]$
Running a One-Time Command
Run a quick command in a container without interacting with it, and automatically remove the container afterward.
[user@host ~]$ podman run --rm registry.access.redhat.com/ubi8/ubi cat /etc/osrelease
Important Podman Options
| Option | Description |
|---|---|
-i | Interactive input |
-t | Allocate terminal |
-d | Run container in background |
--rm | Remove container after exit |
--name | Assign container name |
Container Isolation Basics
Containers use Linux namespaces to isolate processes and users.
- View the processes running inside the container:
[user@host ~]$ podman run -it registry.access.redhat.com/ubi7/ubi /bin/bash [root@ef2550ed815d /]# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 4.5 0.1 11840 2904 pts/0 Ss 22:10 0:00 /bin/bash root 15 0.0 0.1 51768 3388 pts/0 R+ 22:10 0:00 ps aux
- The user name and ID inside the container is different from the user name and ID on the host machine:
[root@ef2550ed815d /]# id uid=0(root) gid=0(root) groups=0(root) [root@ef2550ed815d /]# exit exit [user@host ~]$ id uid=1000(user) gid=1000(user) groups=1000(user),10(wheel)
- The container runs as root inside, but the user remains unprivileged on the host.
Finding and Managing Container Images
Configuring Container Registries
- Podman uses the registries.conf file to determine which container registries it can search and use.
System-wide configuration is present in
/etc/containers/registries.confand user-specific (rootless) configuration is present in~/.config/containers/registries.conf[user@host ~]$ cat /etc/containers/registries.conf [registries.search] registries = ['registry.redhat.io', 'quay.io', 'docker.io'] [registries.insecure] registries = [] [registries.block] registries = []
Key sections in the file:
registries.search → registries Podman searches by default
registries.insecure → registries without TLS (not recommended)
registries.block → blocked registries
- Use
podman infocommand to view the podman registry configuration
Registry Security
Secure registries use TLS encryption
Insecure registries use HTTP or invalid certificates and must be explicitly listed
Some registries require authentication
Login to a private registry using the command
podman login registry.redhat.io
Searching for Container Images
Use
podman searchto find images in registries.- Example: Search for images related to RHEL 8
[user@host ~]$ podman search registry.redhat.io/rhel8 INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED redhat.io registry.redhat.io/openj9/openj9-8-rhel8 OpenJ9 1.8 OpenShift S2I image for Java Apps 0 redhat.io registry.redhat.io/openjdk/openjdk-11-rhel8 OpenJDK S2I image for Java Applications on UBI 0 redhat.io registry.redhat.io/rhel8/rust-toolset Rust and Cargo build system 0 ...output omitted...
- Run the same command with the –no-trunc option to see longer image descriptions:
[user@host ~]$ podman search --no-trunc registry.access.redhat.com/rhel8
Useful Search Options
| Option | Description |
|---|---|
--limit N | Limit results |
--filter stars=N | Minimum star rating |
--filter is-official=true | Official images |
--tls-verify=false | Disable TLS verification |
Using Red Hat Container Catalog
Red Hat provides certified container images through the Red Hat Container Catalog https://access.redhat.com/containers
Benefits:
Trusted & tested images
Security updates and vulnerability fixes
Fully compatible with
podman
Inspecting Container Images
- Use skopeo to inspect a remote image without pulling it actually.
[user@host ~]$ skopeo inspect docker://registry.redhat.io/rhel8/python-36This shows metadata such as Image name, Version, Summary, and Labels.
- Use podman inspect command to image information which is present locally.
[user@host ~]$ podman inspect registry.redhat.io/rhel8/python-36This provides detailed configuration, exposed ports, and runtime settings.
Removing Local Container Images
- Container images are immutable. Updates require downloading a new image and replacing the old one.
- To remove a locally stored image, use the podman rmi command.
[user@host ~]$ podman images REPOSITORY TAG IMAGE ID CREATED SIZE quay.io/generic/rhel7 latest 1d3b6b7d01e4 3 weeks ago 688 MB registry.redhat.io/rhel8/python-36 latest e55cd9a2e0ca 6 weeks ago 811 MB registry.redhat.io/ubi8/ubi latest a1f8c9699786 6 weeks ago 211 MB [user@host ~]$ podman rmi registry.redhat.io/rhel8/python-36:latest e55cd9a2e0ca5f0f4e0249404d1abe3a69d4c6ffa5103d0512dd4263374063ad [user@host ~]$ podman images REPOSITORY TAG IMAGE ID CREATED SIZE quay.io/generic/rhel7 latest 1d3b6b7d01e4 3 weeks ago 688 MB registry.redhat.io/ubi8/ubi latest a1f8c9699786 6 weeks ago 211 MB
- Removing unused images helps free disk space and ensures systems use updated images.
Advanced Container Management
Containers often require network access and runtime configuration. Podman allows this without modifying container images.
Mapping Host Ports to Containers
- Port mapping allows external clients to access services running inside containers.
- Use -p option in the podman run command to map the host port to container port
- Syntax:
podman run -p HOST_PORT:CONTAINER_PORT IMAGE
- Example: Map host port 8000 to container port 8080
[user@host ~]$ podman run -d -p 8000:8080 registry.redhat.io/rhel8/httpd-24
- The container runs in detached mode (
-d) and serves traffic via port 8000 on the host. - Use
podman port -ato view the port mappings - Make sure that the host firewall must allow the mapped port
[root@host ~]# firewall-cmd --add-port=8000/tcp
Ports below 1024 cannot be mapped unless Podman is run as root
Passing Environment Variables to Containers
Containers are commonly configured using environment variables instead of modifying images.
- Use the command podman inspect IMAGE to view the supported variables.
[user@host ~]$ podman inspect registry.redhat.io/rhel8/mariadb-103:1-102
Container images often document variables in the usage or labels section.
Example: Running MariaDB with Environment Variables
[user@host ~]$ podman run -d --name container_name \ -e MYSQL_USER=user_name \ -e MYSQL_PASSWORD=user_password \ -e MYSQL_DATABASE=database_name \ -e MYSQL_ROOT_PASSWORD=mysql_root_password \ -p 3306:3306 \ registry.redhat.io/rhel8/mariadb-103:1-102- This starts a MariaDB container configured at runtime.
Listing Containers
- To list all the running containers use
podsman pscommand, to list including the stopped containers usepodman ps -acommand.[user@host ~]$ podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 89dd9b6354ba registry.redhat.io/rhel8/mariadb-103:1-102 run-mysqld 10 minutes ago Up 10 seconds 0.0.0.0:3306->3306/tcp my-database [user@host ~]$ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 30b743973e98 registry.redhat.io/rhel8/httpd-24:1-105 /bin/bash 17 minutes ago Exited (0) 18 minutes ago 80/tcp my-httpd
- This displays the details includes:
- Container ID.
- Image associated with the container.
- Command executed when the container started.
- Date and time the container was started.
- Total container uptime, if still running, or time since terminated.
- Exposed ports and port forwarding details.
- Container name
Managing Container Lifecycle
| Action | Command | Example |
|---|---|---|
| Stop a container (graceful) | podman stop CONTAINER | podman stop my-database |
| Restart a container | podman restart CONTAINER | podman restart my-database |
| Kill a container (immediate) | podman kill CONTAINER | podman kill my-database |
| Kill container with specific signal | podman kill -s SIGNAL CONTAINER | podman kill -s SIGKILL my-database |
| Remove a stopped container | podman rm CONTAINER | podman rm my-database |
| Force remove a running container | podman rm -f CONTAINER | podman rm -f my-database |
| Remove all stopped containers | podman rm -a | podman rm -a |
| Remove all unused containers | podman container prune | podman container prune |
| Remove unused images | podman image prune | podman image prune |
| Remove unused containers, images, volumes | podman system prune | podman system prune |
- Containers must be stopped before removal unless
-fis used. - Images used by containers cannot be removed unless containers are deleted or force options are used.
prunecommands permanently delete unused resources.
Running Commands Inside a Container
- The podman exec command starts an additional process inside an already running container, which is used to execute the command inside the container.
[user@host ~]$ podman exec 7ed6e671a600 cat /etc/redhat-release Red Hat Enterprise Linux release 8.2 (Ootpa) [user@host ~]$
- The above examples is for executing a single command inside the container.
- To start an interactive shell, use the -it option
[user@host ~]$ podman exec -it my_webserver /bin/bash bash-4.4$ hostname 7ed6e671a600 bash-4.4$ exit [user@host ~]$
- Podman remembers the last container used in any command. You can use the -l option to replace the former container ID or name in the latest Podman command.
[user@host ~]$ podman exec -l cat /etc/redhat-release Red Hat Enterprise Linux release 8.2 (Ootpa) [user@host ~]$
Automatic Container Cleanup
To remove a container automatically when it exits use the command
podman run --rm IMAGE COMMANDUseful for short-lived or test containers.
Attaching Persistent Storage to a Container
- By default, container storage is ephemeral, meaning all data is lost when the container is removed.
- For applications like databases, logs, or uploads, you must attach persistent storage.
Why Persistent Storage Is Needed
Containers are temporary by design
Data stored inside a container is deleted when it stops or is removed
Persistent storage ensures data survives container restarts or replacements
Providing Persistent Storage from the Host
- Podman can mount a host directory into a container.
- The mounted directory exists outside the container lifecycle, so its data remains intact.
- Common use cases such as Database data, Application logs, and User uploads.
Preparing the Host Directory
- Before mounting, ensure:
Correct permissions and ownership
Proper SELinux context
- Podman applies the required SELinux label automatically when using the
:Zoption.
Mounting a Host Directory into a Container
- To mount a host directory to a container, add the –volume (or -v) option to the podman run command, specifying the host directory path and the container storage path, separated by a colon.
- Host-mounted storage persists beyond container life and Works for both root and rootless containers
- Syntax:
podman run -v host_dir:container_dir:Z IMAGEhost_dir→ Directory on the hostcontainer_dir→ Path inside the container:Z→ Appliescontainer_file_tSELinux context
Example: MariaDB with Persistent Data
[user@host ~]$ podman run -d --name mydb \ -v /home/user/dbfiles:/var/lib/mysql:Z \ -e MYSQL_USER=user \ -e MYSQL_PASSWORD=redhat \ -e MYSQL_DATABASE=inventory \ registry.redhat.io/rhel8/mariadb-103:1-102- Database data remains even if the container is removed
- A new container can reuse the same directory
Managing Containers as Systemd Services
Containers running critical services (web servers, databases) usually need to start automatically at boot. Podman integrates with systemd, allowing containers to be managed like regular services.
Why Use Systemd for Containers
Start containers automatically at boot
Restart containers on failure
Manage containers using
systemctlWorks with rootless containers for better security
Running Containers as User Services
Systemd can manage user-level services, allowing non-root users to control containers.
By default, user services start only when the user logs in. To start them at system boot, to enable it run
loginctl enable-lingerand to disable it runloginctl disable-linger[user@host ~]$ loginctl enable-linger [user@host ~]$ loginctl show-user user ...output omitted... Linger=yes [user@host ~]$ loginctl disable-linger [user@host ~]$ loginctl show-user user ...output omitted... Linger=no
Creating Systemd Unit Files for Containers
Podman can automatically generate a systemd unit file from an existing container.
- User service files are stored in
~/.config/systemd/user/directory.[user@host ~]$ ls ~/.config/systemd/user/ myapp.service [user@host ~]$ systemctl --user daemon-reload [user@host ~]$ systemctl --user enable myapp.service [user@host ~]$ systemctl --user start myapp.service
Generating a systemd Service from a Container
- Run a container first using the podman run command.
[user@host ~]$ podman run -d --name web \ -v /home/user/www:/var/www:Z \ registry.redhat.io/rhel8/httpd-24
- Generate the systemd unit file using the podman generate systemd command.
[user@host ~]$ cd ~/.config/systemd/user/ [user@host user]$ podman generate systemd --name web --files --new /home/user/.config/systemd/user/container-web.service
- –new
Creates container on service start
Removes container on service stop
Ideal when using persistent storage
- –files
- Instructs Podman to generates the unit file in the current directory
Reload systemd and Enable the Service
[user@host ~]$ systemctl --user daemon-reload [user@host ~]$ systemctl --user enable container-web [user@host ~]$ systemctl --user start container-web
Managing Containers with systemctl
- Use the systemctl command to control(Start/Stop/Status) your containers.
[user@host ~]$ systemctl --user start container-web [user@host ~]$ systemctl --user stop container-web [user@host ~]$ systemctl --user status container-web
- Do not use
podman start/stoponce systemd manages the container, Always usesystemctl.
Root vs Rootless Container Services
| Feature | Rootless Containers | Root Containers |
|---|---|---|
| Service file location | ~/.config/systemd/user/ | /etc/systemd/system/ |
| Privileges | Non-root user | Root |
--user flag | Required | Not required |
loginctl linger | Required | Not required |
When to Use Systemd vs Kubernetes
Systemd: Small setups, single host, few containers
Kubernetes / OpenShift: Production, scaling, high availability
Improving Command-Line Prductivity
Bash Script
A Bash script is a text file containing a sequence of Linux commands executed in order. Scripts help automate repetitive tasks, reduce errors, and improve efficiency, making them essential for system administrators.
Creating a Bash Script
-
Create a new file using a text editor (vim, nano, etc.).
-
Write commands line by line.
-
Make the file executable.
Advanced editors like vim highlight syntax errors, making scripting easier.
Specifying the Interpreter
- Every Bash script should start with a shebang, which tells the system which interpreter to use.
#!/bin/bashThis ensures the script always runs using Bash.
Making the Script Executable
-
Grant execute permission before running the script
chmod +x script.sh - Run it using
./script.sh -
If the script is placed in a directory listed in
$PATH(such as~/bin), you can run it by name.
Quoting Special Characters
Some characters have special meaning in Bash. To use them literally, apply quoting.
-
Backslash (
\): Escapes a single character. -
Single Quotes (
' '): Preserves text exactly as typed (no variable or command substitution). -
Double Quotes (
" "): Prevents globbing but allows variable and command substitution. -
[user@host ~]$ echo # not a comment [user@host ~]$ echo \# not a comment # not a comment [user@host ~]$ echo # not a comment # [user@host ~]$ echo \# not a comment # # not a comment [user@host ~]$ echo \# not a comment \# # not a comment # [user@host ~]$ echo '# not a comment #' # not a comment # [user@host ~]$ var=$(hostname -s); echo $var host [user@host ~]$ echo "***** hostname is ${var} *****" ***** hostname is host ***** [user@host ~]$ echo Your username variable is \$USER. Your username variable is $USER. [user@host ~]$ echo "Will variable $var evaluate to $(hostname -s)?" Will variable host evaluate to host? [user@host ~]$ echo 'Will variable $var evaluate to $(hostname -s)?' Will variable $var evaluate to $(hostname -s)? [user@host ~]$ echo "\"Hello, world\"" "Hello, world" [user@host ~]$ echo '"Hello, world"' "Hello, world"
Sending Output to STDOUT and STDERR
- By default, commands write to STDOUT. Use
>&2to write error messages to STDERR. - Best practice is to redirect the status message to STDOUT and error messages to STDERR
-
[user@host ~]$ cat ~/bin/hello #!/bin/bash echo "Hello, world" [user@host ~]$ hello Hello, world -
[user@host ~]$ cat ~/bin/hello #!/bin/bash echo "Hello, world" echo "ERROR: Houston, we have a problem." >&2 [user@host ~]$ hello 2> hello.log Hello, world [user@host ~]$ cat hello.log ERROR: Houston, we have a problem.
-
Why echo is Important in Scripts
-
Displays progress messages
-
Shows variable values for debugging
-
Helps identify where scripts fail
Adding simple echo statements is one of the easiest ways to debug scripts.
Running Commands More Efficiently Using Loops
Why Use Loops in Shell Scripts?
System administrators frequently perform repetitive tasks, such as:
-
Running a command on multiple systems
-
Processing multiple files
-
Repeating checks until a task completes
Bash loops help automate these repetitive actions cleanly and efficiently.
Using for Loops
- A
forloop executes commands once for each item in a list. - Syntax:
-
for VARIABLE in LIST; do COMMAND $VARIABLE done
-
- Example:
-
[user@host ~]$ for HOST in host1 host2 host3; do echo $HOST; done host1 host2 host3 [user@host ~]$ for HOST in host{1,2,3}; do echo $HOST; done host1 host2 host3 [user@host ~]$ for HOST in host{1..3}; do echo $HOST; done host1 host2 host3 [user@host ~]$ for FILE in file*; do ls $FILE; done filea fileb filec [user@host ~]$ for FILE in file{a..c}; do ls $FILE; done filea fileb filec [user@host ~]$ for EVEN in $(seq 2 2 10); do echo "$EVEN"; done 2 4 6 8 10
-
- Use double quotes around variables to avoid word-splitting issues.
Understanding Exit Codes
- Every command returns an exit code:
-
0→ success -
Non-zero → failure
-
- The exit code of the last command is stored in
$? -
[user@host bin]$ cat hello #!/bin/bash echo "Hello, world" exit 0 [user@host bin]$ ./hello Hello, world [user@host bin]$ echo $? 0
Testing Script Inputs
Bash provides test operators to validate numbers, strings, and variables.
-
Numeric Comparisons
| Operator | Meaning |
|---|---|
-eq |
equal |
-ne |
not equal |
-gt |
greater than |
-lt |
less than |
-ge |
greater or equal |
-le |
less or equal |
-
-
[user@host ~]$ [ 1 -eq 1 ]; echo $? 0 [user@host ~]$ [ 2 -lt 2 ]; echo $? 1
-
- String Comparisons
-
[user@host ~]$ [ abc = abc ]; echo $? 0 [user@host ~]$ [ abc == def ]; echo $? 1 [user@host ~]$ [ abc != def ]; echo $? 0
-
- String Unary
-
[user@host ~]$ STRING=''; [ -z "$STRING" ]; echo $? 0 [user@host ~]$ STRING='abc'; [ -n "$STRING" ]; echo $? 0
-
Conditional Structures
- It allow a script to make decisions by executing different commands based on whether a specified condition is true or false.
- Bash checks conditions top to bottom and executes the first match.
- Syntax: if/then Statement
-
if <CONDITION>; then <STATEMENT> ... <STATEMENT> fi
-
- Example:
-
[user@host ~]$ if [ -f /etc/passwd ]; then > echo "File exists" > fi
-
- Syntax: if/then/else Statement
-
if <CONDITION>; then <STATEMENT> ... <STATEMENT> else <STATEMENT> ... <STATEMENT> fi
-
- Example:
-
[user@host ~]$ if [ "$USER" = "root" ]; then > echo "Running as root" > else > echo "Running as regular user" > fi
-
- Syntax: if/then/elif/then/else Statement
-
if <CONDITION>; then <STATEMENT> ... <STATEMENT> elif <CONDITION>; then <STATEMENT> ... <STATEMENT> else <STATEMENT> ... <STATEMENT> fi
-
- Example:
-
[user@host ~]$ if systemctl is-active mariadb > /dev/null 2>&1; then > mysql > elif systemctl is-active postgresql > /dev/null 2>&1; then > psql > else > sqlite3 > fi
-
Matching Text in Command Output with Regular Expressions
Regular expressions (regex) let you search and filter text using powerful patterns instead of fixed strings. Tools like grep, vim, and less all support regex for searching logs, config files, and command output.
Regular Expression
- Exact Match
- A simple regex is just plain text. It matches the same characters in the same order.
- Example text:
- cat
dog
concatenate
category
educated
vindication
- cat
- Regex:
cat
Matches:cat,concatenate,category,educated,vindication(because “cat” appears inside them).
- Anchors: start and end of line
- Use anchors to control where in the line the pattern must match.
-
^– beginning of line -
$– end of line
-
- Examples (same text as above):
^cat→ matches lines starting with “cat”dog$→ matches lines ending with “dog”^cat$→ matches a line that is exactlycat
- Use anchors to control where in the line the pattern must match.
-
Wildcards and character sets
-
.(dot): matches any single character (except newline).-
c.t→ matchescat,cot,cut, etc.
-
-
[ ... ]: character set; match one of the listed characters.-
c[ao]t→ matchescatorcot -
c[aou]t→ matchescat,cot,cut
-
-
| Pattern | Description | Example |
|---|---|---|
. |
Matches any single character. | a.b matches abb, a-b. |
? |
Makes the previous character or group optional (0 or 1 time). | colou?r matches color and colour. |
* |
Matches the previous item 0 or more times. | ba* matches b, ba, baaa. |
+ |
Matches the previous item 1 or more times. | ba+ matches ba, baa, but not b. |
{n} |
Matches the previous item exactly n times. |
a{3} matches aaa only. |
{n,} |
Matches the previous item n or more times. |
a{2,} matches aa, aaa, aaaa… |
{,m} |
Matches the previous item at most m times. |
a{,2} matches "", a, aa. |
{n,m} |
Matches the previous item between n and m times. |
a{2,4} matches aa, aaa, aaaa. |
[[:alnum:]] |
Any letter or digit (a–z, A–Z, 0–9). | [[:alnum:]] matches a, Z, 5. |
[[:alpha:]] |
Any letter, upper or lower case. | [[:alpha:]] matches a, Z, but not 5. |
[[:blank:]] |
Space or tab character. | [[:blank:]] matches a space or tab between words. |
[[:cntrl:]] |
Control characters (non-printable, like newline). | Used rarely; often avoided in simple text searches. |
[[:digit:]] |
Any decimal digit 0–9. | [[:digit:]] matches 3 in R3. |
[[:graph:]] |
Any visible character (not space). | Matches letters, digits, punctuation, but not space. |
[[:lower:]] |
Any lowercase letter. | [[:lower:]]+ matches hello but not Hello. |
[[:print:]] |
Any printable character including space. | Matches normal text characters you can see. |
[[:punct:]] |
Any punctuation character. | Matches !, ?, ., ,, etc. |
[[:space:]] |
Any whitespace (space, tab, newline, etc.). | [[:space:]]+ matches spaces and tabs between words. |
[[:upper:]] |
Any uppercase letter. | [[:upper:]] matches H in Hello. |
[[:xdigit:]] |
Any hexadecimal digit (0–9, a–f, A–F). | [[:xdigit:]] matches A in 0xA3. |
\b |
Matches a word boundary (edge of a word). | \bcat\b matches cat, but not concatenate. |
\B |
Matches where there is no word boundary. | \Bcat\B matches educate, not lone cat. |
\< |
Matches the beginning of a word (in some tools). | \<cat matches cat at the start of a word. |
\> |
Matches the end of a word (in some tools). | cat\> matches cat at the end of a word. |
\w |
Matches word characters: letters, digits, and underscore. | \w+ matches hello_123 as one word. |
\W |
Matches non-word characters (not letters, digits, underscore). | \W+ matches !, ?, spaces, etc. |
\s |
Matches any whitespace (space, tab, newline). | \s+ matches multiple spaces between words. |
\S |
Matches any non-whitespace character. | \S+ matches a block of non-space text. |
Matching with grep
-
grepsearches text for lines matching a regex. Basic usagegrep 'PATTERN' FILE - Example: grep lines starting with “computer”
-
[user@host ~]$ grep '^computer' /usr/share/dict/words computer computerese computerise
-
- The grep command can be used in conjunction with other commands using a pipe operator (|).
-
[root@host ~]# ps aux | grep chrony chrony 662 0.0 0.1 29440 2468 ? S 10:56 0:00 /usr/sbin/chronyd
-
- grep Options
| Option | Description |
|---|---|
-i |
Case-insensitive search |
-v |
Invert match (show lines not matching the pattern) |
-r |
Recursive search in directories |
-A N |
Show N lines after each match |
-B N |
Show N lines before each match |
-e |
Add another pattern (logical OR with others) |
-
Example: case-insensitive search.
-
Search for
serverrootin Apache config, ignoring case -
[root@host ~]$ grep -i 'serverroot' /etc/httpd/conf/httpd.conf
-
-
Example: show lines that do not match
-
Show all lines in
/etc/hoststhat do not containserver(case-insensitive) -
[root@host ~]$ grep -vi 'server' /etc/hosts
-
-
Example: hide comment lines
-
Remove comment lines (starting with
#or;) from a file view -
[root@host ~]$ grep -v '^[#;]' /etc/ethertypes
-
-
Example: multiple patterns with
-e-
Search
/var/log/securefor any of these:pam_unix,user root, orAccepted publickey -
[root@host ~]$ grep -e 'pam_unix' -e 'user root' -e 'Accepted publickey' /var/log/secure
-
Searching inside vim / less
Both vim and less support regex-style searching:
-
Press
/, type your pattern, and press Enter -
Press
nto go to the next match,Nfor the previous
