Automated Backup and Log Analysis

Build a Bash toolkit that automates backups and parses system logs for errors.

Introduction

30 Second Summary

Every system administrator dreads two scenarios: losing important files with no backup, and discovering a server problem only after it becomes a disaster. Both are preventable with automation.

In this project, you will build two Bash scripts that automate compressed backups and log analysis on a Linux system, then schedule them to run hands-free with cron.

What You'll Build

By the end, you can type one command and watch a compressed, timestamped archive of your files appear. Type another and get a formatted report that tells you exactly how many errors, failed logins, and resource warnings are hiding in your system logs.

By the end of this project, you'll have:

  • A backup script you can run against any directory to create a compressed .tar.gz archive with today's timestamp, plus automatic cleanup of old backups so your disk never fills up.
  • A log analyzer script that scans your system logs and produces a clean report showing error counts, failed authentication attempts, and top processes by memory.
  • Both scripts scheduled via cron to run automatically on a daily basis without you lifting a finger.
  • Secret Mission: Add threshold-based alerting that writes to an alerts log when error counts exceed safe limits.

Are there any prerequisites?

This project assumes you are comfortable navigating the Linux filesystem, running basic terminal commands, and writing simple scripts. You should have WSL Ubuntu already installed on your Windows machine.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters for the sysadmin role you're targeting.

Set Up Your Environment

Your backup and log analysis scripts will rely on two system services that do not run by default on WSL Ubuntu.

Without rsyslog running, no log files exist for your analyzer to parse. Without cron running, you have no way to schedule scripts automatically. In this step, you will install both services, create your project directories, and confirm everything is generating data.

In this step, get ready to:
  • Install and enable cron and rsyslog services.
  • Create the project directory structure with sample data.
  • Confirm that system logs are being generated.
Verify systemd is running and install required packages

WSL Ubuntu uses systemd to manage background services. Before installing anything, you need to confirm systemd is active as PID 1.

  • Check that systemd is running by running this command in your terminal:
systemctl status --no-pager

You should see a list of loaded units with no errors about systemd being unavailable.

  • Install the cron and rsyslog packages by running:
sudo apt update && sudo apt install -y cron rsyslog

What does this command do?

This is two commands joined by &&. The first (apt update) refreshes Ubuntu's package index so it knows what versions are available. The second (apt install -y) installs both packages without prompting for confirmation.

If the packages are already installed, apt simply reports they are at the newest version. No harm done.

You should see apt fetch package lists and then confirm both packages are installed.

  • Enable and start both services immediately by running these two commands:
sudo systemctl enable --now cron
sudo systemctl enable --now rsyslog

What does enable --now do?

The enable part tells systemd to start the service automatically on boot. The --now flag also starts it immediately, combining two operations into one command.

Seeing a systemd error?

If you see an error about systemd not being available, your WSL may not have systemd enabled. You need to add systemd=true under the [boot] section in /etc/wsl.conf, then restart WSL from PowerShell with wsl.exe --shutdown.

Help me enable systemd in WSL

Create the project directory structure

Your toolkit needs dedicated directories for scripts, backups, reports, and sample data to back up.

  • Create all four project directories by running:
mkdir -p ~/sysadmin-toolkit ~/backups ~/reports ~/documents
  • Add a sample file into ~/documents so you have something to back up by running:
echo "important data" > ~/documents/config.txt
  • Confirm the directories and file exist by running:
ls ~/sysadmin-toolkit ~/backups ~/reports ~/documents

You should see config.txt listed under the ~/documents section, and the other three directories listed as empty.

Not seeing config.txt?

Make sure you ran the echo command exactly as shown, with the > redirect pointing to ~/documents/config.txt. A typo in the path creates the file somewhere else.

Help me find my config.txt file

Confirm logs are being generated

With rsyslog now active, your system should be writing entries to /var/log/syslog. Let's confirm there is data for your log analyzer to work with later.

  • Check recent rsyslog journal entries by running:
sudo journalctl -u rsyslog -b --no-pager | tail -5

You should see several timestamped log lines showing rsyslog activity since boot.

  • Verify the syslog file itself exists by running:
ls -l /var/log/syslog

