Skip to main content

Red Hat Enterprise Linux 9 — Release Summary & Best Practices

Red Hat Enterprise Linux 9 — Release Summary & Best Practices


Release Summary

Red Hat Enterprise Linux 9 (RHEL 9) was released in May 2022, based on CentOS Stream 9 and the Linux kernel 5.14. It is the current production workhorse for most enterprise Red Hat deployments and represents a significant modernization over RHEL 8 — particularly around security defaults, container tooling, and observability. RHEL 9 is currently in Full Support and is the recommended version for all new deployments.

Key Highlights

Area Detail
Kernel 5.14 base — updated per minor release (9.1 → 9.2 → 9.3 → 9.4)
Python Python 3.9 default; 3.11 available via module streams
Security SHA-1 deprecated in signatures; OpenSSH uses Ed25519 by default
Networking nftables default (iptables deprecated); NetworkManager mandatory
Storage XFS default; Stratis 2.x for advanced storage management
Init systemd 250
Containers Podman 4.x default; Buildah and Skopeo included
Web Console Cockpit with enhanced performance monitoring
Observability Performance Co-Pilot (PCP) deeply integrated
Support Lifecycle Full Support until 2027; Maintenance until 2032; ELS until 2035

Notable Changes from RHEL 8

  • nftables replaces iptables as the default packet filtering framework (iptables still available but deprecated)
  • ifcfg network scripts deprecated — use NetworkManager keyfiles instead (mandatory in RHEL 10)
  • OpenSSL 3.0 — significant API/ABI changes for compiled applications
  • SSH root login disabled by default
  • SHA-1 deprecated for digital signatures across all system crypto policies
  • nsswitch.conf now managed exclusively by authselect
  • ReaR (Relax-and-Recover) is the supported bare metal backup/recovery tool

Minor Release Timeline

Release Date Kernel
RHEL 9.0 May 2022 5.14.0-70
RHEL 9.1 Nov 2022 5.14.0-162
RHEL 9.2 May 2023 5.14.0-284
RHEL 9.3 Nov 2023 5.14.0-362
RHEL 9.4 May 2024 5.14.0-427
RHEL 9.5 Nov 2024 5.14.0-503

Installation Best Practices

  • Use Kickstart files for automated, reproducible installs — store in version control alongside your infrastructure code
  • During partitioning, separate /var, /tmp, /home, /boot, and /boot/efi onto dedicated logical volumes
  • Use the Minimal Install base environment for servers — add only required packages post-install
  • Register immediately after install: subscription-manager register --auto-attach
  • Apply all available updates before first use: dnf update -y && reboot
  • Disable unused services at install time — use systemctl disable or Kickstart services --disabled

Security Best Practices

System Crypto Policies

RHEL 9 uses a unified crypto policy framework — this is your primary lever for compliance:

# Check current policy
update-crypto-policies --show

# Set to FIPS (required for many regulatory frameworks)
update-crypto-policies --set FIPS
reboot  # Required after FIPS change

# Available policies: DEFAULT, FUTURE, FIPS, LEGACY (avoid)
  • Never use LEGACY policy in production — it re-enables weak ciphers including RC4 and DH < 1024-bit
  • FUTURE policy is stricter than DEFAULT — good for high-security environments
  • SHA-1 is deprecated but not fully disabled in RHEL 9 DEFAULT — use FUTURE or FIPS to disable it completely

SSH Hardening

# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
AllowGroups ssh-users
Banner /etc/issue.net
# Restart SSH after changes
systemctl restart sshd

# Verify configuration
sshd -T | grep -i 'permitroot\|passwordauth'

SELinux

# Verify status
getenforce        # Should return: Enforcing
sestatus          # Full status

# Never do this in production:
# setenforce 0
# sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

# Troubleshoot denials instead
ausearch -m avc -ts recent
audit2why < /var/log/audit/audit.log
audit2allow -a    # Review before applying
  • Always fix SELinux denials with proper policy — disabling is not a fix
  • Use semanage port -a -t http_port_t -p tcp 8443 to allow custom ports for known services
  • Enable dontaudit rules selectively to reduce noise without weakening policy

Firewall

# List current configuration
firewall-cmd --list-all

# Add a service permanently
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

# Add a custom port
firewall-cmd --permanent --add-port=8443/tcp
firewall-cmd --reload

# Remove a service (lock down defaults)
firewall-cmd --permanent --remove-service=dhcpv6-client
firewall-cmd --reload
  • Use zones to segment traffic — don't dump everything into the public zone
  • Block ICMP redirects and IP source routing: set kernel parameters via /etc/sysctl.d/
  • Enable connection tracking logging for audit trails

Kernel Hardening (sysctl)

