Study guide

15.1 Schedule Tasks with at, cron, and systemd Timers

Red Hat Certified System Administrator RHCSA

Track your progress as you read

Finished sections get a check mark.

1 Introduction & Exam Overview

1.1 Current Exam Facts 1.2 Lab Setup & Performance-Based Study Strategy

2 Essential Tools: Shell, Redirection & Text Analysis

2.1 Shell Prompt and Command Syntax 2.2 Input-Output Redirection and Pipes 2.3 grep and Regular Expressions for Text Analysis

3 Essential Tools: Remote Access, Users & Text Editing

3.1 Access Remote Systems Using SSH 3.2 Log In and Switch Users in Multiuser Targets 3.3 Create and Edit Text Files

4 Essential Tools: Files, Links & Archives

4.1 Create, Delete, Copy, and Move Files and Directories 4.2 Hard and Soft Links 4.3 Archive and Compress with tar, gzip, and bzip2

5 Essential Tools: Permissions & System Documentation

5.1 List, Set, and Change Standard ugo/rwx Permissions 5.2 System Documentation: man, info, and /usr/share/doc

6 Manage Software: RPM and Flatpak

6.1 Configure Access to RPM Repositories 6.2 Install and Remove RPM Software Packages 6.3 Configure Access to Flatpak Repositories 6.4 Install and Remove Flatpak Software Packages

7 Create Simple Shell Scripts

7.1 Conditional Execution: if, test, and [] 7.2 Looping Constructs for Files and Input 7.3 Process Script Inputs ($1, $2, and Beyond) 7.4 Process Shell Command Output in Scripts

8 Operate Running Systems: Boot and Recovery

8.1 Boot, Reboot, and Shut Down a System Normally 8.2 Boot Systems into Different Targets Manually 8.3 Interrupt the Boot Process to Gain Access

9 Operate Running Systems: Processes and Tuning

9.1 Identify and Kill CPU/Memory Intensive Processes 9.2 Adjust Process Scheduling 9.3 Manage Tuning Profiles

10 Operate Running Systems: Logs, Services & File Transfer

10.1 Locate and Interpret System Log Files and Journals 10.2 Preserve System Journals 10.3 Start, Stop, and Check Status of Network Services 10.4 Securely Transfer Files Between Systems

11 Configure Local Storage: Partitions and LVM

11.1 List, Create, and Delete Partitions on GPT Disks 11.2 Create and Remove Physical Volumes 11.3 Assign Physical Volumes to Volume Groups 11.4 Create and Delete Logical Volumes

12 Configure Local Storage: Mounting and Expansion

12.1 Mount File Systems at Boot by UUID or Label 12.2 Add Partitions, Logical Volumes, and Swap Non-Destructively

13 Create and Configure File Systems: Local FS and Permissions

13.1 Create, Mount, and Use VFAT, ext4, and XFS File Systems 13.2 Extend Existing Logical Volumes 13.3 Diagnose and Correct File Permission Problems

14 Create and Configure File Systems: NFS and autofs

14.1 Mount and Unmount Network File Systems Using NFS 14.2 Configure autofs

15 Deploy and Maintain: Scheduling, Services & Targets

15.1 Schedule Tasks with at, cron, and systemd Timers 15.2 Start, Stop, and Enable Services at Boot 15.3 Configure Systems to Boot into a Specific Target

16 Deploy and Maintain: Time, Software Updates & Bootloader

16.1 Configure Time Service Clients 16.2 Install and Update Software from CDN, Remote Repos, and Local Files 16.3 Modify the System Bootloader

17 Manage Basic Networking

17.1 Configure IPv4 and IPv6 Addresses 17.2 Configure Hostname Resolution 17.3 Configure Network Services to Start Automatically at Boot 17.4 Restrict Network Access Using firewalld and firewall-cmd

18 Manage Users and Groups

18.1 Create, Delete, and Modify Local User Accounts 18.2 Change Passwords and Adjust Password Aging 18.3 Create, Delete, and Modify Local Groups and Memberships 18.4 Configure Privileged Access

19 Manage Security: Firewall, umask & SSH Keys

19.1 Configure Firewall Settings with firewall-cmd and firewalld 19.2 Manage Default File Permissions 19.3 Configure Key-Based Authentication for SSH

20 Manage Security: SELinux Modes and Contexts

20.1 Set Enforcing and Permissive Modes for SELinux 20.2 List and Identify SELinux File and Process Contexts 20.3 Restore Default File Contexts

21 Manage Security: SELinux Ports and Booleans

21.1 Manage SELinux Port Labels 21.2 Use Boolean Settings to Modify SELinux Settings