You should see a file with a non-zero size, confirming rsyslog is actively writing log data.

Why check both journalctl and the file?

On modern Ubuntu, journalctl reads from the systemd journal (binary logs). The /var/log/syslog file is a plain-text log that rsyslog writes separately. Your log analyzer script will parse the text file directly with grep, so you need to confirm that file exists and is populated.

Syslog file missing or empty?

If /var/log/syslog does not exist, rsyslog may not have started successfully. Run systemctl status rsyslog to check for errors. Sometimes a short wait (10-20 seconds) is needed after enabling rsyslog before the first entries appear.

Help me troubleshoot rsyslog in WSL

Before you run the next commands, what do you think systemctl status will report for both services?

  • Run your final verification by checking both services:
systemctl status cron
systemctl status rsyslog

Both commands should show Active: active (running) in their output. This confirms your environment is fully ready for the scripts you will build next.

Your environment is ready. Both services are running, your directories are in place, and logs are flowing. Next up, you will build a backup script that compresses any directory into a timestamped archive.

Build and Run Your Backup Script

Your environment is ready with cron scheduling and rsyslog generating logs. Now it's time to build the first tool in your sysadmin toolkit: an automated backup script.

Creating tar archives manually means remembering the right flags, typing a unique filename each time, and hoping you don't overwrite yesterday's backup. A Bash script standardises this so every backup is named consistently and compressed automatically.

In this step, get ready to:
  • Create a Bash script that compresses a directory into a timestamped archive.
  • Run the script multiple times and observe archives accumulating.
Create the backup script

The script will accept a directory path as input, validate it exists, then compress it into a timestamped .tar.gz archive. If no path is given, it defaults to your ~/documents folder.

  • Open nano to create the backup script by running this command:
nano ~/sysadmin-toolkit/backup.sh

This opens an empty file in the nano text editor. Paste the following code as the first section of your script:

#!/bin/bash

# Configuration
BACKUP_SOURCE="${1:-$HOME/documents}"
BACKUP_DEST="$HOME/backups"
RETENTION_DAYS=7
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="backup_${TIMESTAMP}.tar.gz"

# Create backup destination if it doesn't exist
mkdir -p "$BACKUP_DEST"

# Check if source directory exists
if [ ! -d "$BACKUP_SOURCE" ]; then
    echo "ERROR: Source directory '$BACKUP_SOURCE' does not exist."
    exit 1
fi

What does this code do?

  • BACKUP_SOURCE="${1:-$HOME/documents}" takes the first argument the user passes in. If no argument is given, it defaults to ~/documents.
  • TIMESTAMP=$(date +%Y%m%d_%H%M%S) generates a unique name like 20260719_143022 so no two backups overwrite each other.
  • The if [ ! -d ... ] block checks whether the source directory actually exists before attempting the backup. If it doesn't, the script exits with an error message.
  • Below the validation block you just pasted, add the backup creation logic:
# Create compressed backup
echo "Starting backup of '$BACKUP_SOURCE'..."
tar -czf "${BACKUP_DEST}/${BACKUP_NAME}" -C "$(dirname "$BACKUP_SOURCE")" "$(basename "$BACKUP_SOURCE")" 2>/dev/null

if [ $? -eq 0 ]; then
    BACKUP_SIZE=$(du -h "${BACKUP_DEST}/${BACKUP_NAME}" | cut -f1)
    echo "SUCCESS: Backup created - ${BACKUP_NAME} (${BACKUP_SIZE})"
else
    echo "ERROR: Backup failed."
    exit 1
fi

What does the tar command do?

  • tar -czf creates (-c) a gzip-compressed (-z) archive file (-f).
  • -C "$(dirname ...)" changes into the parent directory first so the archive stores relative paths, not absolute ones. This makes the archive portable.
  • if [ $? -eq 0 ] checks whether the previous command (tar) succeeded. If it did, the script prints the filename and size. If it failed, the script exits with an error.
  • Save the file by pressing Ctrl+O, then press Enter to confirm the filename.
  • Exit nano by pressing Ctrl+X.

Nano not saving?

Make sure you press Ctrl+O (the letter O, not zero) to write the file. If nano shows "File Name to Write", just press Enter to accept the default path.

