Red Hat Certified Engineer (RHCE) #18: Exam Tips and Time Management
From #1 to #17, we covered every exam domain of RHCE. This post is a condensed cheat sheet to read once more right before you walk into the hands-on exam. There’s no new syntax here — only how you run the four-hour exam and the traps that most often bleed points across the whole series. RHCE isn’t an exam where you type commands by hand; it’s an exam where you write idempotent playbooks from an empty control node to configure multiple managed nodes, so even with the same knowledge, your operating approach decides pass from fail.
A four-hour operating strategy #
It’s all automation, so setting up the environment comes first #
What makes RHCE decisively different from CKA and RHCSA is that every task converges onto playbooks on a single control node. It isn’t an operation where you switch to a new cluster or SSH to a different node for each task — instead, you set up the environment once right after the exam starts, then keep piling tasks on top of it. So the environment setup you invest in the first 10–15 minutes decides the pace of all four hours.
| Stage | Time | Action |
|---|---|---|
| Environment setup | ~15 min | Set up inventory, ansible.cfg, SSH keys, become, working directory; confirm ping to all hosts |
| First pass | ~3 hrs | Pile up tasks/playbooks starting with the work your hands know. Run --syntax-check and --check each time you write |
| Second pass | ~20 min | Retry the tasks you got stuck on, shore up tasks that break idempotency |
| Verification | ~20 min | Run the whole playbook twice to confirm changed 0, check persistence and reboot survival |
Operate by piling up tasks #
In the RHCE environment, even when the work is split across several questions, it all ends up sharing inventory and ansible.cfg within a single working directory. Make a separate playbook per question, but let the ansible.cfg you pin once at the start carry all the inventory, become, and ssh settings. Not re-pinning the same settings for each question saves the most time.
# First confirm ansible.cfg is recognized in the working directory
ansible --version | grep "config file"
# Whether every host in the inventory responds (once, right after setup)
ansible all -m pingThe location of ansible.cfg ties directly to your score. The ansible.cfg in the current working directory is read first, so if you run a playbook outside the working directory, the inventory and become you intended won’t apply. At the start, place ansible.cfg in the working directory and get the habit of running playbooks only from within it.
# ansible.cfg in the working directory (pinned once)
[defaults]
inventory = ./inventory
remote_user = devops
become = true
[privilege_escalation]
become = true
become_method = sudo
become_user = rootRun –syntax-check and –check often #
If you write a whole playbook and run it all at once, you only discover YAML indentation accidents or module argument errors at the very end. The habit of checking syntax with --syntax-check and behavior with --check in advance every few tasks sharply cuts debugging time.
# Quickly validate only the YAML/playbook syntax (does not run)
ansible-playbook play.yml --syntax-check
# Preview what will change without making real changes (dry-run)
ansible-playbook play.yml --check
# Use tags to quickly run only specific tasks
ansible-playbook play.yml --tags web--check may not be accurate for some modules, but for the vast majority of work it safely shows “what is this playbook trying to change.” For a task you’re stuck on, turn on verbose logs with -vvv to pinpoint immediately which argument is failing.
Don’t over-invest in a single task #
The passing line is 210/300 (70%). For a task that takes a long time relative to its points, first build out the tasks as far as you can, then move on. The most common failure is getting stuck on a task whose module you can’t recall while missing several tasks your hands know well. There’s a way to solve unknown modules with ansible-doc, covered later, so when you’re stuck, flag it and make progress first.
Use ansible-doc as a weapon #
RHCE has no internet. Only local man pages and ansible-doc are allowed, so your ability to solve unknown modules is exactly your ability to read ansible-doc quickly.
# Search by keyword when you don't know the module
ansible-doc -l | grep -i firewall
# The module's full options and description
ansible-doc ansible.posix.firewalld
# Jump straight to just the EXAMPLES at the bottom of the doc (the fastest route)
ansible-doc ansible.posix.firewalld | grep -A 40 EXAMPLESThe core of this approach is copying the EXAMPLES and adapting them. Rather than writing module options from memory, paste the EXAMPLES block from the bottom of ansible-doc into your playbook and change only the values to match the task conditions. The FQCN (e.g., ansible.posix.firewalld) also appears as-is in the ansible-doc -l search results, so you can find the exact module name even when you can’t remember the collection name.
# Which collections are installed (to confirm FQCN)
ansible-galaxy collection listMake active use of the system roles example docs #
rhel-system-roles is a high-value domain that comes up often on RHCE, and the example playbook for each role is installed locally. You can lift the examples under /usr/share/doc and use them as-is without internet, so for system roles work it’s faster and safer to copy and adapt the examples than to write from scratch.
# Check the installed system roles
ls /usr/share/ansible/roles/
# Location of per-role example playbooks and docs
ls /usr/share/doc/rhel-system-roles/
# For example, the timesync (chronyd) role's example
ls /usr/share/doc/rhel-system-roles/timesync//usr/share/doc/rhel-system-roles/<role>/ contains a README along with a ready-to-run example playbook. If the task is “configure chronyd with system roles,” copy that example and change only values like the NTP server address to match the task conditions. There’s no need to memorize role variable names — just refer to the variable table in the README as-is.
Verify idempotency with two runs #
The crux of RHCE grading is whether running the same playbook multiple times gives the same result. The grading system often reruns the playbook to confirm idempotency, so you must run a playbook you’ve written twice and confirm that changed is 0 on the second run.
# First run: changes happen (changed occurs)
ansible-playbook play.yml
# Second run: nothing should change (changed=0 means idempotent)
ansible-playbook play.ymlIf changed remains on the second run, that task isn’t idempotent. The most common cause is the command and shell modules. These two run every time, so when possible switch to a dedicated module, and when unavoidable, pin idempotency yourself with creates/removes or changed_when.
# Control idempotency directly when you have no choice but to use command
- name: Run the init script only once
ansible.builtin.command: /opt/setup.sh
args:
creates: /opt/.setup_done
- name: Mark a command that only reads output as not-changed
ansible.builtin.command: cat /etc/redhat-release
register: rel
changed_when: falsePatterns people often get wrong #
These are the recurring patterns that bleed points in the hands-on exam. More points are lost to operating mistakes than to lack of knowledge.
1) Missing FQCN #
In recent RHCE environments, writing a module by its short name can fail because the collection isn’t found. Write the FQCN as-is: ansible.posix.firewalld instead of firewalld, ansible.builtin.setup instead of setup. Confirm the exact FQCN with ansible-doc -l.
# Use the FQCN instead of the short name
- name: Install a package
ansible.builtin.dnf:
name: httpd
state: present2) Omitting become #
If a task that needs root privileges (package install, service control, modifying system files) lacks become, it fails with a permission error. Pin it globally in ansible.cfg, but recheck where it’s needed at the play or task level.
- name: Configure the system
hosts: webservers
become: true # privilege escalation for the whole play
tasks: ...3) Not handling command/shell idempotency #
command and shell are caught as changed on every run, breaking idempotency. Replace them with a dedicated module, or when unavoidable, control them with creates/removes/changed_when (see the example in the earlier section). If only this task remains changed on the second run, you lose idempotency points in grading.
4) Missing the Vault password file #
A playbook that uses variables encrypted with Vault needs a password at run time. Rather than typing --ask-vault-pass every time, create a password file and register its path in ansible.cfg to reduce mistakes.
# ansible.cfg
[defaults]
vault_password_file = ./vault_pass.txt# Encrypt/edit the variable file
ansible-vault encrypt secret.yml
ansible-vault edit secret.yml5) mount needs state=mounted #
In a filesystem mount task, state: present only adds the /etc/fstab entry and does not mount it right now. The exam usually requires “mount now and persist after reboot,” so use state: mounted. Confusing the difference between present and mounted loses you the persistence points.
- name: Mount NFS now and register it in fstab
ansible.posix.mount:
path: /mnt/data
src: nfs.example.com:/export
fstype: nfs
state: mounted # present is fstab only, mounted is now + persistent6) handler notify name mismatch #
If the name in notify differs from the handler’s name by even one character, the handler isn’t called and the service isn’t restarted. It’s a recurring cause of changing a config without it taking effect. Copy the two and make them match exactly.
tasks:
- name: Deploy the config
ansible.builtin.template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify: restart httpd # must match the handler name below exactly
handlers:
- name: restart httpd
ansible.builtin.service:
name: httpd
state: restarted7) Not running –syntax-check #
If you write a playbook and run it right away, you hit YAML indentation or module argument errors mid-run. Bundle into one habit the practice of validating syntax with --syntax-check and behavior with --check every few tasks.
8) Confusion over the ansible.cfg location #
If you run a playbook outside the working directory, the ansible.cfg you intended isn’t read, dropping inventory, become, and remote_user. Run every playbook from inside the working directory that holds ansible.cfg, and confirm which config is read from the config file line of ansible --version.
Easily confused concept pairs #
Here are the pairs you can momentarily confuse mid-task, compressed into one-line differences.
| Pair | One-line difference |
|---|---|
| state present vs mounted | Adds only the fstab entry (doesn’t mount now) vs mounts now and registers in fstab |
| import vs include | Static: expanded at parse time (tags/handlers are visible ahead) vs dynamic: expanded at run time (controlled wholesale with when) |
| copy vs template | Copies the file as-is vs substitutes Jinja2 variables, renders, then deploys |
| become vs remote_user | Privilege escalation via sudo (usually root) vs specifying the SSH login account |
| changed_when vs failed_when | Decides changed status directly vs decides failed status directly |
| ansible-playbook vs ansible-navigator | The traditional runner (lightweight) vs the execution environment container runner (run -m stdout) |
import_* and include_* differ in when they expand. import expands statically at the time the playbook is read, so the tags and handlers of its tasks are visible ahead, whereas include expands dynamically mid-run, letting you conditionally control the whole block with when/loop. In dynamic situations where variables are decided at run time, use include; otherwise import is the safe choice.
copy and template part ways on variable substitution. For a config file whose values must differ per host, render Jinja2 with template; for a file with fixed content, send it as-is with copy. When you see the condition “different values per host” in the exam, it’s template.
Per-domain checklist for the moment before you sit #
These are the core modules and procedures your hands should produce immediately in each domain.
Environment, inventory, connection #
- inventory: static hosts/groups, separating
group_vars/host_vars - ansible.cfg: place in the working directory, register inventory, remote_user, become
- Connection check:
ansible all -m ping, the config file line ofansible --version - ad-hoc:
ansible <host> -m <module> -a "..."for on-the-spot checks
Playbooks, variables, templates #
- Idempotency: modules first,
creates/changed_whenfor command/shell - Variable precedence,
setup(facts)/ansible_facts, custom facts - template: Jinja2 filters,
when/loop, distinguished from copy - handler: the
notifyname and the handlernamematch exactly
Control flow, error handling, security #
-
block/rescue/always,failed_when/ignore_errors -
when/loop/until, partial runs withtags - Vault:
ansible-vault encrypt/edit, registervault_password_file
roles, collections, system roles #
- role structure (
tasks/handlers/defaults/templates),ansible-galaxy init - collections: confirm FQCN with
ansible-galaxy collection list - system roles: copy examples from
/usr/share/doc/rhel-system-roles/<role>/
Automating RHCSA tasks (half the weight) #
- users/groups/packages/repositories:
user/group/dnf/yum_repository - services/chronyd/logs:
service/timesync role/logrotate - storage/filesystems:
lvg/lvol/filesystem/mount(state=mounted) - firewall/SELinux/SSH keys:
firewalld/seboolean/sefcontext/authorized_key
Checks right before the Remote Exam #
You take RHCE at a test center (Kiosk) or via the Red Hat Remote Exam. Confirm the following before starting.
ID #
- An ID with English Romanization (passport recommended), name matching your registration info exactly
- Present the ID to the camera as instructed by the proctor
Testing environment #
- Clear everything off the desk, remove notes and posters from the walls
- Use a single monitor, disconnect secondary screens
- Block others from entering, secure a quiet, private space
System and right after the start #
- Boot the provided live image from USB, a stable wired network
- Right after the start, set up the working directory, inventory, ansible.cfg, SSH keys, become
- Before the first task, confirm connection to all hosts with
ansible all -m ping
Wrap-up #
What this post locked in:
- The four-hour operation. It’s all automation, so environment setup comes first. Pile tasks in one directory, run
--syntax-check/--checkeach time you write, no over-investing - ansible-doc as a weapon. No internet. Search modules with
ansible-doc -l, copy and adapt EXAMPLES, confirm FQCN withansible-galaxy collection list - system roles examples. Copy the example playbooks under
/usr/share/doc/rhel-system-roles/<role>/and change only the values - Idempotency. Run twice and confirm changed 0. Control command/shell with
creates/changed_when - Recurring mistakes. Missing FQCN, omitting become, command idempotency, the Vault password file, mount state=mounted, handler name mismatch, skipping syntax-check, ansible.cfg location
- Easily confused concept pairs (present/mounted, import/include, copy/template), a per-domain checklist, Remote Exam checks
Next: a full-scale mock exam #
In #19 Full-scale mock exam (Ansible integrated scenario + explanations), the final post of the series, we solve an integrated scenario with playbooks in a domain distribution similar to the real exam, with detailed explanations. It’s the last stage: solving it against a four-hour clock, checking idempotency and persistence, and firming up the high-weight RHCSA automation domain one more time.