History

The CentOS Shock of 2020

  • For nearly two decades, CentOS (Community ENTerprise Operating System) was the go-to free, binary-compatible clone of Red Hat Enterprise Linux (RHEL).
  • On December 8, 2020, Red Hat and the CentOS project announced that CentOS 8 would reach EOL on December 31, 2021 — cutting its life short by nearly a decade.
  • CentOS would pivot to CentOS Stream, a rolling-release distro that sits upstream of RHEL rather than being a downstream clone.
  • This meant CentOS Stream would receive changes before RHEL, making it a preview/testing ground — not the stable, production-ready clone the community relied on.
  • The announcement sent shockwaves through the Linux community. Millions of servers worldwide ran CentOS. Businesses, universities, and developers suddenly needed an alternative.

Gregory Kurtzer Steps Up

  • Gregory Kurtzer — one of the original co-founders of CentOS — announced on the same day (December 8, 2020) that he would create a new community-driven, RHEL-compatible distribution.
  • He posted in the CentOS forum: “If this is true, I would like to announce that I will be starting a new project to fill the void that CentOS is leaving behind.”
  • The project was named Rocky Linux — a tribute to Rocky McGaugh, the other CentOS co-founder who had passed away before seeing CentOS reach its full potential.
  • Development moved at remarkable speed. A community of thousands rallied around the project within weeks.

Timeline

Dec 8,  2020  → CentOS shift announced; Rocky Linux project announced same day
Dec 2020      → Rocky Linux GitHub org created; community forms
Jan 2021      → Rocky Linux Enterprise Software Foundation (RESF) established
Apr 2021      → Rocky Linux 8.3 Release Candidate published
Jun 21, 2021  → Rocky Linux 8.4 — first stable release (GA)
Nov 2021      → Rocky Linux 8.5 released
Jul 2022      → Rocky Linux 9.0 released (RHEL 9 compatible)
2023-2024     → Rocky Linux 8.x and 9.x point releases continue

Rocky Linux Enterprise Software Foundation (RESF)

  • Rocky Linux is governed by the RESF — a Public Benefit Corporation (PBC) in Delaware, USA.
  • Designed to ensure the project remains community-owned and cannot be “pulled” by any single company.
  • Sponsors include CIQ (Gregory Kurtzer’s company), AWS, Google Cloud, Microsoft Azure, ARM, and others.
  • Competing alternative: AlmaLinux (created by CloudLinux, also announced in Dec 2020).

Introduction

What is Rocky Linux?

  • Rocky Linux is a free, open-source, community-driven enterprise Linux distribution that is 1:1 binary compatible with Red Hat Enterprise Linux (RHEL).
  • It is designed as a drop-in replacement for CentOS — every RPM package, library, and binary behaves identically to its RHEL counterpart.
  • Targets production servers, enterprise workloads, HPC clusters, cloud deployments, and anyone who needs long-term stability without paying for RHEL subscriptions.
  • Uses DNF package manager, RPM packages, SELinux enforcing by default, firewalld, systemd — the full RHEL stack.

Rocky Linux vs RHEL vs CentOS Stream vs AlmaLinux

FeatureRocky LinuxRHELCentOS StreamAlmaLinux
CostFreePaid (free ≤16 systems)FreeFree
RHEL compatibility1:1 binarySourceUpstream preview1:1 binary
Support lifecycle10 years10 years~5 years10 years
Governed byRESF (community)Red Hat (IBM)Red Hat (IBM)AlmaLinux OS Foundation
Release modelDownstream RHEL cloneEnterpriseRolling (pre-RHEL)Downstream RHEL clone
Package managerDNF/RPMDNF/RPMDNF/RPMDNF/RPM
SELinuxEnforcing defaultEnforcing defaultEnforcing defaultEnforcing default
TargetServers, enterpriseEnterpriseCI/CD, testingServers, enterprise
Cloud imagesYesYesYesYes
SponsorCIQ, AWS, GoogleIBM/Red HatRed HatCloudLinux

Rocky Linux Editions

Rocky Linux DVD ISO     → Full installer with all packages (~10 GB)
Rocky Linux Boot ISO    → Minimal network installer (~800 MB)
Rocky Linux Minimal ISO → Minimal offline install (~2 GB)
Rocky Linux Cloud       → AWS AMI, Azure, GCP, OpenStack images
Rocky Linux Container   → Docker/OCI container images
Rocky Linux Vagrant     → Vagrant boxes for dev environments
Rocky Linux Live        → Live desktop (GNOME/KDE/XFCE spins)

Advantages

  • Free RHEL clone — identical binaries, no licensing cost, 10-year support lifecycle.
  • Enterprise stability — conservative package updates, long-term ABI compatibility.
  • RHEL skills transfer — everything you learn on Rocky applies directly to RHEL.
  • SELinux enforcing — strong mandatory access control out of the box.
  • Large ecosystem — EPEL, PowerTools/CRB, thousands of RPM packages.
  • Community governed — RESF structure prevents corporate capture.
  • Excellent cloud support — official images on all major cloud providers.
  • Cockpit web UI — browser-based server management included.

Disadvantages

  • Older packages — ships with older (but stable) software versions vs Fedora/Ubuntu.
  • No rolling updates — major version upgrades require a migration process.
  • Smaller desktop community — primarily server-focused, desktop experience is secondary.
  • RHEL source dependency — Rocky rebuilds from RHEL sources; Red Hat’s source access policies can affect rebuild timelines.
  • Less cutting-edge — not suitable if you need the latest kernel or toolchain versions.

Use Cases

  • Production web servers (LAMP/LEMP stacks), database servers (PostgreSQL, MySQL, MariaDB), HPC clusters, enterprise application servers, CI/CD build environments, learning RHEL administration, cloud-native workloads, container hosts (Podman/Docker), home labs mimicking enterprise environments.