If you accidentally closed without saving, run the nano command again and re-paste both code sections. Help me with nano

Make the script executable and test it

Bash scripts need execute permission before you can run them directly. Without it, the system treats the file as plain text.

  • Make the script executable by running this command:
chmod +x ~/sysadmin-toolkit/backup.sh
  • Move into your toolkit directory by running:
cd ~/sysadmin-toolkit

Before you run the script, what do you think it will output if it works correctly?

  • Run the backup script targeting your ~/documents folder:
./backup.sh ~/documents

You should see output like this:

Starting backup of '/home/your-username/documents'...
SUCCESS: Backup created - backup_20260719_143022.tar.gz (4.0K)

What should I see?

The timestamp and file size in your output will differ from the example above. That's expected. The important thing is you see "SUCCESS" followed by a filename ending in .tar.gz and a size in parentheses.

Seeing 'Permission denied'?

Make sure you ran chmod +x on the correct path. Run ls -l ~/sysadmin-toolkit/backup.sh and check that the output starts with -rwxr-xr-x. If the x flags are missing, re-run the chmod command.

Help me fix permission issues with my script

  • Run the script two more times to create additional archives:
./backup.sh ~/documents
./backup.sh ~/documents

Each run creates a new archive with a slightly different timestamp (the seconds change).

  • List the contents of your backups directory to see all three archives:
ls ~/backups/

You should see three .tar.gz files, each with a unique timestamp in the filename.

Notice the piling archives?

Three runs created three separate archives. None were cleaned up. On a real server running daily backups, this fills the disk in weeks. The next step adds a retention policy that automatically deletes old archives.

✔️ Awesome, I've got everything!

Great. Make sure you've saved backup.sh and confirmed all three archives exist in ~/backups/.

ⓧ I'd like to double check the full code

Here is the complete backup.sh as it should look at this point:

#!/bin/bash

# Configuration
BACKUP_SOURCE="${1:-$HOME/documents}"
BACKUP_DEST="$HOME/backups"
RETENTION_DAYS=7
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="backup_${TIMESTAMP}.tar.gz"

# Create backup destination if it doesn't exist
mkdir -p "$BACKUP_DEST"

# Check if source directory exists
if [ ! -d "$BACKUP_SOURCE" ]; then
    echo "ERROR: Source directory '$BACKUP_SOURCE' does not exist."
    exit 1
fi

# Create compressed backup
echo "Starting backup of '$BACKUP_SOURCE'..."
tar -czf "${BACKUP_DEST}/${BACKUP_NAME}" -C "$(dirname "$BACKUP_SOURCE")" "$(basename "$BACKUP_SOURCE")" 2>/dev/null

if [ $? -eq 0 ]; then
    BACKUP_SIZE=$(du -h "${BACKUP_DEST}/${BACKUP_NAME}" | cut -f1)
    echo "SUCCESS: Backup created - ${BACKUP_NAME} (${BACKUP_SIZE})"
else
    echo "ERROR: Backup failed."
    exit 1
fi

Your backup script works perfectly. But those archives keep piling up with nothing to stop them. Next up, you'll add a retention policy that cleans up old backups automatically and schedule the whole thing with cron so it runs hands-free.

Add Retention Policy and Cron Scheduling

Your backup script works. You proved it by running it three times and watching three timestamped archives appear in ~/backups/.

On a real server running daily backups, archives pile up and fill the disk in weeks. You need two things: automatic cleanup that removes old backups, and hands-free scheduling so the script runs without you lifting a finger.

In this step, get ready to:
  • Add a retention policy that automatically deletes backups older than 7 days.
  • Schedule the backup script with cron so it runs daily at 2 AM.
  • Verify cron is firing by checking its logs.
Add a retention policy to your backup script

The find command can locate files by age and delete them in one pass. You will use it to clean up any archive older than your RETENTION_DAYS threshold every time the script runs.

  • Open backup.sh in nano by running:
nano ~/sysadmin-toolkit/backup.sh
  • Scroll to the bottom of the file. Find the closing fi line that ends the success/error check:
fi
  • Add the following code below that fi line, leaving a blank line between them:
