RHCSA command cheat sheet
The commands an EX200 task actually needs, grouped by objective domain - software, users, storage, file systems, services, networking and SELinux. Every output is from a real session on a RHEL 10.0 machine.
- OSRHEL 10.0 (Coughlan)
- Kernel6.12.0-55.9.1.el10_0
- dnf4.20.0
- Flatpak1.16.0
- Commands39
- Reviewed23 August 2026
Finding out what you need
-
dnf provides */semanageWhich package ships a command. The `*/` matters - without a slash dnf reads the argument as a package name.
bash Example session sudo dnf provides */semanage 2>&1 | grep -E "^[a-z].*:|Repo|Provide" | head -8policycoreutils-python-utils-3.8-1.el10.noarch : SELinux policy core python utilitiesRepo : @Systempolicycoreutils-python-utils-3.8-1.el10.noarch : SELinux policy core python utilitiesRepo : rhel10-appstream -
rpm -qc <package>A package's configuration files. The fastest answer to "where does this service keep its config".
bash Example session rpm -qc chrony/etc/chrony.conf/etc/chrony.keys/etc/logrotate.d/chrony/etc/sysconfig/chronyd -
man -k <word>Search one-line descriptions only. Searching the word a task uses often fails - search the word the documentation would use.
bash Example session man -k chage 2>&1 | head -2; whatis chagechage (1) - change user password expiry informationchage (1) - change user password expiry information -
namei -l <path>Every component of a path with its mode. Finds a denial three directories above the file.
bash Example session sudo -u pfa namei -l /srv/proj/team/reports/q3.txtf: /srv/proj/team/reports/q3.txtdr-xr-xr-x root root /drwxr-xr-x root root srvdrwxrwx--- root proj projdrwx------ root proj team reports - Permission denied[exit 1]
Software
-
dnf makecache --repo <id>The only way to verify a repository. `dnf repolist` lists a broken repo as happily as a working one.
bash Example session sudo dnf makecache --repo rhel10-extra 2>&1 | tail -6Updating Subscription Management repositories.RHEL 10 - a second view of BaseOS 76 kB/s | 196 B 00:00Errors during downloading metadata for repository 'rhel10-extra': - Status code: 404 for http://192.168.0.32/rhel10/NoSuchDir/repodata/repomd.xml (IP: 192.168.0.32)Error: Failed to download metadata for repo 'rhel10-extra': Cannot download repomd.xml: Cannot download repodata/repomd.xml: All mirrors were tried -
dnf install ./file.rpmA local package WITH dependency resolution. `rpm -ivh` refuses on unmet dependencies. The `./` is required.
bash Example session cd ~/rpms && sudo dnf -y install ./sysstat*.rpm 2>&1 | tail -14 Installing : sysstat-12.7.6-2.el10.x86_64 4/4 Running scriptlet: sysstat-12.7.6-2.el10.x86_64 4/4Created symlink '/etc/systemd/system/multi-user.target.wants/sysstat.service' → '/usr/lib/systemd/system/sysstat.service'.Created symlink '/etc/systemd/system/sysstat.service.wants/sysstat-collect.timer' → '/usr/lib/systemd/system/sysstat-collect.timer'.Created symlink '/etc/systemd/system/sysstat.service.wants/sysstat-summary.timer' → '/usr/lib/systemd/system/sysstat-summary.timer'.Created symlink '/etc/systemd/system/sysstat.service.wants/sysstat-rotate.timer' → '/usr/lib/systemd/system/sysstat-rotate.timer'. -
dnf history undo lastReverse a transaction. The undo is itself logged, so it can be undone.
bash Example session sudo dnf -y history undo last 2>&1 | tail -8 Erasing : tree-2.1.0-8.el10.x86_64 1/1 Running scriptlet: tree-2.1.0-8.el10.x86_64 1/1Installed products updated. Removed: -
flatpak remote-add --if-not-exists <name> <url>Configure access to a Flatpak repository - one of the two new RHEL 10 objectives. Needs sudo for a system remote.
bash Example session sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo 2>&1 | tail -2; echo "exit=$?"exit=0
Users, groups and sudo
-
useradd -u <uid> -c "<comment>" -s <shell> -d <home> -m <name>An account built to specification. `-m` is required with a custom `-d`.
bash Example session sudo useradd -u 5000 -c "Bob the Builder" -s /sbin/nologin -d /home/bobhome -m bob && grep bob /etc/passwdbob:x:5000:5000:Bob the Builder:/home/bobhome:/sbin/nologin -
usermod -aG <group> <user>ALWAYS -aG. Plain -G replaces every secondary group, silently, with exit 0.
bash Example session sudo usermod -aG devs carol && id caroluid=1001(carol) gid=1002(carol) groups=1002(carol),1003(devs) -
chage -M <max> -m <min> -W <warn> <user>Password ageing. `-E` is a different thing: an absolute account expiry date.
bash Example session sudo chage -M 60 -m 7 -W 14 dave && sudo chage -l daveLast password change : Aug 23, 2026Password expires : Oct 22, 2026Password inactive : neverAccount expires : neverMinimum number of days between password change : 7Maximum number of days between password change : 60Number of days of warning before password expires : 14 -
chage -l <user>Read the ageing policy with the dates decoded. What a grader would read.
bash Example session sudo chage -l daveLast password change : Aug 23, 2026Password expires : neverPassword inactive : neverAccount expires : neverMinimum number of days between password change : 0Maximum number of days between password change : 99999Number of days of warning before password expires : 7 -
visudo -cValidate sudoers. The ONLY reliable signal - a typo produces a bogus rule and sudo keeps working.
bash Example session sudo visudo -c/etc/sudoers: parsed OK/etc/sudoers.d/frank: parsed OK/etc/sudoers.d/sysadmin: parsed OK
Storage
-
parted -s -a optimal <disk> mkpart <name> <start> <end>A GPT partition. parted writes immediately - there is no save step and no undo.
bash Example session sudo parted -s -a optimal /dev/sda mkpart data1 1MiB 3GiB && sudo parted -s /dev/sda print | tail -4 Number Start End Size File system Name Flags 1 1049kB 3221MB 3220MB data1 -
wipefs -a <partition>Clear a stale filesystem signature. On the DISK it does not reach inside where partitions used to be.
bash Example session sudo wipefs -a /dev/sda1 && sudo blkid /dev/sda1 2>&1; echo "exit=$? - nothing left to report"/dev/sda1: 4 bytes were erased at offset 0x00000000 (xfs): 58 46 53 42/dev/sda1: PARTLABEL="data1" PARTUUID="c1447c2e-9eed-4410-b0b1-68359d39f1d6"exit=0 - nothing left to report -
pvcreate <dev> · vgcreate <vg> <dev> · vgextend <vg> <dev>Build and grow a volume group. Growing needs no downtime.
bash Example session sudo vgextend vgdata /dev/sda2 && sudo vgs vgdata; sudo pvs WARNING: adding device /dev/sda2 with idname naa.6002248044ac92640128d3a49d3f82e4 which is already used for /dev/sda. Volume group "vgdata" successfully extended VG #PV #LV #SN Attr VSize VFree vgdata 2 0 0 wz--n- 5.99g 5.99g PV VG Fmt Attr PSize PFree /dev/sda1 vgdata lvm2 a-- <3.00g <3.00g /dev/sda2 vgdata lvm2 a-- <3.00g <3.00g /dev/sdb3 rhel lvm2 a-- <49.00g 0 -
lvcreate -L <size> -n <name> <vg> · -l 100%FREESizes round UP to a whole extent, and LVM says so. `-l` takes extents or a percentage.
bash Example session sudo lvcreate -L 101M -n lvround vgdata && sudo lvs -o lv_name,lv_size vgdata Rounding up size to full physical extent 104.00 MiB Logical volume "lvround" created. LV LSize lvlogs 200.00m lvround 104.00m lvweb 1.00g -
lvextend -r -L +<size> <lv>The one to memorise. `-r` grows the filesystem too, while mounted. Without it df does not change.
bash Example session sudo lvextend -r -L +200M /dev/vgdata/lvgrow 2>&1 | tail -4data blocks changed from 153600 to 204800xfs_growfs done Extended file system xfs on vgdata/lvgrow. Logical volume vgdata/lvgrow successfully resized. -
mkswap <dev> · swapon <dev> · swapon -a`swapon -a` reads fstab, which is what boot does - so it is the way to test a swap entry without rebooting.
bash Example session sudo swapon -a && swapon --show; echo "--- swapon -a reads fstab, which is what boot does"NAME TYPE SIZE USED PRIO/dev/dm-1 partition 3.9G 108K -2/dev/dm-4 partition 512M 0B -3--- swapon -a reads fstab, which is what boot does
File systems and mounting
-
mkfs.xfs · mkfs.ext4 · mkfs.vfat -F 32XFS is the RHEL default and can only ever GROW. Size it generously.
bash Example session sudo mkfs.xfs -f /dev/vgdata/lvxfs 2>&1 | head -10meta-data=/dev/vgdata/lvxfs isize=512 agcount=4, agsize=25600 blks = sectsz=4096 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=1 = reflink=1 bigtime=1 inobtcount=1 nrext64=1 = exchange=0data = bsize=4096 blocks=102400, imaxpct=25 -
blkid -s UUID -o value <dev>Just the UUID, for a command substitution. Never type a UUID by hand.
bash Example session sudo blkid -s UUID -o value /dev/vgdata/lvweb98bb9ad4-09ea-4cba-8a32-7d316f1c3d63 -
findmnt --verifyChecks every fstab entry without mounting. Run it before every reboot - a bad fstab loses the whole exam.
bash Example session sudo findmnt --verify --verbose 2>&1 | tail -8 [ ] source /dev/mapper/rhel-swap exists [ ] FS type is swap/data [ ] target exists [ ] LABEL=applogs translated to /dev/mapper/vgdata-lvweb [ ] source /dev/mapper/vgdata-lvweb exists [ ] FS type is xfsSuccess, no errors or warnings detected -
showmount -e <server>What a server exports. Comes with nfs-utils, which is NOT installed by default.
bash Example session showmount -e rhcsa-util01Export list for rhcsa-util01:/srv/nfsshare 192.168.0.0/24/srv/nfsrw 192.168.0.0/24 -
chmod 2770 <dir> · 3770 adds stickySGID so new files take the directory group; sticky so members cannot delete each other's files.
bash Example session sudo chmod 3770 /srv/proj/team && sudo ls -ld /srv/proj/teamdrwxrws--T. 3 root proj 37 Aug 23 13:19 /srv/proj/team
Services, scheduling and logs
-
systemctl enable --now <unit>Both halves in one command. `start` alone is the single most expensive mistake on this exam.
bash Example session sudo systemctl enable --now httpd 2>&1 | tail -2; systemctl is-active httpd; systemctl is-enabled httpdCreated symlink '/etc/systemd/system/multi-user.target.wants/httpd.service' → '/usr/lib/systemd/system/httpd.service'.activeenabled -
systemctl show <unit> -p ActiveState -p UnitFileStateThe two facts, unambiguously named. Running and enabled are unrelated.
bash Example session systemctl show httpd -p ActiveState -p UnitFileStateActiveState=activeUnitFileState=enabled -
systemctl list-timersNext and last run for every timer. cron has no equivalent.
bash Example session systemctl list-timers certgrid-demo.timer --no-pagerNEXT LEFT LAST PASSED UNIT ACTIVATESSun 2026-08-23 13:40:00 UTC 1min 37s - - certgrid-demo.timer certgrid-demo.service 1 timers listed.Pass --all to see loaded but inactive timers, too. -
systemd-analyze calendar '<expr>'Test a schedule BEFORE relying on it. A cron mistake is only discovered by the job not running.
bash Example session systemd-analyze calendar '*:0/10' 2>&1 | head -4 Original form: *:0/10Normalized form: *-*-* *:00/10:00 Next elapse: Sun 2026-08-23 13:40:00 UTC From now: 1min 37s left -
journalctl -b -1 -p errErrors from the previous boot. Needs a persistent journal, which is not the default.
bash Example session journalctl -b -1 -p err --no-pager -n 5; echo "exit=$? - errors from the PREVIOUS boot, which is the whole point of preserving it"Aug 23 13:32:04 rhcsa-a01 sshd-session[1970]: error: mm_reap: preauth child terminated by signal 15Aug 23 13:32:05 rhcsa-a01 dbus-broker-launch[830]: Activation request for 'org.freedesktop.nm_dispatcher' failed.Aug 23 13:32:05 rhcsa-a01 auditd[795]: plugin /usr/sbin/sedispatch terminated unexpectedlyexit=0 - errors from the PREVIOUS boot, which is the whole point of preserving it
Networking and the firewall
-
nmcli con mod <name> ipv4.method manual ipv4.addresses <a/p> ipv4.gateway <g>Edits the profile ONLY. Nothing changes until `nmcli con up`.
bash sudo nmcli con mod demo0 ipv4.addresses 10.99.0.20/24 ipv4.gateway 10.99.0.1 ipv4.dns "192.168.0.10 192.168.0.11" ipv4.method manual -
nmcli con mod <name> +ipv4.addresses <a/p>The `+` APPENDS. Plain assignment replaces the whole list - the same trap as usermod -G.
bash Example session sudo nmcli con mod demo0 +ipv4.addresses 10.99.1.20/24 && sudo nmcli con up demo0 >/dev/null 2>&1; ip -4 addr show demo0 | grep inet inet 10.99.0.20/24 brd 10.99.0.255 scope global noprefixroute demo0 inet 10.99.1.20/24 brd 10.99.1.255 scope global noprefixroute demo0 -
getent hosts <name>Resolution the way an application sees it. `dig` and `host` ignore /etc/hosts and will lie to you.
bash Example session getent hosts rhcsa-util01; getent ahosts rhcsa-util01 | head -3192.168.0.32 rhcsa-util01192.168.0.32 STREAM rhcsa-util01192.168.0.32 DGRAM192.168.0.32 RAW -
firewall-cmd --add-service=<name> --permanent && firewall-cmd --reloadBoth, always. `--permanent` alone does not change the running firewall; without it the rule dies at the next reload.
bash Example session sudo firewall-cmd --add-service=http --permanent && sudo firewall-cmd --reload >/dev/null && sudo firewall-cmd --list-services; sudo firewall-cmd --list-services --permanentsuccesscockpit dhcpv6-client http sshcockpit dhcpv6-client http ssh -
firewall-cmd --list-allThe complete state of the active zone. The one command to verify any firewall task.
bash Example session sudo firewall-cmd --remove-service=http --permanent >/dev/null 2>&1; sudo firewall-cmd --remove-service=https --permanent >/dev/null 2>&1; sudo firewall-cmd --remove-port=9876/tcp --permanent >/dev/null 2>&1; sudo firewall-cmd --delete-zone=certgrid --permanent >/dev/null 2>&1; sudo firewall-cmd --reload >/dev/null 2>&1; sudo firewall-cmd --list-allpublic (default, active) target: default ingress-priority: 0 egress-priority: 0 icmp-block-inversion: no interfaces: eth0 sources: services: cockpit dhcpv6-client ssh ports: protocols: forward: yes masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
SELinux
-
semanage fcontext -a -t <type> "<path>(/.*)?"Writes the RULE. It does not relabel anything - the file is unchanged until restorecon.
bash Example session sudo semanage fcontext -a -t httpd_sys_content_t "/webroot(/.*)?" && sudo semanage fcontext -l -C 2>/dev/nullSELinux fcontext type Context /webroot(/.*)? all files system_u:object_r:httpd_sys_content_t:s0 -
restorecon -Rv <path>Applies policy to files. The second half of every context fix, and it undoes any chcon.
bash Example session sudo restorecon -Rv /webroot; ls -Z /webroot/index.htmlRelabeled /webroot from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0Relabeled /webroot/index.html from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0unconfined_u:object_r:httpd_sys_content_t:s0 /webroot/index.html -
semanage port -a -t <type>_port_t -p tcp <port>Permission denied binding a FREE high port as root is an SELinux port label, not a conflict.
bash Example session sudo semanage port -a -t http_port_t -p tcp 9876 && sudo semanage port -l 2>/dev/null | grep -E "^http_port_t"http_port_t tcp 9876, 80, 81, 443, 488, 8008, 8009, 8443, 9000 -
setsebool -P <boolean> onThe `-P` is the whole thing. Without it the change is runtime only.
bash Example session sudo setsebool -P httpd_enable_homedirs on && sudo semanage boolean -l -C 2>/dev/null; getsebool httpd_enable_homedirsSELinux boolean State Default Description httpd_can_network_connect (off , off) Allow httpd to can network connecthttpd_enable_homedirs (on , on) Allow httpd to enable homedirsvirt_sandbox_use_all_caps (on , on) Allow virt to sandbox use all capsvirt_use_nfs (on , on) Allow virt to use nfshttpd_enable_homedirs --> on -
ausearch --input-logs -m AVC -ts recentWITH --input-logs. The bare form returned no matches on this machine while 16 denials sat in the log.
bash Example session sudo ausearch --input-logs -m AVC -ts recent 2>&1 | tail -8type=PROCTITLE msg=audit(1787495025.353:16872): proctitle=2F7573722F7362696E2F6874747064002D44464F524547524F554E44type=SYSCALL msg=audit(1787495025.353:16872): arch=c000003e syscall=262 success=no exit=-13 a0=ffffff9c a1=7f48cc03b448 a2=7f48d29219f0 a3=0 items=0 ppid=29918 pid=29921 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" subj=system_u:system_r:httpd_t:s0 key=(null)type=AVC msg=audit(1787495025.353:16872): avc: denied { getattr } for pid=29921 comm="httpd" path="/webroot/index.html" dev="dm-0" ino=35610097 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0----time->Sun Aug 23 14:23:45 2026type=PROCTITLE msg=audit(1787495025.353:16873): proctitle=2F7573722F7362696E2F6874747064002D44464F524547524F554E44
No command matches that search.