Installation

System Requirements

Rocky Linux 9 (Minimum):
  CPU:   1 GHz 64-bit (x86_64, aarch64, ppc64le, s390x)
  RAM:   1.5 GB (2 GB recommended for GUI)
  Disk:  10 GB (20 GB recommended)
  GPU:   1024x768 (for graphical install)

Rocky Linux 9 (Recommended Server):
  CPU:   2+ cores, 64-bit
  RAM:   4 GB+
  Disk:  40 GB+ (SSD preferred)
  NIC:   1 Gbps+

ISO Types

DVD ISO    (~10 GB)  → Complete offline installer; all package groups included
                        Best for: air-gapped installs, full control
Boot ISO   (~800 MB) → Minimal; downloads packages from internet during install
                        Best for: always-fresh packages, small download
Minimal ISO (~2 GB)  → Offline minimal install; no GUI packages
                        Best for: servers, containers, scripted installs
  • Download from: https://rockylinux.org/download
  • Verify checksum:
# Download the CHECKSUM file alongside the ISO
sha256sum -c CHECKSUM --ignore-missing
# Or manually:
sha256sum Rocky-9.x-x86_64-dvd.iso

Creating Bootable USB

# Linux — using dd
sudo dd if=Rocky-9.x-x86_64-dvd.iso of=/dev/sdX bs=4M status=progress oflag=sync
 
# Windows — use Rufus (https://rufus.ie) or Ventoy
# macOS
sudo dd if=Rocky-9.x-x86_64-dvd.iso of=/dev/rdiskX bs=4m

Anaconda Installer Steps

  • Rocky Linux uses the Anaconda installer (same as Fedora and RHEL).
1. Boot from USB → select "Install Rocky Linux 9"
2. Language & Keyboard → choose your locale
3. Installation Summary screen (hub-and-spoke model):
   ┌─ Software Selection ──────────────────────────────┐
   │  Base Environment:                                 │
   │  • Server with GUI    → GNOME + server tools       │
   │  • Server             → CLI only (recommended)     │
   │  • Minimal Install    → bare minimum               │
   │  • Workstation        → desktop environment        │
   │  • Custom OS          → you choose everything      │
   └───────────────────────────────────────────────────┘
4. Installation Destination → select disk
   - Automatic partitioning (recommended):
     /boot/efi  → 600 MB  (EFI, FAT32)
     /boot      → 1 GB    (ext4)
     /          → rest    (XFS — Rocky default)
     swap       → auto    (or zram on modern systems)
   - Custom: LVM is common for enterprise flexibility
5. Network & Hostname → set hostname (e.g., server01.example.com)
6. Root Password → set strong root password (or lock root, use sudo user)
7. User Creation → create admin user, check "Make this user administrator"
8. Begin Installation → ~10-20 minutes
9. Reboot → remove USB

Default Filesystem: XFS

  • Rocky Linux (like RHEL) defaults to XFS for the root filesystem.
XFS advantages:
• High performance for large files and parallel I/O
• Excellent scalability (supports up to 8 exabytes)
• Online defragmentation and resizing (grow only)
• Journaling for crash recovery
• Default in RHEL/Rocky since RHEL 7

Note: XFS cannot shrink — plan partition sizes carefully.
LVM is recommended for flexibility (can add space later).

First Boot Setup

# Update all packages immediately
sudo dnf update -y
 
# Enable EPEL (Extra Packages for Enterprise Linux)
sudo dnf install epel-release -y
 
# Enable CRB (CodeReady Builder) — needed by many EPEL packages
sudo dnf config-manager --set-enabled crb
 
# Install common tools
sudo dnf install -y vim curl wget git htop net-tools bash-completion \
  tar unzip zip bind-utils lsof strace tcpdump
 
# Check SELinux status
getenforce        # should say "Enforcing"
sestatus          # detailed SELinux status
 
# Check firewall status
sudo systemctl status firewalld
 
# Set timezone
sudo timedatectl set-timezone Asia/Kolkata   # adjust to your zone
timedatectl status
 
# Set hostname
sudo hostnamectl set-hostname myserver.example.com

Kernel & Architecture

RHEL-Compatible Kernel

  • Rocky Linux ships the same kernel version as RHEL — stable, heavily patched, and enterprise-tested.
  • Unlike Fedora (which ships the latest upstream kernel), Rocky’s kernel is conservative and backport-heavy.
uname -r                    # current kernel version
uname -a                    # full system info
rpm -q kernel               # all installed kernels
sudo dnf list installed kernel*
 
# Rocky 9 ships kernel 5.14.x (RHEL 9 baseline)
# Rocky 8 ships kernel 4.18.x (RHEL 8 baseline)
 
# GRUB2 — manage boot entries
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
sudo grubby --default-kernel          # show default kernel
sudo grubby --set-default /boot/vmlinuz-<version>
sudo grubby --info=ALL                # all boot entries

Boot Process

flowchart TD
    A[Power On / Reset] --> B[UEFI/BIOS POST]
    B --> C[UEFI reads EFI partition\n/boot/efi/EFI/rocky/]
    C --> D[GRUB2 Bootloader\n/boot/grub2/grub.cfg]
    D --> E[Kernel selected\nvmlinuz-x.x.x]
    E --> F[initramfs loaded\ndracut-generated]
    F --> G[Kernel decompresses\nand initializes hardware]
    G --> H[systemd PID 1 starts]
    H --> I[sysinit.target\nmount filesystems, udev]
    I --> J[basic.target\nlogging, sockets]
    J --> K{Boot target?}
    K -->|Server| L[multi-user.target\nCLI login]
    K -->|Workstation| M[graphical.target\nGDM login]