# Apply retention policy - delete backups older than RETENTION_DAYS
echo "Applying retention policy (keeping last ${RETENTION_DAYS} days)..."
DELETED=$(find "$BACKUP_DEST" -name "backup_*.tar.gz" -mtime +${RETENTION_DAYS} -delete -print | wc -l)
echo "Cleaned up ${DELETED} old backup(s)."

echo "Backup complete. Current backups:"
ls -lh "$BACKUP_DEST"/backup_*.tar.gz 2>/dev/null

What does this code do?

The find command searches $BACKUP_DEST for files matching the pattern backup_*.tar.gz that were modified more than 7 days ago. The -delete flag removes each match, and -print outputs the deleted filenames so wc -l can count them.

The final ls -lh line shows all remaining backups after cleanup runs. This gives you a quick visual confirmation every time the script executes.

  • Save the file with Ctrl+O, press Enter to confirm, then exit nano with Ctrl+X.
  • Run the updated script to see the retention policy in action:
./backup.sh ~/documents

You should see your usual SUCCESS line, followed by "Applying retention policy" and "Cleaned up 0 old backup(s)." since none of your archives are older than 7 days yet.

Seeing a permission denied or syntax error?

Make sure you are running the script from inside ~/sysadmin-toolkit/. Run cd ~/sysadmin-toolkit first. Also verify the file is still executable with ls -l backup.sh and confirm you see -rwx at the start of the permissions.

If the retention find line throws a syntax error, check that your quotes match exactly. Every double quote in the line must be a standard double quote character.

Help me debug my backup.sh retention policy code

Why not delete immediately when archives are made?

Retention policies check file age at runtime. This means if your cron job fails for a few days, older backups are still available as a safety net. Deleting at creation time would mean you only ever keep one backup.

✔️ Awesome, I've got everything!

Great. Double check you saved backup.sh before continuing.

ⓧ I'd like to double check the full code

Here is the full backup.sh split into two parts. Compare each section against your file.

#!/bin/bash

# Configuration
BACKUP_SOURCE="${1:-$HOME/documents}"
BACKUP_DEST="$HOME/backups"
RETENTION_DAYS=7
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="backup_${TIMESTAMP}.tar.gz"

# Create backup destination if it doesn't exist
mkdir -p "$BACKUP_DEST"

# Check if source directory exists
if [ ! -d "$BACKUP_SOURCE" ]; then
    echo "ERROR: Source directory '$BACKUP_SOURCE' does not exist."
    exit 1
fi
# Create compressed backup
echo "Starting backup of '$BACKUP_SOURCE'..."
tar -czf "${BACKUP_DEST}/${BACKUP_NAME}" -C "$(dirname "$BACKUP_SOURCE")" "$(basename "$BACKUP_SOURCE")" 2>/dev/null

if [ $? -eq 0 ]; then
    BACKUP_SIZE=$(du -h "${BACKUP_DEST}/${BACKUP_NAME}" | cut -f1)
    echo "SUCCESS: Backup created - ${BACKUP_NAME} (${BACKUP_SIZE})"
else
    echo "ERROR: Backup failed."
    exit 1
fi

# Apply retention policy - delete backups older than RETENTION_DAYS
echo "Applying retention policy (keeping last ${RETENTION_DAYS} days)..."
DELETED=$(find "$BACKUP_DEST" -name "backup_*.tar.gz" -mtime +${RETENTION_DAYS} -delete -print | wc -l)
echo "Cleaned up ${DELETED} old backup(s)."

echo "Backup complete. Current backups:"
ls -lh "$BACKUP_DEST"/backup_*.tar.gz 2>/dev/null
Schedule the backup with cron

Your backup script works perfectly when you run it manually. But sysadmins do not sit around at 2 AM typing commands. Cron is a scheduling daemon that runs commands at intervals you define in a crontab file.

What is a crontab?

A crontab is a per-user schedule file. Each line has five time fields (minute, hour, day-of-month, month, day-of-week) followed by the command to run. The entry 0 2 * * * means "at minute 0 of hour 2, every day, every month, every day-of-week."

  • Open your crontab for editing by running:
crontab -e

What will I see?

