Red Hat Enterprise Linux 9 (RHEL 9, codenamed “Plow”) is the most significant release of RHEL in years, built on Linux kernel 5.14, OpenSSL 3.0, and shipped with a hardened-by-default security posture including SELinux enforcing mode, nftables as the default firewall backend, and full support for the latest POWER, ARM, and x86_64 architectures. Whether you are running it on bare metal, in a virtual machine, or as a cloud instance on AWS, Azure, or GCP, getting the initial configuration right is the foundation for everything that follows.

In this guide you will perform the complete post-installation configuration of a freshly deployed RHEL 9 server: setting a hostname, creating an administrative user, locking down SSH, synchronising time, configuring automatic security updates, and verifying SELinux is enforcing. By the end you will have a production-ready baseline you can build on for any workload.

This tutorial assumes you have just finished the RHEL 9 installation and can log in as root either directly or via the console. All commands are run as root unless stated otherwise.

Prerequisites

  • A freshly installed RHEL 9 (9.x) server — bare metal, VM, or cloud instance
  • Root or sudo access
  • Active Red Hat subscription (for dnf updates) — or a no-cost developer subscription from developers.redhat.com
  • At least 1 GB RAM and 20 GB disk space
  • A public IP or NAT-reachable network interface

Step 1 — Register Your Subscription

RHEL 9 uses the Red Hat Subscription Manager (RHSM) to access package repositories. Register your system before running any updates.

subscription-manager register --username YOUR_REDHAT_USERNAME --password YOUR_PASSWORD --auto-attach

The --auto-attach flag automatically finds and attaches the best-fit subscription. On a developer subscription this attaches a 16-system RHEL Developer Subscription. Confirm the subscription is active:

subscription-manager status

Expected output: Overall Status: Current. If you see Insufficient, run subscription-manager attach --auto again.

Step 2 — Apply All System Updates

The first action on any new server is to bring all packages up to date. RHEL 9 uses dnf, which replaced yum.

dnf update -y

Reboot after the update to activate any new kernel:

reboot

Verify you are running the updated kernel:

uname -r

Step 3 — Set the Hostname and FQDN

RHEL 9 uses hostnamectl which updates both /etc/hostname and the running system atomically.

hostnamectl set-hostname web01.example.com

Verify the change:

hostnamectl status

Update /etc/hosts so the hostname resolves locally without DNS:

echo "203.0.113.10  web01.example.com  web01" >> /etc/hosts

Replace 203.0.113.10 with your server’s actual IP address.

Step 4 — Create an Administrative User

Direct root login should be disabled after setup. Create a dedicated admin user who can use sudo for privileged commands.

useradd -m -G wheel -s /bin/bash adminuser
passwd adminuser

The -G wheel flag adds the user to the wheel group, which RHEL 9 grants full sudo access by default. Confirm sudo works before disabling root:

su - adminuser -c "sudo whoami"

Expected output: root

Step 5 — Configure SSH Key Authentication and Disable Root Login

On your local workstation, generate an Ed25519 key pair:

ssh-keygen -t ed25519 -C "adminuser@web01" -f ~/.ssh/web01_ed25519

Copy your public key to the server:

ssh-copy-id -i ~/.ssh/web01_ed25519.pub [email protected]

On the server, edit /etc/ssh/sshd_config and set:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 30

Restart SSH:

systemctl restart sshd

Important: Open a second terminal and verify key login works before closing your current session.

Step 6 — Configure the Firewall with firewalld

systemctl enable --now firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
firewall-cmd --list-all

Step 7 — Verify SELinux is Enforcing

getenforce

Expected output: Enforcing. If it shows Permissive, enable it:

setenforce 1
grep ^SELINUX= /etc/selinux/config

The file must read SELINUX=enforcing for persistence across reboots.

Step 8 — Synchronise Time with Chrony

systemctl enable --now chronyd
chronyc tracking
chronyc makestep

Step 9 — Set the Timezone

timedatectl list-timezones | grep Europe
timedatectl set-timezone Europe/London
timedatectl status

Step 10 — Enable Automatic Security Updates

dnf install -y dnf-automatic

Edit /etc/dnf/automatic.conf and set:

[commands]
upgrade_type = security
apply_updates = yes

[emitters]
emit_via = stdio
systemctl enable --now dnf-automatic.timer
systemctl list-timers dnf-automatic.timer

Step 11 — Configure a Basic Sudoers Policy

Review the default sudoers to ensure only wheel group members can elevate:

visudo

Confirm this line is present and uncommented:

%wheel  ALL=(ALL)  ALL

And this line is commented out (it would allow passwordless sudo, which is insecure for human accounts):

# %wheel  ALL=(ALL)  NOPASSWD: ALL

Step 12 — Set a Login Banner

A legal warning banner deters unauthorised access and is required in many compliance frameworks (PCI-DSS, HIPAA, SOC 2):

cat > /etc/issue.net << 'EOF'
***********************************************************************
* AUTHORISED ACCESS ONLY                                              *
* This system is for the use of authorised users only.               *
* Individuals using this system without authority, or in excess of   *
* their authority, are subject to having their activities monitored  *
* and recorded. Unauthorised use is a criminal offence.              *
***********************************************************************
EOF

Enable the banner in /etc/ssh/sshd_config:

Banner /etc/issue.net
systemctl restart sshd

Verification

# Confirm hostname
hostname -f

# Confirm SSH config is correct
sshd -T | grep -E "permitrootlogin|passwordauthentication|pubkeyauthentication"

# Confirm firewall is running with correct services
firewall-cmd --list-all

# Confirm SELinux
getenforce

# Confirm time sync
chronyc tracking

# Confirm automatic updates timer
systemctl is-active dnf-automatic.timer

Troubleshooting

  • subscription-manager register fails with “system is already registered” — run subscription-manager unregister first, then re-register.
  • SSH locked out after disabling password auth — access the VM console, restore PasswordAuthentication yes, restart sshd, copy your public key correctly, then re-disable.
  • SELinux blocking a service — use ausearch -m avc -ts recent | audit2why to diagnose. Never disable SELinux to fix an application issue.

Security Considerations

  • Leave SELinux in enforcing mode at all times. Use targeted policies to resolve denials, not SELINUX=disabled.
  • Restrict SSH source IPs with firewalld rich rules if your admin IP range is static.
  • Rotate SSH keys periodically and audit ~/.ssh/authorized_keys on all accounts.
  • Review /var/log/secure and journalctl -u sshd regularly for unusual login attempts.

Conclusion

You now have a fully configured RHEL 9 server baseline: registered, fully patched, with a named admin user, SSH key authentication only, firewalld configured, SELinux enforcing, time synchronised, and automatic security updates active. This baseline is the prerequisite for every other tutorial in this RHEL 9 series.

Next steps: How to Add and Delete Users on RHEL 9, How to Configure the Firewall on RHEL 9 with firewalld, and How to Set Up SSH Key-Based Authentication on RHEL 9.