Linux File System Hierarchy (FHS) on Rocky

/           Root filesystem (XFS by default)
├── /bin    → symlink to /usr/bin  (merged-usr since Rocky 9)
├── /boot   Kernel, initramfs, GRUB2 config
├── /dev    Device files (managed by udev)
├── /etc    System-wide configuration files
├── /home   User home directories
├── /lib    → symlink to /usr/lib
├── /lib64  → symlink to /usr/lib64
├── /media  Auto-mount removable media
├── /mnt    Manual temporary mount point
├── /opt    Optional/third-party software
├── /proc   Virtual FS: process and kernel info
├── /root   Root user's home directory
├── /run    Runtime data (tmpfs, cleared on reboot)
├── /sbin   → symlink to /usr/sbin
├── /srv    Data served by this system
├── /sys    Virtual FS: hardware/driver info (sysfs)
├── /tmp    Temporary files (tmpfs or XFS)
├── /usr    User programs, libraries, docs
│   ├── /usr/bin    User executables
│   ├── /usr/lib    Libraries
│   ├── /usr/local  Locally compiled software
│   └── /usr/share  Architecture-independent data
└── /var    Variable data (logs, spool, cache)
    ├── /var/log    System logs
    ├── /var/lib    Application state data
    └── /var/spool  Print/mail queues

GRUB2 Management

# Edit GRUB defaults
sudo vim /etc/default/grub
# Key options:
# GRUB_TIMEOUT=5          → boot menu timeout in seconds
# GRUB_DEFAULT=saved      → remember last selection
# GRUB_CMDLINE_LINUX=""   → kernel parameters
 
# Regenerate GRUB config after changes
# UEFI systems:
sudo grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg
# BIOS/Legacy systems:
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
 
# List all kernels
sudo grubby --info=ALL
 
# Set default kernel
sudo grubby --set-default /boot/vmlinuz-5.14.0-xxx.el9.x86_64
 
# Add kernel parameter (e.g., disable IPv6)
sudo grubby --update-kernel=ALL --args="ipv6.disable=1"
 
# Remove kernel parameter
sudo grubby --update-kernel=ALL --remove-args="ipv6.disable=1"

DNF Package Management

DNF Overview

  • DNF (Dandified YUM) is the default package manager for Rocky Linux, inherited from RHEL/Fedora.
  • Handles dependency resolution, transaction history, module streams, and repository management.
flowchart LR
    A[User: dnf install nginx] --> B[DNF reads repo metadata]
    B --> C[Resolves dependencies]
    C --> D[Downloads RPMs\nfrom enabled repos]
    D --> E[GPG signature check]
    E --> F[RPM transaction\ninstall/upgrade/erase]
    F --> G[Run scriptlets\npost-install hooks]
    G --> H[Package installed ✓]

Essential DNF Commands

# ── SEARCH & INFO ──────────────────────────────────────
dnf search nginx                    # search by name/description
dnf info nginx                      # detailed package info
dnf list available                  # all available packages
dnf list installed                  # all installed packages
dnf list updates                    # available updates
dnf provides /usr/bin/python3       # which package owns a file
dnf repoquery --list nginx          # list files in a package
dnf repoquery --requires nginx      # show dependencies
 
# ── INSTALL & REMOVE ───────────────────────────────────
sudo dnf install nginx              # install package
sudo dnf install nginx-1.20.1       # install specific version
sudo dnf install ./local.rpm        # install local RPM file
sudo dnf remove nginx               # remove package
sudo dnf autoremove                 # remove unused dependencies
sudo dnf reinstall nginx            # reinstall package
 
# ── UPDATE ─────────────────────────────────────────────
sudo dnf update                     # update all packages
sudo dnf update nginx               # update specific package
sudo dnf upgrade                    # alias for update
sudo dnf check-update               # check without installing
 
# ── GROUPS ─────────────────────────────────────────────
dnf group list                      # list all package groups
dnf group info "Development Tools"  # group details
sudo dnf group install "Development Tools"
sudo dnf group remove "Development Tools"
 
# ── HISTORY ────────────────────────────────────────────
dnf history                         # transaction history
dnf history info 5                  # details of transaction #5
sudo dnf history undo 5             # undo transaction #5
sudo dnf history rollback 3         # rollback to transaction #3
 
# ── CACHE ──────────────────────────────────────────────
sudo dnf clean all                  # clean all cached data
sudo dnf clean packages             # clean cached packages only
sudo dnf makecache                  # rebuild metadata cache

DNF Modules (AppStream)

  • Rocky Linux uses Application Streams (AppStream) — allows multiple versions of the same software to coexist.
# List all available modules
dnf module list
 
# List specific module streams
dnf module list nodejs
 
# Enable a specific stream
sudo dnf module enable nodejs:18
 
# Install module (default profile)
sudo dnf module install nodejs:18
 
# Install specific profile
sudo dnf module install nodejs:18/development
 
# Switch streams (e.g., PHP 7.4 → 8.1)
sudo dnf module reset php
sudo dnf module enable php:8.1
sudo dnf module install php:8.1
 
# Disable a module
sudo dnf module disable nodejs:18
 
# Show active module info
dnf module info nodejs

Repository Management

# List all repos
dnf repolist
dnf repolist all              # including disabled repos
 
# Enable/disable a repo
sudo dnf config-manager --set-enabled powertools   # Rocky 8
sudo dnf config-manager --set-enabled crb          # Rocky 9
sudo dnf config-manager --set-disabled crb
 
# Add a repo manually
sudo dnf config-manager --add-repo https://example.com/repo.repo
 
# Repo files location
ls /etc/yum.repos.d/