If this is your first time editing a crontab, Ubuntu may ask you to choose an editor. Select nano (option 1). The file opens with comment lines starting with # explaining the format.

  • Scroll to the bottom of the file (past the comment lines).
  • Add these two lines at the very end, replacing YOUR_USERNAME with your actual Linux username:
0 2 * * * /home/[[USERNAME="your Linux username"]]/sysadmin-toolkit/backup.sh /home/[[USERNAME="your Linux username"]]/documents >> /home/[[USERNAME="your Linux username"]]/reports/backup.log 2>&1
* * * * * echo "cron works $(date)" >> /tmp/cron-test.txt

What do these entries do?

The first line runs your backup script every day at 2:00 AM and appends any output to ~/reports/backup.log. The 2>&1 part redirects error messages into the same log file so nothing is lost.

The second line is a test entry that writes to /tmp/cron-test.txt every single minute. You will use it to confirm cron is actually firing, then remove it.

Find your username quickly

Not sure what your Linux username is? Run whoami in a separate terminal and it prints your username.

  • Save and exit nano with Ctrl+O, Enter, then Ctrl+X.

You should see a message like "crontab: installing new crontab" confirming your schedule is active.

  • Now wait about 60 seconds for the test entry to fire at least once.

Seeing errors when saving the crontab?

Cron is strict about formatting. Make sure there are no blank lines between your entries, and that each line has exactly five time fields before the command. If you see "errors in crontab, can't install", open it again with crontab -e and compare your entry character-by-character against the example above.

Help me fix my crontab formatting

Verify cron is running and logging

You need proof that cron actually executed your test entry. Two checks will confirm this: the test file it writes to, and the systemd journal that logs every cron execution.

  • After waiting at least 60 seconds, check the test file by running:
cat /tmp/cron-test.txt

You should see at least one line saying "cron works" followed by a timestamp. Each line represents one minute that cron fired.

  • Confirm cron is logging to the systemd journal by running:
sudo journalctl -u cron --no-pager | tail -5

You should see recent entries showing cron executing your commands, with timestamps from the last few minutes.

  • Now remove the test entry. Open your crontab again:
crontab -e
  • Delete the line that starts with * * * * * echo (the test entry). Leave the backup entry in place.
  • Save and exit with Ctrl+O, Enter, then Ctrl+X.

Before you run the final check, what do you expect crontab -l to show you now?

  • Verify your final crontab by running:
crontab -l

You should see only your backup entry: the line starting with 0 2 * * * pointing at your backup.sh script. The test entry is gone.

Don't see anything in /tmp/cron-test.txt?

First check that cron is actually running with systemctl status cron. If it shows "inactive", start it with sudo systemctl start cron.

If cron is active but the file is missing, make sure you waited at least 60 full seconds. Cron fires at the start of each minute, so if you saved your crontab at 2:00:45, the first execution happens at 2:01:00.

Help me troubleshoot why cron is not executing my test entry

Why does cron only run while WSL is active?

On a real Linux server, cron runs 24/7. In WSL, the entire Linux instance shuts down when you close all terminals and no background processes are running. Your 2 AM backup job only fires if WSL is still awake at that time. On a production server, this is not an issue.

Your backup system is now fully automated. It creates compressed archives, cleans up old ones, and runs on a schedule without you touching a thing. Next up, you will build a log analyzer that gives you visibility into what is actually happening on your system.

Build the Log Analyzer

Your backup script protects data, but you still have no visibility into what is actually happening on the system. Errors, failed logins, and resource pressure hide silently in log files until someone manually investigates.

In this step, you will build a log analyzer script that parses /var/log/syslog and /var/log/auth.log, counts errors and suspicious activity, and writes a formatted report you can read at a glance. This surfaces problems proactively instead of waiting for something to break.

In this step, get ready to:
  • Generate log activity so you have real entries to analyze.
  • Create a log analyzer script that parses system logs and writes a summary report.
  • Schedule the analyzer to run daily via cron.
Generate log activity to analyze

Before running a log analyzer, you need actual log entries to parse. Your system already has some entries from earlier steps, but a few extra commands will give you richer data to work with.

  • Generate authentication log entries by running:
  • Run a few commands that create entries in different log files:
sudo ls /root
logger "Test error message"
sudo systemctl restart rsyslog

