Learn › RHCSA (EX200) › Deploy, Configure, Maintain
crontab - a hands-on Linux lab on a real virtual machine.
Program the system to run recurring tasks on a clock with cron. List jobs with crontab -l, edit them with crontab -e, remove the whole table with crontab -r, and install a table from a file with crontab file. Read and write the five time fields (minute, hour, day-of-month, month, day-of-week) with ranges like 1-5 and steps like /2, know where user crontabs live (/var/spool/cron/) versus the system /etc/crontab and /etc/cron.d/ drop-ins, and redirect a job output to a log with and 2&1. Serves EX200 deploy-configure: schedule tasks using at and cron.
It is Monday morning and the finance team is asking where the weekly sales report is. You wrote the script weeks ago. It works perfectly when you run it by hand. But nobody ran it, because you were asleep at 6 a.m. when it was supposed to fire, and so was everyone else. The script is fine. The problem is that a human was the trigger, and humans forget, sleep, and take vacations.
Machines do not. What you needed was a way to tell the system itself: run this program at six o'clock every weekday, forever, whether I am here or not. That mechanism is called cron, and the command that programs it is crontab. Learn it once and you never babysit a scheduled job again. On the exam, scheduling a recurring task with cron is one of the most reliable points on the board, and this lesson earns it.
The black boxes below are a practice terminal: a safe sandbox that checks the one command each step teaches. The outputs are from a real RHEL 10 machine (AlmaLinux 10.2). A few values, like the exact backup path timestamp and the job command you choose, depend on the machine and the task, so treat those as illustrative. What is stable everywhere is the SHAPE: crontab -l lists your jobs, crontab -e opens them for editing, and a job line is five time fields followed by the command to run.
cron is a background service that wakes up once a minute, checks a list of scheduled jobs, and runs any whose time has come. Think of it as an alarm clock for commands. You do not sit and watch the clock; you write down what to run and when, and cron watches the clock for you.
The list of jobs belonging to one user is called that user's crontab, short for cron table. Each line in the table is one job: a schedule plus a command. The crontab command is how you view, edit, and remove your own table. You almost never edit the underlying file by hand, because crontab manages it safely and reloads cron for you.
Any task that must happen on a clock, without a person present, is a cron job. Nightly backups. A log-rotation sweep. A script that clears a cache every hour. A report that goes out every weekday at six. If the rule is "do this thing on this schedule, repeatedly, unattended," cron is the tool.
It solves the one problem a human cannot: reliability over time. A person will run a task the first week and forget it by the third. cron runs it identically at 3 a.m. on a holiday. On RHEL systems, cron is provided by a package called cronie, and its service is crond. You rarely touch the service directly; you just write jobs with crontab and cron does the rest.
Start with the safest command, the one that only reads. crontab -l lists the current user's crontab, one job per line. The -l stands for list. If you have no jobs yet it prints no crontab for root and nothing else, which is not an error, just an empty table.
Here the table already holds one job, so listing it shows that job back to you. Read your crontab:
crontab -l
prompt: [root@servera ~]# answer: crontab -l output: 0 6 * * 1-5 /usr/local/bin/report hint: The list flag is -l for list: crontab -l
That single line IS a cron job, and it has two parts. The command at the end, /usr/local/bin/report, is what runs. Everything before it, 0 6 * * 1-5, is the schedule: five fields that say when. crontab -l is the command you run first on any machine to see what is already scheduled, and it is the command you run last to confirm the job you just added actually landed. It reads, it never changes anything, so it is always safe to run.
Every cron schedule is five fields, separated by spaces, always in the same order. Read them left to right:
A * in any field means "every value." So * * * * * runs every minute of every hour of every day. Reading the job from the last step, 0 6 * * 1-5, field by field: minute 0, hour 6, day-of-month * (any), month * (any), day-of-week 1-5. That is 06:00, on days one through five of the week, which is Monday through Friday. Six in the morning, every weekday. Exactly the report that failed to run when a human was the trigger.
The order never changes: minute, hour, day-of-month, month, day-of-week. The single most common beginner mistake is swapping the first two and putting the hour before the minute. 6 0 * * 1-5 does not mean six o'clock; it means minute 6 of hour 0, so 00:06, six minutes past midnight. Read the fields left to right every time: minute first, hour second.
You already saw a range: 1-5 in the day-of-week field means days one through five, a hyphen joining a start and an end. Ranges work in any field. 9-17 in the hour field means every hour from 9 a.m. to 5 p.m.
The other shorthand is the step, written with a slash. */2 means "every second value." In the minute field, */2 * * * * runs every 2 minutes. In the hour field, 0 */6 * * * runs at minute 0 of every sixth hour, so midnight, 6 a.m., noon, and 6 p.m. You can also combine a comma list, like 0 9,17 * * * for nine and five o'clock. Between * for every, a - range, a , list, and a */n step, you can express almost any schedule an exam will ask for.
A quick sanity check that saves points: read each of your five fields out loud as a sentence. 0 6 * * 1-5 becomes "at minute 0, at hour 6, any day of month, any month, on weekdays." If the sentence matches what the task asked for, the schedule is right. If you cannot say it cleanly, the fields are probably in the wrong order.
To add, change, or delete a job, you open your crontab in an editor with crontab -e. The -e stands for edit. It opens your personal table in the default editor, you type or change job lines, you save and quit, and cron reloads automatically. This is the normal, safe way to manage jobs, because crontab validates the file and installs it for you.
On the exam you may prefer to load a job non-interactively instead of opening an editor. You can hand crontab a file, and it installs that file as your whole crontab. The ground-truth machine did exactly that: it wrote one job line into a temporary file, then loaded it. Install a crontab from a file:
echo '0 6 * * 1-5 /usr/local/bin/report' > /tmp/ct; crontab /tmp/ct
prompt: [root@servera ~]# answer: echo '0 6 * * 1-5 /usr/local/bin/report' > /tmp/ct; crontab /tmp/ct output: Backup of root's previous crontab saved to /root/.cache/crontab/crontab.bak hint: Write the job line to a file, then hand that file to crontab: crontab /tmp/ct
Two commands ran here. The echo ... > /tmp/ct wrote the job line into a temporary file. Then crontab /tmp/ct installed that file as root's entire crontab. The message Backup of root's previous crontab saved to /root/.cache/crontab/crontab.bak is cronie being careful: before it replaced the old table, it saved a copy of what was there. Handing crontab a filename replaces the whole table, so the backup is your safety net if you overwrote something. On the exam, crontab -e is the safer habit because it edits in place instead of replacing everything.
That backup message and its path are RHEL 10 cronie behavior; the saved file is /root/.cache/crontab/crontab.bak. On other systems or older versions you may see no message at all, because installing a crontab is normally silent. Your exact path and timestamp will differ. The takeaway is the concept: replacing a crontab is a full overwrite, so a saved backup is a good thing to have.
Your personal crontab is not a file you open by name. It is stored for you under /var/spool/cron/, one file per user named after the user, and you are meant to manage it only through the crontab command. Editing the spool file by hand skips cron's reload and is a classic way to break a job silently.
System-wide schedules live somewhere else. /etc/crontab and the drop-in files under /etc/cron.d/ are for jobs the system runs, and they have one extra field: a username between the time fields and the command, naming who the job runs as. Packages drop their schedules into /etc/cron.d/. The rule to hold: your own recurring job goes in your user crontab via crontab -e; a system or package job goes in /etc/cron.d/ with a user field. Unless a task says otherwise, reach for crontab -e.
When cron runs a job, any text the job prints does not appear on a screen, because no screen is attached. By default cron mails that output to the job's owner, which on a lab machine usually goes nowhere useful. The clean habit is to send the output into a log file yourself, using the same > and 2>&1 redirection you learned in Foundations.
Append the job's normal output to a log with >>, and fold error output into the same file with 2>&1. A well-formed logging job looks like this:
0 6 * * 1-5 /usr/local/bin/report >> /var/log/report.log 2>&1
The >> /var/log/report.log appends whatever the report prints to that file instead of losing it. The 2>&1 says "send errors to the same place as normal output." Now every run leaves a trace you can read, and a failure is not a silent mystery. When a job seems not to work, a log file is the first thing you check.
One more flag finishes the set, and it is the sharp one. crontab -r removes the current user's entire crontab. The -r stands for remove. It does not open an editor and it does not ask for confirmation; it deletes every job you have in one stroke.
Know exactly what it does before you ever type it: -r wipes the whole table, not one line. To delete a single job, you use crontab -e and remove just that line. Remove the crontab:
crontab -r
prompt: [root@servera ~]# answer: crontab -r output: hint: The remove flag is -r, and it deletes the whole table: crontab -r
No output, and that silence is the danger. crontab -r deleted root's entire crontab without a word and without asking. Run crontab -l now and it reports no crontab for root, because the table is gone. The trap on this command is the keyboard: -r sits right next to -e in habit, and typing crontab -r when you meant crontab -e erases everything instead of opening it for edit. When you want to change one job, always reach for -e, never -r.
crontab -r and crontab -e are one keystroke apart and do opposite things. -e edits, -r removes it all, no undo, no prompt. If you fat-finger -r, your only recovery is the backup cronie may have saved under /root/.cache/crontab/crontab.bak, or rebuilding the job from memory. Before you press Enter on any crontab -r, be certain you meant to wipe the whole table.
Scaffolding off. No command is printed this time. You have every piece you need.
A task hands you a finished script at /usr/local/bin/report and one instruction: it must run at 6 a.m., Monday through Friday, and no other time. You already have the whole schedule written into a file at /tmp/ct. What single command installs that file as root's crontab, so cron will run the report every weekday morning? It is the command that takes a filename and makes it your active table, the same one that made cronie save a backup.
prompt: [root@servera ~]# answer: crontab /tmp/ct output: Backup of root's previous crontab saved to /root/.cache/crontab/crontab.bak hint: Hand the schedule file straight to crontab, no flag needed: crontab /tmp/ct
crontab /tmp/ct installs the file as root's entire crontab, and cron now runs /usr/local/bin/report at 0 6 * * 1-5, which is 06:00 every weekday. cronie saved the previous table to /root/.cache/crontab/crontab.bak on the way, as it always does on a replace. To prove the job took, you would run crontab -l and read the line back. That install-then-verify pair, load the job and list it, is the shape of every cron task on the exam.
You earned this cheat sheet. Every row is a form you just ran or built:
The one thing to burn in for the exam: the five fields are always minute, hour, day-of-month, month, day-of-week, in that order. Read them left to right, every time. Your own recurring job goes in your crontab with crontab -e; system and package jobs live in /etc/cron.d/ with an extra user field.
When a cron job does not seem to run, check three things in order. First, crontab -l to confirm the job is actually installed and the fields are right. Second, the log file you redirected output to, for the error the job printed. Third, that crond is running with systemctl status crond. Ninety percent of "my cron job failed" turns out to be a schedule typo you can see the moment you read crontab -l.
The practice terminal has walked you through the whole loop. crontab -l reads what is scheduled, and the five time fields say exactly when a job fires. Ranges and steps shape any schedule. crontab -e and installing from a file add jobs, output redirection keeps a log, and crontab -r clears the table. Every one of those you typed yourself.
The deploy-configure module capstone, Operation Rollout, is where you run these against a real RHEL 10 machine. A full VM boots for you, and the mission hands you deploy-and-configure objectives that use exactly what you practiced here. Schedule a recurring job at the right time, verify it landed with crontab -l, and send its output to a log. One difference from this lesson: the mission shows no commands. You read the objective, recall the five-field schedule and the crontab forms, and type them. That recall is what makes it stick on exam day.
Finish the other deploy-configure lessons, then go schedule a real job on a real machine.
Practice cron: Scheduled Tasks in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.