Rocky Linux Default Repos

baseos      → Core OS packages (kernel, glibc, systemd)
appstream   → Application packages + module streams
extras      → Extra packages not in RHEL
crb         → CodeReady Builder (Rocky 9) / PowerTools (Rocky 8)
            → Needed for EPEL and development headers

EPEL Repository

# Install EPEL (Extra Packages for Enterprise Linux)
sudo dnf install epel-release -y
 
# Enable CRB first (many EPEL packages need it)
sudo dnf config-manager --set-enabled crb   # Rocky 9
# or
sudo dnf config-manager --set-enabled powertools  # Rocky 8
 
# Verify EPEL is active
dnf repolist | grep epel
 
# EPEL provides: htop, tmux, neovim, fail2ban, certbot, and thousands more
sudo dnf install htop tmux neovim fail2ban -y

RPM Commands

# Query installed packages
rpm -qa                          # list all installed packages
rpm -qi nginx                    # package info
rpm -ql nginx                    # list files in package
rpm -qf /etc/nginx/nginx.conf    # which package owns this file
rpm -qR nginx                    # package dependencies
 
# Install/verify RPM files
sudo rpm -ivh package.rpm        # install with verbose + hash
sudo rpm -Uvh package.rpm        # upgrade
sudo rpm -e nginx                # erase/remove
rpm -V nginx                     # verify package integrity
rpm -Va                          # verify all packages
 
# GPG keys
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial
rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n'

Shell & Terminal

Default Shell: Bash

  • Rocky Linux uses Bash (Bourne Again Shell) as the default shell — same as RHEL.
echo $SHELL          # /bin/bash
bash --version       # GNU bash version
cat /etc/shells      # all available shells
chsh -s /bin/zsh     # change your shell to zsh

Essential Commands

# ── NAVIGATION ─────────────────────────────────────────
pwd                  # print working directory
ls -la               # list with hidden files + details
ls -lh               # human-readable sizes
cd /etc              # change directory
cd ~                 # go to home
cd -                 # go to previous directory
 
# ── FILE OPERATIONS ────────────────────────────────────
cp -r src/ dest/     # copy recursively
mv file.txt /tmp/    # move/rename
rm -rf dir/          # remove recursively (careful!)
mkdir -p a/b/c       # create nested directories
touch file.txt       # create empty file / update timestamp
ln -s /etc/nginx nginx_link   # symbolic link
ln /etc/hosts hosts_hard      # hard link
 
# ── VIEWING FILES ───────────────────────────────────────
cat /etc/os-release  # view file
less /var/log/messages  # paginated view
head -20 file.log    # first 20 lines
tail -f /var/log/messages  # follow log in real time
grep -r "error" /var/log/  # recursive search
grep -i "failed" auth.log  # case-insensitive
 
# ── DISK & SYSTEM ───────────────────────────────────────
df -h                # disk usage (human-readable)
du -sh /var/log/     # directory size
free -h              # memory usage
top                  # process monitor
htop                 # better process monitor (install via EPEL)
ps aux               # all running processes
ps aux | grep nginx  # find specific process
kill -9 PID          # force kill process
killall nginx        # kill by name
 
# ── ARCHIVES ────────────────────────────────────────────
tar -czf archive.tar.gz dir/   # create gzip archive
tar -xzf archive.tar.gz        # extract gzip archive
tar -cjf archive.tar.bz2 dir/  # create bzip2 archive
tar -xjf archive.tar.bz2       # extract bzip2 archive
zip -r archive.zip dir/        # create zip
unzip archive.zip              # extract zip

File Permissions

# Permission format: [type][owner][group][others]
# Example: -rwxr-xr-- 1 root wheel 4096 Jan 1 12:00 script.sh
#           d = directory, - = file, l = symlink
#           r=4, w=2, x=1
 
chmod 755 script.sh      # rwxr-xr-x
chmod 644 config.conf    # rw-r--r--
chmod +x script.sh       # add execute for all
chmod u+x,g-w file       # symbolic mode
chmod -R 755 /var/www/   # recursive
 
chown user:group file    # change owner and group
chown -R nginx:nginx /var/www/html/
chgrp wheel file         # change group only
 
# Special permissions
chmod 4755 file          # setuid (runs as owner)
chmod 2755 dir/          # setgid (new files inherit group)
chmod 1777 /tmp          # sticky bit (only owner can delete)
 
# View permissions
ls -la file
stat file                # detailed file info including permissions

Shell Scripting Basics

#!/bin/bash
# Rocky Linux bash script example
 
# Variables
NAME="Rocky"
VERSION=9
echo "Welcome to $NAME Linux $VERSION"
 
# Conditionals
if [ -f /etc/rocky-release ]; then
    echo "This is Rocky Linux"
elif [ -f /etc/redhat-release ]; then
    echo "This is a RHEL-family distro"
else
    echo "Unknown distro"
fi
 
# Loops
for pkg in nginx vim curl; do
    sudo dnf install -y "$pkg"
done
 
# Functions
check_service() {
    if systemctl is-active --quiet "$1"; then
        echo "$1 is running"
    else
        echo "$1 is NOT running"
    fi
}
check_service nginx
check_service sshd
 
# Read user input
read -p "Enter hostname: " HOSTNAME
sudo hostnamectl set-hostname "$HOSTNAME"

User & Group Management

User Account Types

root (UID 0)      → superuser, full system access
System users      → UID 1-999, for services (nginx, postgres, etc.)
Regular users     → UID 1000+, human users

User Commands

# Create user
sudo useradd username                    # basic user creation
sudo useradd -m -s /bin/bash -c "Full Name" username  # with home + shell
sudo useradd -u 1500 -g developers username  # specific UID/GID
 