What do these commands do?

  • sudo ls /root creates an entry in /var/log/auth.log because you used sudo.
  • logger "Test error message" writes a custom message directly to /var/log/syslog.
  • sudo systemctl restart rsyslog creates service start/stop events in the system journal.
  • Wait a few seconds for logs to flush, then confirm entries exist by running:
tail -3 /var/log/syslog

You should see recent log entries including your "Test error message" line.

Not seeing log entries?

  • Make sure rsyslog is running. Check with systemctl status rsyslog.
  • If the file does not exist, rsyslog may not have been started in a previous step. Run sudo systemctl enable --now rsyslog and wait 10 seconds.
  • Help me troubleshoot missing syslog entries in WSL.
Create the log analyzer script

The analyzer script reads two log files, counts different categories of events using grep, and writes a formatted report to ~/reports/. You will build it section by section so each part is clear.

  • Open a new file in nano by running:
nano ~/sysadmin-toolkit/log-analyzer.sh
  • Add the shebang, configuration variables, and report header:
#!/bin/bash

# Configuration
LOG_FILE="/var/log/syslog"
AUTH_LOG="/var/log/auth.log"
REPORT_DIR="$HOME/reports"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
REPORT_FILE="${REPORT_DIR}/log_report_${TIMESTAMP}.txt"

# Create report directory
mkdir -p "$REPORT_DIR"

# Start report
echo "========================================" > "$REPORT_FILE"
echo " System Log Analysis Report" >> "$REPORT_FILE"
echo " Generated: $(date)" >> "$REPORT_FILE"
echo "========================================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"

What does this code do?

  • LOG_FILE and AUTH_LOG point to the two system log files rsyslog maintains.
  • REPORT_FILE uses a timestamp so each run creates a unique report instead of overwriting the last one.
  • The single > on the first echo overwrites any existing file. Every echo after that uses >> to append.
  • Below the report header, add the syslog error summary section:
# Section 1: Error summary from syslog
echo "--- SYSLOG ERROR SUMMARY ---" >> "$REPORT_FILE"
if [ -r "$LOG_FILE" ]; then
    ERROR_COUNT=$(grep -ci "error" "$LOG_FILE")
    WARNING_COUNT=$(grep -ci "warning" "$LOG_FILE")
    CRITICAL_COUNT=$(grep -ci "critical\|emergency\|alert" "$LOG_FILE")
    echo "Errors: ${ERROR_COUNT}" >> "$REPORT_FILE"
    echo "Warnings: ${WARNING_COUNT}" >> "$REPORT_FILE"
    echo "Critical/Emergency/Alert: ${CRITICAL_COUNT}" >> "$REPORT_FILE"
    echo "" >> "$REPORT_FILE"
    echo "Most recent errors:" >> "$REPORT_FILE"
    grep -i "error" "$LOG_FILE" | tail -5 >> "$REPORT_FILE"
else
    echo "Cannot read $LOG_FILE (permission denied or missing)" >> "$REPORT_FILE"
fi
echo "" >> "$REPORT_FILE"

What does this code do?

  • grep -ci counts matching lines case-insensitively. The -c flag returns a count instead of the matching lines themselves.
  • if [ -r "$LOG_FILE" ] checks whether the script has read permission before trying to parse. This prevents ugly errors if the file is missing or locked.
  • The tail -5 at the end grabs only the five most recent error lines so the report stays concise.
  • Continue adding the authentication analysis section:
# Section 2: Authentication analysis
echo "--- AUTHENTICATION ANALYSIS ---" >> "$REPORT_FILE"
if [ -r "$AUTH_LOG" ]; then
    FAILED_AUTH=$(grep -ci "authentication failure\|failed password" "$AUTH_LOG")
    SUDO_USAGE=$(grep -c "sudo:" "$AUTH_LOG")
    SESSION_OPENED=$(grep -c "session opened" "$AUTH_LOG")
    echo "Failed authentication attempts: ${FAILED_AUTH}" >> "$REPORT_FILE"
    echo "Sudo commands executed: ${SUDO_USAGE}" >> "$REPORT_FILE"
    echo "Sessions opened: ${SESSION_OPENED}" >> "$REPORT_FILE"
    echo "" >> "$REPORT_FILE"
    echo "Recent failed attempts:" >> "$REPORT_FILE"
    grep -i "authentication failure\|failed password" "$AUTH_LOG" | tail -5 >> "$REPORT_FILE"