15.1 Schedule Tasks with at, cron, and systemd Timers

Key Takeaways Explain This Topic AI

Use at for one-shot future jobs, cron (crontab -e or /etc/crontab and /etc/cron.*) for recurring schedules, and systemd .timer/.service unit pairs for modern persistent timers on RHEL 10.

User crontabs are edited with crontab -e; system-wide schedules live in /etc/crontab and drop-in directories—field order and user column differ between user crontab and /etc/crontab.

systemd timers require a matching service unit (or existing unit), a .timer with OnCalendar/OnBootSec-style triggers, then systemctl enable --now name.timer and list-timers verification.

Always verify jobs with atq/atrm, crontab -l, systemctl list-timers, and logs (journalctl -u, /var/log/cron) so EX200 end state is demonstrable.

Schedules must persist across reboot when the task requires it—prefer installed crontab entries, /etc files, or enabled timers over ad-hoc shells that die with the session.

Last updated: August 2026

15.1 Schedule Tasks with at, cron, and systemd Timers

Quick Answer: Run a command once in the future with at . Run recurring jobs with crontab -e (per user) or /etc/crontab / /etc/cron.d (system). Prefer systemd .timer + .service unit pairs for modern persistent schedules: write units under /etc/systemd/system/ , then systemctl enable --now name.timer . Verify with atq , crontab -l , and systemctl list-timers .

Why scheduling is a deploy objective

Under Deploy, configure, and maintain systems , Red Hat expects you to schedule tasks so work runs later or repeatedly without an interactive shell. EX200 tasks look like:

  • “At 23:00 tonight, run this backup script once.”
  • “Every weekday at 02:15, as root, run /usr/local/bin/cleanup.sh .”
  • “Create a systemd timer that runs myscript.service every hour and enable it.”

You are graded on the configured schedule and ownership , not on watching the clock until the job fires—though you should know how to test (near-term at , temporary cron minute, systemctl start name.service for the timer’s unit).

Three tool families coexist on RHEL 10:

  • Tool Best for Persistence
  • at / batch One-time future execution Job queue until run (or removed)
  • cron Classic recurring calendar schedules User crontab or /etc files
  • systemd timers Native systemd schedules, boot-relative and calendar Enabled .timer units

Know all three; the exam may specify which to use or leave the choice open when any correct persistent method works.

  • One-shot jobs with at
  • Package and service rpm -q at
  • sudo dnf install -y at # if missing on a minimal image
  • sudo systemctl enable --now atd
  • systemctl is-active atd

Without atd running, at jobs do not execute.

  • Submit a job at 23:00
  • # at> /usr/local/bin/backup.sh
  • # at> <EOT> # Ctrl-D
  • Or pipe: echo "/usr/local/bin/backup.sh" | at 23:00
  • echo "/usr/local/bin/backup.sh" | at now + 5 minutes
  • echo "/usr/local/bin/backup.sh" | at 2:30am tomorrow
  • echo "/usr/local/bin/backup.sh" | at teatime # 16:00 on many systems

Time formats at accepts are flexible ( HH:MM , now + N minutes/hours/days , weekday names). Prefer explicit times under exam pressure.

  • Inspect and remove atq # list pending jobs (job number, time, user)
  • at -c JOBNUMBER # show script content of a job
  • atrm JOBNUMBER # remove
  • # or: at -d JOBNUMBER

Exam tip: After submitting, immediately atq and at -c to prove the command body is correct. Wrong path or missing execute bit fails silently later.

batch and permissions (awareness)

batch runs when load is low. Access control historically uses /etc/at.allow and /etc/at.deny . On the exam you typically work as root or a permitted user—if at is denied, check allow/deny files and that atd is active. ls /etc/at.allow /etc/at.deny 2>/dev/null

Environment note

at jobs run with a limited environment; use absolute paths for binaries and scripts. Redirect output if you need proof files: echo "/usr/local/bin/backup.sh > /var/tmp/backup.out 2>&1" | at now + 2 minutes

  • Recurring jobs with cron
  • User crontab: crontab -e