# Set password
sudo passwd username
 
# Modify user
sudo usermod -aG wheel username          # add to wheel (sudo) group
sudo usermod -aG docker,nginx username   # add to multiple groups
sudo usermod -s /bin/zsh username        # change shell
sudo usermod -l newname oldname          # rename user
sudo usermod -L username                 # lock account
sudo usermod -U username                 # unlock account
 
# Delete user
sudo userdel username                    # keep home directory
sudo userdel -r username                 # remove home + mail spool
 
# View user info
id username                              # UID, GID, groups
whoami                                   # current user
who                                      # logged-in users
w                                        # who + what they're doing
last                                     # login history
cat /etc/passwd                          # user database
getent passwd username                   # user entry

Group Commands

sudo groupadd developers                 # create group
sudo groupadd -g 2000 developers         # with specific GID
sudo groupmod -n devs developers         # rename group
sudo groupdel developers                 # delete group
 
# View groups
groups username                          # user's groups
cat /etc/group                           # group database
getent group wheel                       # specific group

The wheel Group & sudo

  • On Rocky Linux (like RHEL), the wheel group grants sudo access.
# Add user to wheel group
sudo usermod -aG wheel username
 
# Verify
groups username    # should show "wheel"
 
# sudo configuration
sudo visudo        # safely edit /etc/sudoers
 
# /etc/sudoers key lines:
# %wheel  ALL=(ALL)  ALL          → wheel group can sudo (with password)
# %wheel  ALL=(ALL)  NOPASSWD: ALL → no password (less secure)
 
# User-specific sudo rule (in /etc/sudoers.d/)
echo "username ALL=(ALL) NOPASSWD: /usr/bin/dnf" | sudo tee /etc/sudoers.d/username

Password Policies

# View password aging info
sudo chage -l username
 
# Set password expiry (90 days)
sudo chage -M 90 username
 
# Force password change on next login
sudo chage -d 0 username
 
# Set minimum days between changes
sudo chage -m 7 username
 
# Password policy in /etc/login.defs
grep -E "PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_WARN_AGE" /etc/login.defs

Systemd & Service Management

systemctl Essentials

# ── SERVICE CONTROL ────────────────────────────────────
sudo systemctl start nginx          # start service
sudo systemctl stop nginx           # stop service
sudo systemctl restart nginx        # stop + start
sudo systemctl reload nginx         # reload config (no downtime)
sudo systemctl enable nginx         # start on boot
sudo systemctl disable nginx        # don't start on boot
sudo systemctl enable --now nginx   # enable + start immediately
sudo systemctl disable --now nginx  # disable + stop immediately
 
# ── STATUS & INFO ───────────────────────────────────────
systemctl status nginx              # service status + recent logs
systemctl is-active nginx           # active/inactive
systemctl is-enabled nginx          # enabled/disabled
systemctl is-failed nginx           # failed/not-failed
systemctl list-units --type=service # all active services
systemctl list-units --type=service --all  # including inactive
systemctl list-unit-files --type=service   # all + enabled state
 
# ── SYSTEM TARGETS ──────────────────────────────────────
systemctl get-default               # current default target
sudo systemctl set-default multi-user.target   # set CLI default
sudo systemctl set-default graphical.target    # set GUI default
sudo systemctl isolate rescue.target            # switch to rescue mode
 
# ── SYSTEM POWER ────────────────────────────────────────
sudo systemctl reboot               # reboot
sudo systemctl poweroff             # shutdown
sudo systemctl halt                 # halt (no power off)
sudo systemctl suspend              # suspend to RAM

journalctl — Log Management

# View all logs
journalctl                          # all logs (oldest first)
journalctl -r                       # reverse (newest first)
journalctl -f                       # follow (like tail -f)
journalctl -n 50                    # last 50 lines
 
# Filter by service
journalctl -u nginx                 # nginx logs only
journalctl -u nginx -f              # follow nginx logs
journalctl -u nginx --since today   # today's nginx logs
 
# Filter by time
journalctl --since "2024-01-01 00:00:00"
journalctl --since "1 hour ago"
journalctl --since yesterday --until today
 
# Filter by priority
journalctl -p err                   # errors only
journalctl -p warning               # warnings and above
# Priorities: emerg, alert, crit, err, warning, notice, info, debug
 
# Filter by boot
journalctl -b                       # current boot
journalctl -b -1                    # previous boot
journalctl --list-boots             # all boot sessions
 
# Kernel messages
journalctl -k                       # kernel messages (like dmesg)
dmesg | tail -20                    # recent kernel messages
 
# Disk usage
journalctl --disk-usage
sudo journalctl --vacuum-size=500M  # keep only 500MB of logs
sudo journalctl --vacuum-time=30d   # keep only last 30 days

Traditional Log Files

/var/log/messages       # general system messages (main log)
/var/log/secure         # authentication, sudo, SSH logs
/var/log/cron           # cron job logs
/var/log/maillog        # mail server logs
/var/log/boot.log       # boot messages
/var/log/dnf.log        # DNF package manager log
/var/log/audit/audit.log # SELinux + auditd events
/var/log/httpd/         # Apache access + error logs
/var/log/nginx/         # Nginx access + error logs

Custom Systemd Unit

# /etc/systemd/system/myapp.service
[Unit]
Description=My Custom Application
After=network.target
Wants=network-online.target
 
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
 
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
 
[Install]
WantedBy=multi-user.target
# After creating the unit file:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
journalctl -u myapp -f

Networking

Network Commands

# ── IP & INTERFACES ────────────────────────────────────
ip addr show                        # all interfaces + IPs
ip addr show eth0                   # specific interface
ip link show                        # link layer info
ip route show                       # routing table
ip route add default via 192.168.1.1  # add default gateway
ip neigh show                       # ARP table
 