else
    echo "Cannot read $AUTH_LOG (permission denied or missing)" >> "$REPORT_FILE"
fi
echo "" >> "$REPORT_FILE"

Why track authentication events?

Failed login attempts and sudo usage are the first indicators of unauthorized access. On a production server, a spike in failed attempts often means someone is trying to brute-force their way in.

Tracking sudo commands also tells you who used elevated privileges and when.

  • Add the final sections for disk usage, top processes, and the terminal summary:
# Section 3: Disk usage
echo "--- DISK USAGE ---" >> "$REPORT_FILE"
df -h / /tmp 2>/dev/null >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"

# Section 4: Top processes by memory
echo "--- TOP PROCESSES (by memory) ---" >> "$REPORT_FILE"
ps aux --sort=-%mem | head -6 >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"

echo "========================================" >> "$REPORT_FILE"
echo " End of Report" >> "$REPORT_FILE"
echo "========================================" >> "$REPORT_FILE"

# Display summary to terminal
echo "Log analysis complete!"
echo "Report saved to: ${REPORT_FILE}"
echo ""
echo "Quick summary:"
if [ -r "$LOG_FILE" ]; then
    echo "  Syslog errors: ${ERROR_COUNT}"
fi
if [ -r "$AUTH_LOG" ]; then
    echo "  Failed auth attempts: ${FAILED_AUTH}"
fi

What does this code do?

  • df -h / /tmp shows disk usage in human-readable format for root and temp partitions. A full disk is one of the most common causes of service failure.
  • ps aux --sort=-%mem | head -6 lists the top 5 memory-consuming processes (plus the header line). This reveals runaway processes before they cause out-of-memory problems.
  • The terminal summary at the end gives you a quick at-a-glance view without needing to open the full report file.
  • Save the file by pressing Ctrl+O, then Enter to confirm the filename.
  • Exit nano by pressing Ctrl+X.

✔️ Awesome, I've got everything!

Great. Double check you have saved your file and exited nano before moving on.

ⓧ I'd like to double check the full code

Your log-analyzer.sh should contain all four chunks in this order. Scroll up and confirm each one is present:

  • Configuration variables and report header (shebang through the four echo lines that create the report banner).
  • Section 1: Syslog error summary (the if [ -r "$LOG_FILE" ] block counting errors, warnings, and critical events).
  • Section 2: Authentication analysis (the if [ -r "$AUTH_LOG" ] block counting failed auth, sudo usage, and sessions).
  • Sections 3 and 4: Disk usage, top processes, report footer, and terminal summary.

If anything is missing, reopen the file with nano ~/sysadmin-toolkit/log-analyzer.sh and add the missing section.

Run the log analyzer and schedule it

Your script is written but not yet executable. You will mark it as executable, run it with sudo (needed to read /var/log/auth.log), and then schedule it in cron to run daily.

  • Make the script executable by running:
chmod +x ~/sysadmin-toolkit/log-analyzer.sh

Before you run the script, what do you think the terminal output will look like?

  • Run the log analyzer with sudo by running:
sudo ~/sysadmin-toolkit/log-analyzer.sh

You should see output like this:

Log analysis complete!
Report saved to: /home/your-user/reports/log_report_20260719_143022.txt

Quick summary:
  Syslog errors: 3
  Failed auth attempts: 0

Why does this need sudo?

/var/log/auth.log is owned by the syslog user and the adm group. Without sudo, the script cannot read it and you would see the "permission denied" fallback message in your report instead of actual data.

  • Open the full report to confirm all sections are populated by running:
cat ~/reports/log_report_*.txt

You should see formatted sections for syslog errors, authentication analysis, disk usage, and top processes.

Seeing "Cannot read" in the report?

  • This means you ran the script without sudo. Delete the report with rm ~/reports/log_report_*.txt and rerun using sudo ~/sysadmin-toolkit/log-analyzer.sh.
  • If the syslog section shows 0 errors, that just means your system is healthy. The "Test error message" from logger may not match the word "error" in a way grep catches. This is normal.
  • Help me troubleshoot my log analyzer report output.
  • Schedule the log analyzer to run daily at 2:30 AM by opening your crontab:
