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
dnfupdates) — 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 unregisterfirst, 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 | audit2whyto 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_keyson all accounts. - Review
/var/log/secureandjournalctl -u sshdregularly 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.