# ── CONNECTIVITY ────────────────────────────────────────
ping -c 4 8.8.8.8                   # ping (4 packets)
traceroute 8.8.8.8                  # trace route
mtr 8.8.8.8                         # combined ping + traceroute
curl -I https://rockylinux.org      # HTTP headers
wget https://example.com/file.tar.gz  # download file
 
# ── SOCKETS & PORTS ─────────────────────────────────────
ss -tulnp                           # all listening ports + process
ss -tulnp | grep :80                # who's on port 80
netstat -tulnp                      # older alternative (net-tools)
 
# ── DNS ─────────────────────────────────────────────────
dig rockylinux.org                  # DNS lookup
dig +short rockylinux.org           # just the IP
nslookup rockylinux.org             # alternative DNS lookup
host rockylinux.org                 # simple lookup
cat /etc/resolv.conf                # DNS servers

NetworkManager & nmcli

  • Rocky Linux uses NetworkManager for network configuration.
# ── STATUS ──────────────────────────────────────────────
nmcli general status               # overall NM status
nmcli connection show              # all connections
nmcli device status                # all devices
nmcli device show eth0             # device details
 
# ── CONFIGURE STATIC IP ─────────────────────────────────
nmcli connection modify "eth0" \
    ipv4.method manual \
    ipv4.addresses "192.168.1.100/24" \
    ipv4.gateway "192.168.1.1" \
    ipv4.dns "8.8.8.8,8.8.4.4"
nmcli connection up "eth0"
 
# ── CONFIGURE DHCP ──────────────────────────────────────
nmcli connection modify "eth0" ipv4.method auto
nmcli connection up "eth0"
 
# ── ADD NEW CONNECTION ───────────────────────────────────
nmcli connection add type ethernet ifname eth1 con-name "my-eth1"
 
# ── WIFI (if applicable) ─────────────────────────────────
nmcli device wifi list
nmcli device wifi connect "SSID" password "password"
 
# Connection files location
ls /etc/NetworkManager/system-connections/

firewalld — Zone-Based Firewall

  • Rocky Linux uses firewalld with a zone-based model (same as RHEL/Fedora).
# ── STATUS ──────────────────────────────────────────────
sudo systemctl status firewalld
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --list-all              # current zone rules
 
# ── ZONES ───────────────────────────────────────────────
# Zones (trust level low → high):
# drop, block, public, external, dmz, work, home, internal, trusted
sudo firewall-cmd --set-default-zone=public
 
# ── ALLOW SERVICES ──────────────────────────────────────
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload                # apply permanent rules
 
# ── ALLOW PORTS ─────────────────────────────────────────
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=3000-3010/tcp
sudo firewall-cmd --permanent --remove-port=8080/tcp
 
# ── RICH RULES (advanced) ───────────────────────────────
# Allow specific IP to SSH
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.50" service name="ssh" accept'
# Block an IP
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.5" drop'
 
# ── LIST & REMOVE ────────────────────────────────────────
sudo firewall-cmd --list-services
sudo firewall-cmd --list-ports
sudo firewall-cmd --permanent --remove-service=telnet

SSH Hardening

# Edit SSH config
sudo vim /etc/ssh/sshd_config
 
# Recommended hardening settings:
# /etc/ssh/sshd_config — Rocky Linux SSH hardening
Port 2222                        # change default port (optional)
Protocol 2                       # SSH protocol 2 only
PermitRootLogin no               # disable root SSH login
PasswordAuthentication no        # key-based auth only
PubkeyAuthentication yes         # enable key auth
AuthorizedKeysFile .ssh/authorized_keys
PermitEmptyPasswords no          # no empty passwords
MaxAuthTries 3                   # limit auth attempts
LoginGraceTime 30                # 30 second login window
ClientAliveInterval 300          # disconnect idle after 5 min
ClientAliveCountMax 2
AllowUsers youruser              # whitelist specific users
X11Forwarding no                 # disable X11 forwarding
# Apply changes
sudo systemctl restart sshd
 
# If you changed the port, update firewalld
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
 
# Generate SSH key pair (on client)
ssh-keygen -t ed25519 -C "your@email.com"
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

SELinux

What is SELinux?

  • Security-Enhanced Linux (SELinux) is a mandatory access control (MAC) system built into the Linux kernel, developed by the NSA and Red Hat.
  • On Rocky Linux (like RHEL and Fedora), SELinux is enforcing by default — it actively blocks unauthorized access.
  • Unlike traditional Unix permissions (DAC), SELinux enforces policies based on labels/contexts on every file, process, and port.

SELinux Modes

# Check current mode
getenforce                    # Enforcing / Permissive / Disabled
sestatus                      # detailed status
 
# Temporarily change mode (until reboot)
sudo setenforce 0             # switch to Permissive (log but don't block)
sudo setenforce 1             # switch back to Enforcing
 
# Permanently change mode
sudo vim /etc/selinux/config
# /etc/selinux/config
SELINUX=enforcing     # enforcing | permissive | disabled
SELINUXTYPE=targeted  # targeted (default) | mls

SELinux Contexts

# View contexts
ls -Z /var/www/html/          # file contexts
ps -eZ | grep nginx           # process contexts
ss -tlnpZ                     # port contexts
 
# Context format: user:role:type:level
# Example: system_u:object_r:httpd_sys_content_t:s0
 
# Change file context
sudo chcon -t httpd_sys_content_t /var/www/html/myfile.html
sudo chcon -R -t httpd_sys_content_t /var/www/mysite/
 
# Restore default context (from policy)
sudo restorecon -v /var/www/html/myfile.html
sudo restorecon -Rv /var/www/mysite/
 