crontab -e
  • Add this line below your existing backup entry:
30 2 * * * sudo /home/[[USERNAME="your actual username"]]/sysadmin-toolkit/log-analyzer.sh

What does this cron schedule mean?

The five fields are: minute (30), hour (2), day of month (*), month (*), day of week (*). This means "run at 2:30 AM every day."

The backup script runs at 2:00 AM and the analyzer at 2:30 AM. This gives backups time to finish before the log analysis begins.

  • Save and exit the crontab editor.
  • Verify your full crontab by running:
crontab -l

You should see two entries: your backup job at 0 2 * * * and your log analyzer job at 30 2 * * *.

Only seeing one crontab entry?

  • Make sure you added the new line on a separate line below the existing backup entry. Each job needs its own line.
  • Run crontab -e again and check that both lines are present.
  • Help me fix my crontab entries for the log analyzer.

Your sysadmin toolkit now protects data with automated backups and surfaces problems from logs with a single command. Next up, you will take this even further in a Secret Mission that adds threshold-based alerting to your analyzer.

Secret mission

Add Threshold-Based Alerting

Your log analyzer tells you what happened, but only if you remember to read the report. On a production server, you need the system to tell YOU when something crosses a danger line. Can you add threshold-based alerting that actively writes to an alerts log when error counts get too high?

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally on your WSL Ubuntu instance, so there are no ongoing cloud costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Project scripts in ~/sysadmin-toolkit/ (backup.sh, log-analyzer.sh).
  • Backup archives in ~/backups/.
  • Generated reports and alerts in ~/reports/.
  • Sample data in ~/documents/.
  • Two cron job entries (backup and log analyzer schedules).
  • cron and rsyslog packages and services.

Keep everything running

No action needed. Choose this if you are still actively building on your sysadmin toolkit or plan to add more scripts.

  • Your cron jobs will continue to run whenever your WSL instance is active.
  • Backup archives will accumulate in ~/backups/ but the retention policy automatically cleans files older than 7 days.
  • Reports will accumulate in ~/reports/. You can manually delete old ones at any time.

Pause - I'll come back to this later

Stop the scheduled jobs and services to free up resources, but keep your scripts and files intact for later.

  • Remove your cron entries by running:
  • Stop the cron and rsyslog services by running:
crontab -r
sudo systemctl stop cron
sudo systemctl stop rsyslog

Your scripts and generated files remain in place. When you return, re-enable the services and re-add your cron entries.

Delete - I don't want to use this again

Remove all project resources and start fresh. This deletes your scripts, backups, reports, sample data, and scheduled jobs.

  • Remove all cron entries by running:
crontab -r
  • Delete the project directories by running:
rm -rf ~/sysadmin-toolkit ~/backups ~/reports ~/documents
  • Stop and disable the cron and rsyslog services by running:
sudo systemctl stop cron
sudo systemctl disable cron
sudo systemctl stop rsyslog
sudo systemctl disable rsyslog
  • Uninstall the cron and rsyslog packages by running:
sudo apt remove -y cron rsyslog
  • Confirm the directories are gone by running:
ls ~/sysadmin-toolkit ~/backups ~/reports ~/documents

You should see "No such file or directory" for each path. Everything is cleaned up.

Nice Work!

Nice Work!

Nice work! You just built a production-style sysadmin toolkit that automates the two tasks every junior Linux administrator is expected to handle: protecting data with backups and surfacing problems from logs.

You've learned how to:

  • Build a compressed backup script with tar that creates timestamped archives and automatically cleans up old backups with a retention policy to prevent disk fill.
  • Build a log analyzer that parses /var/log/syslog and /var/log/auth.log for errors, failed authentication attempts, and resource usage, writing a formatted summary report.
  • Schedule both scripts with cron for hands-free automation, and learned how systemd, rsyslog, and cron work together on a Linux system.
  • Secret Mission: Added threshold-based alerting that actively notifies you when error counts or failed authentication attempts exceed safe limits.

Ready to quiz yourself?