Each user (including root) has a personal crontab managed only via the crontab command—not by hand-editing spool files. crontab -e # edit current user's crontab

  • sudo crontab -e -u bob # edit bob's crontab (as root)
  • crontab -l # list
  • crontab -r # remove entire crontab (dangerous—confirm first)
  • crontab -l -u bob
  • User crontab fields (no user column): # min hour dom month dow command
  • # 0-59 0-23 1-31 1-12 0-7 (0 and 7 = Sunday)
  • 15 2 * * 1-5 /usr/local/bin/cleanup.sh
  • Field Meaning Examples
  • minute 0–59 0 , */15 , 0,30
  • hour 0–23 2 , 9-17
  • day of month 1–31 1 , */2
  • month 1–12 or names 1 , jan
  • day of week 0–7 (Sun) or names 1-5 , mon # Examples in a user crontab
  • 0 3 * * * /usr/local/bin/nightly.sh
  • */10 * * * * /usr/local/bin/poll.sh
  • 0 9 1 * * /usr/local/bin/monthly.sh
  • 30 4 * * 0 /usr/local/bin/weekly-sun.sh

Comments start with # . Leave a trailing newline. Syntax errors can cause crontab -e to reject the file when you save—read the error and fix before exiting.

  • System crontab: /etc/crontab sudo less /etc/crontab
  • Typical layout: SHELL=/bin/bash
  • PATH=/sbin:/bin:/usr/sbin:/usr/bin
  • MAILTO=root

# Example of job definition:

  • # .---------------- minute (0 - 59)
  • # | .------------- hour (0 - 23)
  • # | | .---------- day of month (1 - 31)

# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...

# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue ...

  • # | | | | |
  • # * * * * * user-name command to be executed

Critical difference: /etc/crontab and files under /etc/cron.d/ include a user field before the command. User crontabs from crontab -e do not . # /etc/crontab or /etc/cron.d/myclean

15 2 * * 1-5 root /usr/local/bin/cleanup.sh

If you put a user field in a personal crontab, cron mis-parses the line. If you omit the user in /etc/crontab , the job is wrong.