# Set persistent custom context
sudo semanage fcontext -a -t httpd_sys_content_t "/mywebroot(/.*)?"
sudo restorecon -Rv /mywebroot/

SELinux Booleans

# List all booleans
getsebool -a
getsebool -a | grep httpd
 
# Common booleans
sudo setsebool -P httpd_can_network_connect on    # allow Apache to connect to network
sudo setsebool -P httpd_can_network_connect_db on # allow Apache → database
sudo setsebool -P httpd_use_nfs on                # allow Apache to use NFS
sudo setsebool -P httpd_execmem on                # allow Apache to execute memory
sudo setsebool -P allow_user_exec_content on      # allow users to run content
 
# -P flag makes the change persistent across reboots

SELinux Port Management

# List ports for a type
sudo semanage port -l | grep http
sudo semanage port -l | grep ssh
 
# Add a custom port (e.g., Nginx on port 8080)
sudo semanage port -a -t http_port_t -p tcp 8080
 
# Modify existing port label
sudo semanage port -m -t http_port_t -p tcp 8080
 
# Remove custom port
sudo semanage port -d -t http_port_t -p tcp 8080

Troubleshooting SELinux Denials

# View recent denials
sudo ausearch -m avc -ts recent
sudo ausearch -m avc -ts today
 
# View denials in human-readable form
sudo sealert -a /var/log/audit/audit.log
 
# Install setroubleshoot for better messages
sudo dnf install setroubleshoot-server -y
sudo systemctl restart auditd
 
# audit2why — explain why something was denied
sudo ausearch -m avc -ts recent | audit2why
 
# audit2allow — generate a policy module to allow the action
sudo ausearch -m avc -ts recent | audit2allow -M mypolicy
sudo semodule -i mypolicy.pp
 
# Check /var/log/audit/audit.log directly
sudo grep "denied" /var/log/audit/audit.log | tail -20

Security Hardening

System Crypto Policies

  • Rocky Linux 9 introduces system-wide cryptographic policies — a single setting that configures all crypto libraries consistently.
# View current policy
update-crypto-policies --show
 
# Available policies:
# DEFAULT  → balanced security/compatibility
# FUTURE   → stricter (may break old clients)
# LEGACY   → maximum compatibility (weaker)
# FIPS     → FIPS 140-2 compliance
 
# Set policy
sudo update-crypto-policies --set DEFAULT
sudo update-crypto-policies --set FUTURE
 
# Apply FIPS mode (requires reboot)
sudo fips-mode-setup --enable
sudo reboot

fail2ban — Brute Force Protection

# Install (from EPEL)
sudo dnf install fail2ban -y
sudo systemctl enable --now fail2ban
 
# Configuration
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo vim /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local — key settings
[DEFAULT]
bantime  = 3600        ; ban for 1 hour
findtime = 600         ; within 10 minutes
maxretry = 5           ; after 5 failures
 
[sshd]
enabled  = true
port     = ssh
logpath  = %(sshd_log)s
backend  = %(sshd_backend)s
 
[nginx-http-auth]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
sudo systemctl restart fail2ban
 
# Check status
sudo fail2ban-client status
sudo fail2ban-client status sshd
 
# Unban an IP
sudo fail2ban-client set sshd unbanip 192.168.1.100

auditd — System Auditing

# auditd is installed and running by default on Rocky Linux
sudo systemctl status auditd
 
# View audit log
sudo ausearch -ts today
sudo ausearch -m USER_LOGIN -ts today    # login events
sudo ausearch -m SYSCALL -ts recent      # system calls
 
# Add audit rules
sudo auditctl -w /etc/passwd -p wa -k passwd_changes
sudo auditctl -w /etc/sudoers -p wa -k sudoers_changes
sudo auditctl -w /var/log/auth.log -p wa -k auth_log
 
# Persistent audit rules
sudo vim /etc/audit/rules.d/audit.rules
# /etc/audit/rules.d/audit.rules
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config
-a always,exit -F arch=b64 -S execve -k exec_commands
sudo service auditd restart
sudo aureport --summary                  # audit summary report
sudo aureport --login                    # login report
sudo aureport --failed                   # failed events

AIDE — File Integrity Monitoring

# Install AIDE (Advanced Intrusion Detection Environment)
sudo dnf install aide -y
 
# Initialize the database (takes a few minutes)
sudo aide --init
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
 
# Run a check (compare current state vs baseline)
sudo aide --check
 
# Update database after legitimate changes
sudo aide --update
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
 
# Schedule daily checks via cron
echo "0 3 * * * root /usr/sbin/aide --check | mail -s 'AIDE Report' admin@example.com" \
    | sudo tee /etc/cron.d/aide

dnf-automatic — Automatic Security Updates

sudo dnf install dnf-automatic -y
 
# Configure
sudo vim /etc/dnf/automatic.conf
# /etc/dnf/automatic.conf
[commands]
upgrade_type = security    # only security updates (or 'default' for all)
apply_updates = yes        # auto-apply (or 'no' to just download)
download_updates = yes
 
[emitters]
emit_via = email
 
[email]
email_from = root@localhost
email_to = admin@example.com
email_host = localhost
# Enable the timer
sudo systemctl enable --now dnf-automatic.timer
sudo systemctl status dnf-automatic.timer

Lynis — Security Auditing

# Install Lynis (from EPEL)
sudo dnf install lynis -y
 
# Run full system audit
sudo lynis audit system
 
# View report
cat /var/log/lynis.log
cat /var/log/lynis-report.dat
 
# Lynis gives a hardening index score and specific recommendations
# Focus on "Suggestions" and "Warnings" in the output

Rocky Linux as a Server

LAMP Stack (Linux + Apache + MariaDB + PHP)

