The previous lesson ended with a diagnosis and an outstanding task: Tramontana's nightly backup runs at the wrong time, with the wrong priority and over more data than necessary. Somebody scheduled it at some point and nobody has looked at it since. It is the exact portrait of badly done automation: it works until the day it brings the service down.
cron is the Unix timer. It has been running since 1975 and it is still the most widespread way of telling a server "do this every day at four". Its syntax is short and its failures are always the same three, repeated in every company in the world. This lesson gives you the complete syntax and, above all, those three failures with their solutions, because they account for 90% of real cron incidents.
One point about scope: here the tasks will be one-liners or calls to existing commands. Writing the backup script with its checks and its error handling is Module 4.
Contents
- What automates well and what automates badly
- The user crontab and the system crontab
- The five fields
- The conflict between day of the month and day of the week
- Classic failure 1: cron's PATH
- Classic failure 2: the output nobody reads
- Classic failure 3: the time zone
- Debugging a cron task
- Overlapping and
flock anacronand systemd timers- The Tramontana case: the nightly backup and the purge
- What automates well and what automates badly
| Good candidate | Bad candidate |
|---|---|
| Idempotent: repeating it breaks nothing | Accumulates effects on each run |
| Bounded and predictable duration | Can run for hours indefinitely |
| Fails visibly and on the record | Fails silently |
| Needs no human decisions | Requires case-by-case judgement |
| Its resources are controlled | Competes freely with the service |
Tramontana's nightly backup failed on the last two rows: it did not limit its I/O priority and nobody read its result. An automatic task that fails silently is worse than not having it at all, because it generates the confidence that the job is done. The first time you discover that the backups have been failing for three months is usually the day you need to restore one.
- The user crontab and the system crontab
The cron daemon (the cron package on Ubuntu) reads several different places:
| Location | User field | Edited with | What for |
|---|---|---|---|
the user's crontab -e |
no | crontab -e |
one account's tasks |
/etc/crontab |
yes | editor + sudo |
system tasks |
/etc/cron.d/<file> |
yes | editor + sudo |
tasks belonging to packages or services |
/etc/cron.{hourly,daily,weekly,monthly}/ |
— | executable scripts | simple periodic tasks |
operator@srv-tramontana:~$ crontab -l
30 3 * * * tar -czf /srv/tramontana/backups/outgoing/nightly.tar.gz /opt/tramontanaThere is the culprit behind last night's incident.
| Option | Effect |
|---|---|
crontab -l |
list |
crontab -e |
edit (uses $EDITOR, which you set in 03-01) |
crontab -r |
delete the entire crontab, without asking |
crontab file |
replace the crontab with a file's contents |
crontab -u user -l |
see another user's (requires root) |
Careful with crontab -r. It asks for no confirmation and there is no wastebasket: it deletes all your tasks at a stroke. And it is right next to -e on the keyboard. The professional habit is simple: keep your crontab in a version-controlled file and load it with crontab file. That way an unfortunate keystroke costs you a crontab ~/cron/operator.cron, not a reconstruction from memory.
The key difference between the two formats is that the lines in /etc/crontab and /etc/cron.d/ have an extra field with the user who runs the task, right before the command:
Forgetting that field is a frequent error: cron reads the user name as the command's first argument and the task fails in a baffling way.
The cron.daily directories and company are run by run-parts, which has a quirk: it ignores files whose name contains a dot. A script called backup.sh in /etc/cron.daily/ will never run. It has to be called backup, with no extension, and be executable.
- The five fields
┌───── minute (0-59) │ ┌─── hour (0-23) │ │ ┌─ day of month (1-31) │ │ │ ┌───── month (1-12 or jan-dec) │ │ │ │ ┌─── weekday (0-7, where 0 and 7 are Sunday, or sun-sat) │ │ │ │ │ * * * * * command
| Syntax | Means |
|---|---|
* |
any value |
5 |
that exact value |
1,15,30 |
a list |
9-17 |
a range |
*/5 |
every 5 units from 0 |
0-30/10 |
every 10, within the range |
| Expression | When it runs |
|---|---|
* * * * * |
every minute |
*/15 * * * * |
at minutes 0, 15, 30 and 45 |
30 4 * * * |
every day at 04:30 |
0 9-18 * * 1-5 |
on the hour, from 9 to 18, Monday to Friday |
0 4 1 * * |
on the 1st of each month at 04:00 |
15 2 * * 6 |
on Saturdays at 02:15 |
0 0 1 1,7 * |
on 1 January and 1 July |
*/10 2-4 * * * |
every 10 minutes, between 2 and 4 |
5 0 * * * |
at 00:05 (not at 00:00: avoids the minute of maximum contention) |
That last row is genuine advice. At 00:00 exactly, everything anybody scheduled without thinking starts up; shifting by five minutes avoids competing with it.
Shortcuts that replace the five fields:
| Shortcut | Equivalent to |
|---|---|
@reboot |
once, when the system boots |
@hourly |
0 * * * * |
@daily / @midnight |
0 0 * * * |
@weekly |
0 0 * * 0 |
@monthly |
0 0 1 * * |
@yearly |
0 0 1 1 * |
- The conflict between day of the month and day of the week
Here is a rule that contradicts intuition and makes many tasks run more often than intended.
If the "day of the month" and "day of the week" fields are both different from *, they are combined with OR, not with AND. The task runs if either of the two is satisfied.
The usual intention — "only on Friday the 13th" — requires checking inside the command, because cron cannot express it:
(Note the \%: in a crontab, the % symbol has a special meaning that we will see in section 6.)
When one of the two is *, there is no ambiguity and the other one governs. The table summarises the four cases:
| Day of month | Weekday | Result |
|---|---|---|
* |
* |
every day |
15 |
* |
only on the 15th |
* |
1 |
only on Mondays |
15 |
1 |
on the 15th and also every Monday |
- Classic failure 1: cron's PATH
It is, by a long way, number one. The symptom is always the same: the command works perfectly when you type it and does nothing from cron.
You have known the cause since 03-01: cron launches neither a login nor an interactive shell, so it does not read /etc/profile, nor ~/.profile, nor ~/.bashrc. Nothing from your environment exists there. The PATH cron provides by default is:
Two directories. /usr/local/bin is not there, nor is /usr/sbin, and of course neither is the /home/operator/scripts you added in 03-01. Your aliases do not exist either, nor your variables, nor the locale you have configured.
If rsync were installed in /usr/local/bin, a task invoking it by name would fail every time, silently and leaving no visible trace.
The three solutions, in order of preference:
-
Absolute paths always. Find them out with
command -vand write them exactly.command -v rsync tar findgives you all of them at once:/usr/bin/rsync,/usr/bin/tar,/usr/bin/find. -
Declare the PATH in the crontab header. Variable assignments go before the tasks and apply to all of them:
- Make the script load the environment with
. ~/.profileas its first line. It works, but it couples the task to one account's personal configuration, and that is fragile.
The rule we apply at Tramontana is number 1 reinforced with number 2: absolute paths in the commands and an explicit PATH in the header, for whatever the command may invoke internally.
- Classic failure 2: the output nobody reads
Cron captures each task's stdout and stderr and sends them by local mail to the owning user. On a server with no MTA configured — like srv-tramontana — that mail goes nowhere: it is discarded or it sits in /var/mail/operator without anybody ever opening it.
The result: if you do not redirect the output, you find out nothing. Neither about the successes nor, above all, about the failures.
| Redirection | What you get |
|---|---|
| (nothing) | local mail nobody reads |
> /dev/null |
silences the output; errors do still generate mail |
> /dev/null 2>&1 |
absolute silence. Avoid it |
>> /var/log/... 2>&1 |
the right thing: everything is recorded |
30 4 * * * /usr/bin/rsync -a /home/operator/data/ /srv/tramontana/backups/outgoing/ >> /var/log/tramontana/cron-backup.log 2>&1
The 2>&1 goes at the end, for what you learned in 03-04: first stdout is redirected to the file and then that destination is copied onto stderr. The other way round, the errors — which are exactly what you care about — would end up in the phantom mail.
A dated record is achieved by prefixing a date:
30 4 * * * { /usr/bin/date '+--- %F %T start'; /usr/bin/rsync -a /home/operator/data/ /srv/tramontana/backups/outgoing/; } >> /var/log/tramontana/cron-backup.log 2>&1MAILTO controls where the mail goes: [email protected] sends it to a real address (if there is an MTA), and MAILTO="" disables sending altogether. The professional combination is MAILTO="" plus redirection to a log file, with a monitoring system watching that file.
And the % quirk: in a crontab, % is interpreted as a newline that feeds the command's standard input. That is why date +%F inside a crontab has to be written date +\%F. It is a source of errors out of all proportion to how obscure the detail is.
- Classic failure 3: the time zone
cron uses the system's time zone, not the user's. Always check it:
In zones with summer time there are two problematic days a year. When the clock goes forward in March, 02:30 does not exist: a task scheduled for that time does not run that day. When it goes back in October, 02:30 happens twice, and depending on the implementation the task may run twice.
Three ways of protecting yourself:
- Schedule outside the 01:00–03:00 band. It is the simplest solution and the one that really solves the problem.
- Use UTC on the server (
sudo timedatectl set-timezone UTC), a common practice on servers, at the cost of having to translate the schedules in your head. - Declare
CRON_TZ=UTCin the crontab header, which fixes the zone for those tasks only.
There is a coincidence here worth underlining: Tramontana's backup was at 03:30, inside the risky band, and it also clashed with the load peak. Moving it solves two problems at once.
- Debugging a cron task
flowchart TD
A["The task does not do what is expected"] --> B{"Did it run?<br/>grep CRON /var/log/syslog"}
B -->|No| C["Check the syntax of the 5 fields<br/>and that the crontab is loaded"]
B -->|Yes| D{"Is there a record<br/>in your log file?"}
D -->|No| E["The redirection is missing:<br/>add >> log 2>&1"]
D -->|Yes| F{"What does the error say?"}
F -->|"command not found"| G["PATH: use absolute paths"]
F -->|"permission denied"| H["Wrong user<br/>or destination permissions"]
F -->|"something else"| I["Reproduce with env -i"]
Where it is recorded. The daemon notes each run in the syslog:
operator@srv-tramontana:~$ grep CRON /var/log/syslog | tail -3
Aug 18 03:30:01 srv-tramontana CRON[2210]: (operator) CMD (tar -czf /srv/tramontana/backups/outgoing/nightly.tar.gz /opt/tramontana)
Aug 18 04:30:01 srv-tramontana CRON[2455]: (operator) CMD (/usr/bin/rsync -a /home/operator/data/ /srv/tramontana/backups/outgoing/)
Aug 18 04:30:02 srv-tramontana CRON[2455]: (CRON) info (No MTA installed, discarding output)Three things: the backup started at 03:30:01, coinciding with the incident; the rsync ran too; and the message "No MTA installed, discarding output" is cron literally telling you it has thrown the output in the bin. There is failure 2, confessed in the log itself.
That record tells you whether the task ran, but not whether it worked: cron does not check the exit code. For that you need your own log file.
Dumping cron's environment. The definitive trick for failure 1: schedule a temporary task that saves its environment and compare it with yours.
operator@srv-tramontana:~$ cat /tmp/cron-env.txt
SHELL=/bin/sh
PWD=/home/operator
LOGNAME=operator
PATH=/usr/bin:/bin
HOME=/home/operatorFive variables. No LANG, and none of your extended PATH. Note as well that SHELL is /bin/sh, not bash: Bash-specific constructs may fail. If you need them, declare SHELL=/bin/bash in the header.
Testing the command with cron's environment, without waiting for it to fire:
operator@srv-tramontana:~$ env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=/home/operator \
/bin/sh -c 'rsync -a /home/operator/data/ /srv/tramontana/backups/outgoing/'
/bin/sh: 1: rsync: not foundReproduced in two seconds. env -i starts the command with no inherited variables at all and adds only the ones you specify: it is a faithful simulation of cron. Any new task should be tested this way before being scheduled.
- Overlapping and
flock
flockIf one run lasts longer than the interval, cron launches the next one anyway. Two backups writing to the same file produce a corrupt archive; two simultaneous purges can cut the ground from under each other.
flock solves the problem with a lock file:
30 4 * * * /usr/bin/flock -n /var/lock/tramontana-backup.lock /usr/bin/tar -czf /srv/tramontana/backups/outgoing/nightly.tar.gz /opt/tramontana/app/ >> /var/log/tramontana/cron-backup.log 2>&1
| Option | Behaviour if the lock is taken |
|---|---|
-n |
exits immediately with code 1 |
-w 60 |
waits up to 60 seconds and then gives up |
| (nothing) | waits indefinitely |
-n is almost always the right option for a periodic task: if the previous one is still running, this one is superfluous. Without -n, the runs queue up and you end up with twenty processes waiting.
The lock is released on its own when the process finishes, even if it dies abruptly, because the kernel manages it through the file descriptor. There are no orphaned lock files to clean up by hand, which is exactly the problem with implementing it with a touch and an rm.
anacron and systemd timers
anacron and systemd timerscron assumes the machine is switched on at the scheduled time. If the server was off, the task is not recovered. anacron fills that gap: it works with a granularity of days and, on start-up, runs whatever was left pending. On Ubuntu, cron.daily, cron.weekly and cron.monthly are managed by anacron for precisely that reason.
| cron | anacron | systemd timer | |
|---|---|---|---|
| Granularity | minutes | days | seconds |
| Recovers what was missed | no | yes | yes (Persistent=true) |
| Logging | syslog | syslog | journalctl -u <unit> |
| Dependencies between tasks | no | no | yes |
| Randomise the start | no | yes | RandomizedDelaySec |
| Complexity | minimal | low | high (two files per task) |
When to choose each. cron for the simple and periodic on a server that is always on: it is universal, it fits on one line and any administrator understands it. anacron for laptops and machines that get switched off. systemd timers when you need dependencies between units, resource control, retries or the system's integrated logging; they are studied in 05-05. For Tramontana's backup, cron is sufficient and more readable.
- The Tramontana case: the nightly backup and the purge
We redo the guilty task applying everything above. The crontab is kept in a version-controlled file and loaded from there.
operator@srv-tramontana:~$ crontab -l > ~/cron-operator.bak-$(date +%F)
operator@srv-tramontana:~$ nano ~/cron/operator.cron# operator's crontab — Tramontana Bookings SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin MAILTO="" CRON_TZ=Europe/Madrid # Daily backup of the data and of the active release. 04:20 (outside the peak and the clock change). 20 4 * * * /usr/bin/flock -n /var/lock/tramontana-backup.lock /usr/bin/ionice -c 3 /usr/bin/tar -czf /srv/tramontana/backups/outgoing/data-$(/usr/bin/date +\%F).tar.gz /home/operator/data /opt/tramontana/app/ >> /var/log/tramontana/cron-backup.log 2>&1 # Purge of backups more than 30 days old. Sundays at 05:10. 10 5 * * 0 /usr/bin/find /srv/tramontana/backups/outgoing -type f -name 'data-*.tar.gz' -mtime +30 -delete >> /var/log/tramontana/cron-purge.log 2>&1
It is loaded with crontab ~/cron/operator.cron and checked with crontab -l. Go over each decision, because each one corrects a specific problem:
- 04:20 instead of 03:30: outside the error peak you identified in 03-05 and outside the clock-change band.
flock -n: if a previous backup is still running, this one does not start.ionice -c 3: the backup only uses the disk when the service does not need it. It is the direct correction of the 03-06 incident./opt/tramontana/app/with a trailing slash instead of the whole of/opt/tramontana:appis the symbolic link to the active release, and the slash makestarfollow the link. One release is copied instead of four, and always the one that is serving.- A dated name:
data-2026-08-18.tar.gz, with an escaped\%F. A file per day, instead of overwriting the only copy you had. >> ... 2>&1in both tasks: everything is recorded, with the2>&1at the end.- Absolute paths in every command, including the
dateinside the substitution. - The purge as a separate task and with
find -mtime +30: you know from 03-03 that this means 31 days or more, ample margin for a monthly retention.
Verification before going home: the command is tested with cron's environment and the result is checked.
operator@srv-tramontana:~$ env -i SHELL=/bin/bash PATH=/usr/bin:/bin HOME=/home/operator /bin/bash -c \
'/usr/bin/flock -n /var/lock/tramontana-backup.lock /usr/bin/ionice -c 3 /usr/bin/tar -czf /tmp/test.tar.gz /home/operator/data /opt/tramontana/app/'
operator@srv-tramontana:~$ echo $?; ls -lh /tmp/test.tar.gz
0
-rw-r----- 1 operator operator 47M Aug 18 13:40 /tmp/test.tar.gz
operator@srv-tramontana:~$ tar -tzvf /tmp/test.tar.gz | head -2
drwxr-x--- operator/tramontana 0 2026-08-18 09:14 home/operator/data/
-rw-r----- operator/tramontana 1284 2026-08-14 11:02 home/operator/data/bookings.csvExit code 0, 47 MB — one release, not four — and tar -tzvf confirms that the contents are what we expect before signing the task off. It is the course convention applied here: look inside the archive before trusting it.
The report for Marta. The nightly backup has been rescheduled to 04:20, with the lowest disk priority and limited to the release in service instead of all four. This protects against the backup leaving the application without disk again, against two backups overlapping and against losing the previous copy when the new one is generated. It does not protect against the backup failing: nobody is yet watching /var/log/tramontana/cron-backup.log, so an error would still go unnoticed, nor against the loss of the entire server, because the backups are still on the same disk. Both of those require decisions beyond a technical adjustment and are dealt with in the administration module.
Common Mistakes and Tips
crontab -rinstead of-e. It deletes everything without asking. Keep the crontab in a file and load it withcrontab file.- Forgetting the user field in
/etc/cron.d/. Cron takes it as an argument to the command. - Giving a
/etc/cron.daily/script an extension.run-partsignores names with a dot. - Assuming your PATH exists. Cron gives you
/usr/bin:/binand nothing else. Absolute paths. - An unescaped
%. Inside a crontab,%is a newline. Write\%. - Not redirecting the output. Without
>> log 2>&1the failures disappear. - Putting
2>&1before the output redirection. It goes at the end, always. - Scheduling between 02:00 and 03:00. That is the clock-change band.
- A
* * * * *for testing and forgetting to remove it. It leaves a task running every minute forever. - Tip: document every task with a comment saying what it does and why at that time. In a year's time you will be the one thanking yourself, not somebody else.
- Tip: make the task write a dated success marker. Checking "is the last line of the log from today?" is a trivial check and it detects 90% of the problems.
Exercises
Exercise 1. Write the cron expressions for: every 20 minutes between 8 and 20 on working days; the last day of each month at 23:50; and at 06:15 on the 1st and the 15th. Justify the second case, which has a catch.
Exercise 2. Schedule a task that records every hour the number of 500 errors in errors.log in /var/log/tramontana/errors-500.log together with the date. Apply the three protections against the classic failures and prove that it would work in cron's environment before installing it.
Exercise 3. A scheduled task */5 * * * * /home/operator/scripts/synchronise >> /tmp/sync.log 2>&1 produces no lines at all in its log, even though the command works when you run it. Describe the complete diagnostic procedure, in order, saying what you would check at each step and what you would conclude from each result.
Solutions
Solution 1.
*/20 8-20 * * 1-5 # every 20 min, from 8 to 20, Monday to Friday 15 6 1,15 * * # at 06:15 on the 1st and the 15th
The second one, "the last day of each month", cannot be expressed with the five fields: cron does not know how many days the month has, and 31 would skip February, April, June, September and November. The idiomatic solution is to schedule it every day and let the command decide:
You ask what day tomorrow will be: if it is the 1st, today is the last day of the month, whatever its length and even in a leap year. It is a good example of cron's boundary: when the condition does not fit into the five fields, run it daily and check inside. The \% is escaped, as always.
Solution 2. First the command is tested in cron's environment:
operator@srv-tramontana:~$ env -i PATH=/usr/bin:/bin HOME=/home/operator /bin/sh -c \
'echo "$(/usr/bin/date +%F\ %T) $(/usr/bin/grep -c "ERROR 500" /var/log/tramontana/errors.log)"'
2026-08-18 13:52 41It works with only /usr/bin:/bin in the PATH, so we do not depend on anything cron does not have. Now the crontab line:
0 * * * * /usr/bin/flock -n /var/lock/errors-500.lock /bin/sh -c 'echo "$(/usr/bin/date +\%F\ \%T) $(/usr/bin/grep -c \"ERROR 500\" /var/log/tramontana/errors.log)"' >> /var/log/tramontana/errors-500.log 2>&1
The three protections: absolute paths for date and grep, so that the minimal PATH does not matter; >> ... 2>&1 so that both the result and any error end up in the file and not in mail nobody reads; and CRON_TZ declared in the crontab header — along with choosing minute 0 of each hour, far from the clock-change band — so that the timestamps are consistent. flock is added because it is a cheap habit, even though here the risk of overlapping is minimal. And the % signs are escaped: without the backslashes, cron would cut the command off at the first % and pass the rest as standard input, which is a particularly hard failure to recognise.
A line that starts accumulating escapes like this one is the sign that the content is asking for a script of its own. That is what Module 4 will resolve.
Solution 3. The procedure, in order, from the most external to the most internal:
- Does the task exist?
crontab -l | grep synchronise. If it does not appear, another crontab was edited —root's, for example — or acrontab -rtook it away. - Is it running?
grep CRON /var/log/syslog | grep synchronise | tail -3. If there are no entries, cron is not launching it: check the syntax of the fields and that the daemon is active. If there are, cron is launching it and the problem is further in. - Does the log file exist and with what permissions?
ls -l /tmp/sync.log. An empty but existing log confirms that the redirection works and that the command writes nothing; the file not existing points to the command not even having started. - Is it executable and with the right path?
ls -l /home/operator/scripts/synchronise. Without thexbit, cron gets "permission denied". And here is the prime suspect: the task uses an absolute path to the script, but the script internally probably invokes commands by name, and those do depend on the PATH. - Reproduce with cron's environment:
env -i PATH=/usr/bin:/bin HOME=/home/operator /bin/sh /home/operator/scripts/synchronise. This step almost always reproduces the failure instantly. - Verify writing to the destination with
sudo -u operator touchin the destination directory, in case the task runs as a different user from the one you think.
The most likely conclusion is the one from step 5. And there is a clue pointing to it from the start: if the log is empty instead of containing an error message, the command produced no output at all, which fits with a shell that could not even start the binary. An empty log is information, not an absence of information.
Conclusion
You have turned a dangerous automatic task into a defensible one.
- You know what automates well: the idempotent, the bounded, with controlled resources and visible failure. And you distinguish the user crontab from the system crontab, with its extra user field, and you know
run-partsand its fussiness about dots in names. - You have mastered the five fields with lists, ranges, steps and shortcuts, and you know that day of the month and day of the week are combined with OR, not with AND.
- You have the three classic failures sorted: cron's minimal PATH — absolute paths always — the output nobody reads —
>> log 2>&1with the2>&1at the end — and the time zone with its risky band between 02:00 and 03:00. - You debug a task methodically:
grep CRON /var/log/syslogto find out whether it ran, your own log to find out whether it worked, dumping the environment withenvand reproducing it exactly withenv -i. - You avoid overlapping with
flock -n, knowing that the kernel releases the lock and leaves no remains. - You know
anacronfor machines that get switched off and you know when a systemd timer justifies its complexity. - And you have rescheduled Tramontana's backup with a new time, a lock, an I/O priority, a scope reduced to the active release, a dated name and logging, with the purge as a separate task and an honest report about what the measure does not cover.
One front remains untouched. Everything you have done so far happens inside srv-tramontana, and a server only exists for whoever can reach it. The module's last lesson, Networking Commands, gives you the tools to look outwards: ip to read the machine's real configuration, ping, traceroute and mtr for the path, dig and /etc/hosts for name resolution, ss to find out which ports are listening and who is holding them, and curl to check whether the service really responds. All of it organised into a layered diagnostic methodology that you will apply to the case waiting for you: Marta reports that the bookings website "will not load", and you have to work out exactly where the break is.
Linux Course: From Beginner to System Administrator
Module 1: Introduction to Linux
- What Is Linux?
- History of Linux
- Linux Distributions
- Installing Linux
- First Contact with the System
- The Linux File System Structure
Module 2: Basic Linux Commands
- Introduction to the Command Line
- Getting Help and System Documentation
- Navigating the File System
- File and Directory Operations
- Viewing and Editing Files
- Hard and Symbolic Links
- File Permissions and Ownership
Module 3: Advanced Command-Line Skills
- The Shell Environment: Variables, Aliases and History
- Using Wildcards and Regular Expressions
- Searching Files and Content: find, locate and grep
- Pipes and Redirection
- Text Processing: cut, sort, uniq, sed and awk
- Process Management
- Scheduling Tasks with Cron
- Networking Commands
Module 4: Shell Scripting
- Introduction to Shell Scripting
- Variables and Data Types
- Script Input, Output and Arguments
- Control Structures
- Functions and Libraries
- Debugging and Error Handling
- Production Scripts: Best Practices
Module 5: System Administration
- User and Group Management
- sudo and Special Permissions
- Package Management
- Disk Management
- systemd and Service Management
- System Logs: journald and syslog
- System Monitoring and Performance Tuning
- Backup and Restore
Module 6: Networking and Security
- Network Configuration
- SSH and Remote Access
- Firewalls and Perimeter Security
- Intrusion Detection Systems
- Secrets Management and TLS Certificates
- Securing Linux Systems
Module 7: Advanced Topics
- The Boot Process and System Recovery
- Advanced Diagnostics: strace, perf and eBPF
- Linux Kernel Tuning
- Virtualization with Linux
- Linux Containers and Docker
- Automation with Ansible
- High Availability and Load Balancing