Drop-in and periodic directories

  • Path Role
  • /etc/crontab Main system crontab
  • /etc/cron.d/* Package/admin drop-in crontabs (same 7-field style with user)
  • /etc/cron.hourly/ Scripts run hourly (run-parts style)
  • /etc/cron.daily/ Daily
  • /etc/cron.weekly/ Weekly
  • /etc/cron.monthly/ Monthly
  • /var/spool/cron/<user> Spool for user crontabs— do not edit by hand sudo ls /etc/cron.d /etc/cron.daily

# Install a system job:

  • sudo tee /etc/cron.d/rhcsa-clean <<'EOF'
  • # Run cleanup weekdays at 02:15 as root
  • 15 2 * * 1-5 root /usr/local/bin/cleanup.sh
  • EOF
  • sudo chmod 644 /etc/cron.d/rhcsa-clean

Scripts in cron.daily etc. must be executable and typically not contain dots in ways that confuse run-parts (distribution-specific; prefer clear names without extension issues). For exam clarity, an explicit line in /etc/cron.d or a user crontab is often safer than fighting run-parts rules.

  • crond service systemctl status crond
  • sudo systemctl enable --now crond

On RHEL the daemon is crond . If it is stopped, no cron jobs run.

  • Logging and mail sudo grep CRON /var/log/cron 2>/dev/null | tail
  • sudo journalctl -u crond -b --no-pager | tail

Cron may mail output to the user ( MAILTO ). Redirect to files when the task wants an artifact: 0 3 * * * /usr/local/bin/nightly.sh >> /var/log/nightly.log 2>&1

systemd timers (timer + service pairs)

Timers are first-class systemd units. A timer unit activates another unit (almost always a service ) on a schedule.

  • Minimal pair under /etc/systemd/system
  • Service ( /etc/systemd/system/cleanup.service ) — what to run: [Unit]
  • Description=RHCSA cleanup oneshot service
  • [Service]
  • Type=oneshot
  • ExecStart=/usr/local/bin/cleanup.sh
  • Timer ( /etc/systemd/system/cleanup.timer ) — when to run: [Unit]
  • Description=Run cleanup.service on a schedule
  • [Timer]
  • OnCalendar=*-*-* 02:15:00
  • Persistent=true
  • [Install]
  • WantedBy=timers.target sudo systemctl daemon-reload
  • sudo systemctl enable --now cleanup.timer
  • systemctl list-timers --all
  • systemctl status cleanup.timer
  • systemctl status cleanup.service
  • OnCalendar vs monotonic triggers
  • Directive Meaning
  • OnCalendar= Wall-clock schedule (like cron, richer syntax)
  • OnBootSec= Time after boot
  • OnUnitActiveSec= Time after the unit last activated
  • OnStartupSec= After systemd startup
  • Persistent=true Catch up missed runs while powered off (when applicable)
  • Unit= Explicit unit to activate if name differs from timer prefix [Timer]
  • OnBootSec=10min
  • OnUnitActiveSec=1h # Calendar examples (systemd.time)
  • OnCalendar=hourly
  • OnCalendar=daily
  • OnCalendar=Mon..Fri 02:15
  • OnCalendar=*-*-* 00/2:00:00
  • Validate calendar expressions: systemd-analyze calendar 'Mon..Fri 02:15'
  • systemd-analyze calendar '*-*-* 02:15:00'
  • Enable, start, and test sudo systemctl enable cleanup.timer # start timer at boot
  • sudo systemctl start cleanup.timer # arm timer now

# equivalent:

sudo systemctl enable --now cleanup.timer

# Manually run the payload once (does not wait for calendar):

  • sudo systemctl start cleanup.service
  • sudo journalctl -u cleanup.service -n 20 --no-pager systemctl list-timers
  • systemctl list-timers --all
  • systemctl cat cleanup.timer
  • systemctl cat cleanup.service

list-timers shows NEXT and LEFT —proof the timer is armed. After reboot, re-check that the timer is active and enabled.

Where unit files live

  • Location Use
  • /etc/systemd/system/ Admin-created units (exam default)
  • /usr/lib/systemd/system/ Vendor units—prefer drop-ins over editing
  • /etc/systemd/system/name.timer.d/ Drop-in overrides

Do not put custom timers only in /tmp . They must be on disk under systemd paths to persist.

Choosing at vs cron vs timer

  • Requirement Prefer
  • Once at a specific future time at
  • Simple recurring line, classic style crontab -e or /etc/cron.d
  • Boot-relative (“10 minutes after every boot”) systemd timer OnBootSec=
  • Tight integration with other units / logging systemd timer
  • Task explicitly says “cron” or “timer” Follow the wording

If the task says systemd timer , deliver a .timer + .service pair, not only a crontab.

  • Exam workflows
  • Workflow A — One-time job in five minutes sudo systemctl enable --now atd
  • echo "/usr/local/bin/job.sh >> /var/tmp/job.out 2>&1" | at now + 5 minutes
  • atq
  • Workflow B — User recurring job crontab -e
  • # add: 0 * * * * /usr/local/bin/hourly-user.sh
  • crontab -l
  • Workflow C — System job as root via /etc/cron.d sudo tee /etc/cron.d/hourly-root <<'EOF'
  • 0 * * * * root /usr/local/bin/hourly-root.sh
  • EOF
  • sudo systemctl is-active crond || sudo systemctl enable --now crond
  • Workflow D — systemd hourly timer sudo tee /etc/systemd/system/hourly-job.service <<'EOF'
  • [Unit]
  • Description=Hourly job service
  • [Service]
  • Type=oneshot
  • ExecStart=/usr/local/bin/hourly-root.sh
  • EOF
  • sudo tee /etc/systemd/system/hourly-job.timer <<'EOF'
  • [Unit]
  • Description=Hourly job timer
  • [Timer]
  • OnCalendar=hourly
  • Persistent=true
  • [Install]
  • WantedBy=timers.target
  • EOF
  • sudo systemctl daemon-reload
  • sudo systemctl enable --now hourly-job.timer
  • systemctl list-timers | grep hourly-job
  • Common traps

Forgetting the user field in /etc/crontab / cron.d , or adding a user field in crontab -e .

Editing /var/spool/cron/* by hand instead of crontab -e .

Relative paths in jobs—use absolute paths to scripts and interpreters.

Script not executable or wrong SELinux context—test with manual run first.

atd or crond disabled —schedule exists but never fires.

Only writing a .timer without a service (or wrong Unit= name).

Creating units but skipping daemon-reload and enable --now .

Assuming systemctl start foo.timer enables at boot —use enable for persistence.

Day-of-week and day-of-month both restricted in cron—behavior is OR in classic cron; keep schedules simple on the exam.

Not verifying with crontab -l , atq , or list-timers before moving on.

Relationship to other chapters

Logs (Ch10): journalctl -u name.service and /var/log/cron prove execution attempts.

Services (15.2): timers are units—same enable / start / status discipline.

Shell scripting (Ch07): scheduled payloads are often your scripts—test them interactively first.

Permissions/SELinux: scheduled root jobs still hit DAC and SELinux on target files.

Section checkpoint

You should submit and manage at jobs with atq / atrm , edit user crontabs with crontab -e , write correct system lines in /etc/crontab or /etc/cron.d (including the user column), ensure atd / crond are running, build systemd timer/service pairs , daemon-reload , enable --now *.timer , and verify with list-timers and logs. That is the EX200 bar for scheduling tasks on RHEL 10.