# /etc/sysctl.d/99-hardening.conf
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.tcp_syncookies = 1
kernel.randomize_va_space = 2
kernel.dmesg_restrict = 1
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
# Apply without reboot
sysctl -p /etc/sysctl.d/99-hardening.conf

Networking Best Practices

RHEL 9 strongly favors NetworkManager — configure via nmcli, nmtui, or keyfiles:

# Show all connections
nmcli connection show

# Configure static IP
nmcli connection modify "Wired connection 1" \
  ipv4.method manual \
  ipv4.addresses 10.10.100.30/24 \
  ipv4.gateway 10.10.100.1 \
  ipv4.dns "10.10.100.10 10.10.100.11"

nmcli connection up "Wired connection 1"

# Disable IPv6 on a connection if required
nmcli connection modify eth0 ipv6.method disabled
  • Set hostname correctly: hostnamectl set-hostname server01.casino.local
  • Configure NTP via chrony: dnf install chrony && systemctl enable --now chronyd
  • Verify time sync: chronyc tracking and chronyc sources
  • Use bonding or teaming for NIC redundancy on physical hosts

Storage Best Practices

# Check filesystem usage
df -hT

# Extend an LVM logical volume + filesystem online
lvextend -L +10G /dev/mapper/rhel-var
xfs_growfs /var

# Check XFS filesystem health
xfs_info /var
xfs_repair -n /dev/sdb1   # -n = dry run, no changes

# Enable fstrim for SSDs
systemctl enable --now fstrim.timer
  • Mount /tmp on tmpfs for security and performance on stateless systems:
    systemctl enable --now tmp.mount
    
  • Use nodev, nosuid, noexec mount options on /tmp, /home, and removable media
  • Set disk quotas on /home if it's shared storage

System Management Best Practices

Patching with DNF

# List available security advisories
dnf updateinfo list security

# Apply security patches only
dnf update --security -y

# Apply a specific advisory
dnf update --advisory RHSA-2024:1234 -y

# Exclude a package from updates (use sparingly)
dnf update --exclude=kernel* -y

# Check history and roll back if needed
dnf history list
dnf history undo <ID>

Service Management

# List all failed services
systemctl --failed

# Check service status and recent logs
systemctl status sshd
journalctl -u sshd -n 50 --no-pager

# Enable and start a service
systemctl enable --now httpd

# Mask a service to prevent it from starting under any condition
systemctl mask bluetooth

Log Management

# View logs for a specific time range
journalctl --since "2024-01-01" --until "2024-01-02"

# Follow logs for a unit
journalctl -fu httpd

# Export logs to file
journalctl --since today > /tmp/today-logs.txt

# Check disk usage of journal
journalctl --disk-usage

Configure persistent logging:

# /etc/systemd/journald.conf
Storage=persistent
SystemMaxUse=2G
SystemKeepFree=500M
Compress=yes

Performance Monitoring

# Quick system overview
top
htop    # if installed

# CPU/IO stats
iostat -xz 5
vmstat 5

# Memory details
free -h
cat /proc/meminfo

# Performance Co-Pilot (built into RHEL 9)
dnf install pcp pcp-system-tools
systemctl enable --now pmcd
pmstat        # System-level stats
pmrep         # Report-format metrics

Container Best Practices (Podman 4.x)

# Pull and run a container
podman pull registry.access.redhat.com/ubi9/ubi:latest
podman run -it --rm ubi9/ubi:latest bash

# Run as a systemd service (rootful)
podman run -d --name nginx -p 80:80 nginx:latest
podman generate systemd --name nginx --files --new
cp container-nginx.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now container-nginx

# Rootless container
podman run -d --name myapp --userns=keep-id myimage:latest
  • Use Red Hat Universal Base Images (UBI) as your base — supported, scannable, redistributable
  • Scan images before deployment: podman scan myimage:latest (requires subscription)
  • Use SELinux labels for container volumes: podman run -v /data:/data:Z myimage
  • Enable auto-update for containers: podman auto-update with io.containers.autoupdate=registry label

Automation Integration

# Install RHEL System Roles
dnf install rhel-system-roles

# Example: Apply timesync role
cat > timesync.yml << 'EOF'
---
- hosts: all
  roles:
    - rhel-system-roles.timesync
  vars:
    timesync_ntp_servers:
      - hostname: time.casino.local
        iburst: yes
EOF

ansible-playbook -i inventory timesync.yml
  • Use Red Hat Insights for proactive risk identification — insights-client --register
  • Integrate with Ansible Automation Platform for at-scale patching, hardening, and drift remediation
  • Use OpenSCAP for automated compliance scanning:
    dnf install openscap-scanner scap-security-guide
    oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis \
      --results results.xml \
      /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
    

RHEL 9 support lifecycle: Full Support until 2027 · Maintenance until 2032 · Extended Life until 2035