# Install Apache
sudo dnf install httpd -y
sudo systemctl enable --now httpd
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
 
# Install MariaDB
sudo dnf install mariadb-server -y
sudo systemctl enable --now mariadb
sudo mysql_secure_installation    # secure the installation
 
# Install PHP (using AppStream module)
sudo dnf module enable php:8.1 -y
sudo dnf install php php-mysqlnd php-fpm php-json php-mbstring php-xml -y
sudo systemctl enable --now php-fpm
sudo systemctl restart httpd
 
# Test PHP
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/info.php
curl http://localhost/info.php | grep "PHP Version"
sudo rm /var/www/html/info.php   # remove after testing!
 
# SELinux for Apache
sudo setsebool -P httpd_can_network_connect_db on
sudo restorecon -Rv /var/www/html/

Nginx Web Server

# Install Nginx
sudo dnf install nginx -y
sudo systemctl enable --now nginx
 
# Basic virtual host config
sudo vim /etc/nginx/conf.d/mysite.conf
# /etc/nginx/conf.d/mysite.conf
server {
    listen 80;
    server_name mysite.example.com;
    root /var/www/mysite;
    index index.html index.php;
 
    access_log /var/log/nginx/mysite_access.log;
    error_log  /var/log/nginx/mysite_error.log;
 
    location / {
        try_files $uri $uri/ =404;
    }
 
    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
sudo nginx -t                    # test config
sudo systemctl reload nginx      # apply config
 
# SELinux for Nginx
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/mysite(/.*)?"
sudo restorecon -Rv /var/www/mysite/

PostgreSQL Database Server

# Install PostgreSQL (using AppStream module)
sudo dnf module enable postgresql:15 -y
sudo dnf install postgresql-server postgresql-contrib -y
 
# Initialize database cluster
sudo postgresql-setup --initdb
 
# Start and enable
sudo systemctl enable --now postgresql
 
# Connect as postgres user
sudo -u postgres psql
 
# Basic PostgreSQL commands
-- Create database and user
CREATE DATABASE myapp;
CREATE USER myuser WITH ENCRYPTED PASSWORD 'strongpassword';
GRANT ALL PRIVILEGES ON DATABASE myapp TO myuser;
\q
# Configure remote access
sudo vim /var/lib/pgsql/data/postgresql.conf
# listen_addresses = 'localhost'   → change to '*' for remote
 
sudo vim /var/lib/pgsql/data/pg_hba.conf
# Add: host myapp myuser 192.168.1.0/24 md5
 
sudo systemctl restart postgresql
sudo firewall-cmd --permanent --add-service=postgresql
sudo firewall-cmd --reload

Let’s Encrypt SSL with Certbot

# Install Certbot (from EPEL)
sudo dnf install certbot python3-certbot-nginx -y
 
# Obtain certificate (Nginx)
sudo certbot --nginx -d mysite.example.com -d www.mysite.example.com
 
# Obtain certificate (Apache)
sudo certbot --apache -d mysite.example.com
 
# Test auto-renewal
sudo certbot renew --dry-run
 
# Certbot auto-renewal timer (installed automatically)
sudo systemctl status certbot-renew.timer
 
# Manual renewal
sudo certbot renew

Cockpit Web UI

  • Cockpit is a browser-based server management interface — included in Rocky Linux.
# Install Cockpit
sudo dnf install cockpit -y
sudo systemctl enable --now cockpit.socket
 
# Open firewall
sudo firewall-cmd --permanent --add-service=cockpit
sudo firewall-cmd --reload
 
# Access at: https://your-server-ip:9090
# Login with your system username and password
 
# Install additional Cockpit modules
sudo dnf install cockpit-machines    # VM management (libvirt)
sudo dnf install cockpit-podman      # container management
sudo dnf install cockpit-storaged    # storage management
sudo dnf install cockpit-networkmanager  # network management

Podman — Rootless Containers

# Podman is available in Rocky Linux repos
sudo dnf install podman podman-compose -y
 
# Run a container (rootless — no sudo needed)
podman run -d --name nginx -p 8080:80 nginx:latest
 
# List containers
podman ps
podman ps -a    # including stopped
 
# Manage containers
podman stop nginx
podman start nginx
podman rm nginx
podman logs nginx
 
# Pull images
podman pull rockylinux:9
podman images
 
# Run Rocky Linux container
podman run -it rockylinux:9 bash
 
# Generate systemd unit for container
podman generate systemd --name nginx --files --new

More Learn

Official Resources

  • Fedora — upstream of RHEL; cutting-edge RPM-based distro
  • CentOS — predecessor to Rocky Linux; now CentOS Stream
  • Debian — different family (APT-based), also enterprise-friendly
  • Kali Linux — security-focused Debian-based distro
  • Ubuntu — popular Debian-based distro with LTS releases
  • Linux Advanced — advanced Linux administration topics

Key Concepts to Learn Next

  • SELinux deep dive → Linux Advanced
  • LVM (Logical Volume Manager) for flexible storage
  • Ansible automation for Rocky Linux fleet management
  • Podman + Kubernetes on Rocky Linux
  • Rocky Linux in AWS/GCP/Azure cloud environments
  • RHEL certification (RHCSA/RHCE) — Rocky is perfect for exam prep

YouTube Playlists

  • Search: “Rocky Linux tutorial” on YouTube
  • Search: “RHEL 9 administration” — applies directly to Rocky Linux
  • Search: “Linux Foundation RHCSA prep Rocky Linux”

Books & Courses

  • “RHCSA Red Hat Enterprise Linux 9” by Michael Jang — applies to Rocky
  • Red Hat Learning Subscription (concepts apply to Rocky)
  • Linux Foundation LFCS certification prep