THU.JUN.18
2026
23:37:22
← back to modules MODULE · 01 · PHP
0 / 10 chapters complete · 0%

Maintenance — Keep It Running

Set up three habits once, then stop thinking about them: backups, log rotation, auto-updates. This separates "I have a server" from "I run a server."
Cron + mysqldump for nightly backups. logrotate is already configured for Apache; add a PHP rule. unattended-upgrades for auto security patches. Done forever.

Cron — Linux's scheduler in 60 seconds

Cron lets you run any command on a schedule. Format:

minute hour day-of-month month day-of-week command

Examples:

  • 0 3 * * * = 3:00 AM every day
  • */15 * * * * = every 15 minutes
  • 0 0 * * 0 = midnight on Sundays

Build the backup script

Create the script:
sudo nano /usr/local/bin/backup-db.sh
Paste:
#!/usr/bin/env bash
set -euo pipefail

DEST=/home/erictey/backups/db
mkdir -p "$DEST"

DATE=$(date +%F)
FILE="$DEST/all-$DATE.sql.gz"

mysqldump --all-databases --single-transaction --routines --triggers --events \\
    | gzip > "$FILE"

# Keep last 14 days only
find "$DEST" -name '*.sql.gz' -mtime +14 -delete

echo "Backed up to $FILE"
Make it executable:
sudo chmod 755 /usr/local/bin/backup-db.sh
sudo chown root:root /usr/local/bin/backup-db.sh
Test it BEFORE trusting cron. Run by hand:
sudo /usr/local/bin/backup-db.sh
Should print "Backed up to /home/erictey/backups/db/all-2026-05-29.sql.gz" (today's date). Check the file exists:
ls -lh /home/erictey/backups/db/

Schedule it

Edit root's crontab:
sudo crontab -e
(If asked which editor, pick nano = 1.) Add at the bottom:
0 3 * * *   /usr/local/bin/backup-db.sh >> /var/log/backup-db.log 2>&1
Save and exit. That runs at 3 AM every day, logs output to a file you can inspect later.

Backup project files too

Same pattern. Create /usr/local/bin/backup-files.sh:

#!/usr/bin/env bash
set -euo pipefail

DEST=/home/erictey/backups/files
mkdir -p "$DEST"

tar -czf "$DEST/server-$(date +%F).tar.gz" /home/erictey/server
find "$DEST" -name '*.tar.gz' -mtime +14 -delete

Cron it at 3:30 AM (slightly different time so the two jobs don't fight for disk):

30 3 * * *   /usr/local/bin/backup-files.sh >> /var/log/backup-files.log 2>&1

Auto-install security updates

Single highest-leverage thing you can do for security:

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure unattended-upgrades

Say yes when prompted. Now security patches install every night while you sleep.

mDNS — find the server by name without hosts file edits

sudo apt install avahi-daemon -y
sudo systemctl enable --now avahi-daemon

Now Mac/iOS/Windows 10+/Linux devices on the LAN can reach the server at hostname.local with zero per-device config. Like AirDrop, but for network names.

Build: Health Check Dashboard

Goal: one page you can visit weekly to confirm everything's still happy.

  1. Create /home/erictey/server/status.php
  2. Paste:
    <?php
    header('Content-Type: text/plain; charset=utf-8');
    
    echo "=== SERVER STATUS ===\\n\\n";
    echo "PHP version:       " . PHP_VERSION . "\\n";
    echo "Loaded php.ini:    " . php_ini_loaded_file() . "\\n";
    echo "Server software:   " . $_SERVER['SERVER_SOFTWARE'] . "\\n";
    echo "Timezone:          " . date_default_timezone_get() . " (now: " . date('c') . ")\\n";
    
    try {
        $pdo = new PDO('mysql:host=127.0.0.1', 'medtrack_user', 'YOUR_PW');
        $row = $pdo->query("SELECT VERSION() v")->fetch();
        echo "MariaDB version:   " . $row['v'] . " ✓\\n";
    } catch (Throwable $e) {
        echo "MariaDB:           ✗ FAILED - " . $e->getMessage() . "\\n";
    }
    
    $free = disk_free_space('/');
    echo "Disk free (/):     " . round($free / 1e9, 1) . " GB\\n";
    echo "memory_limit:      " . ini_get('memory_limit') . "\\n";
    echo "Uptime:            " . trim(file_get_contents('/proc/uptime')) . " s\\n";
    
    // Backup freshness
    $last = glob('/home/erictey/backups/db/*.sql.gz');
    if ($last) {
        $newest = max(array_map('filemtime', $last));
        $age_h = round((time() - $newest) / 3600, 1);
        echo "Last DB backup:    {$age_h}h ago\\n";
    } else {
        echo "Last DB backup:    NO BACKUPS FOUND ✗\\n";
    }
    ?>
  3. Sub in your real DB password
  4. Visit http://192.168.0.19/status.php
  5. You should see a clean status report. All systems good?

Stretch: protect this page with basic auth so only you can see it. Create /etc/apache2/.htpasswd with sudo htpasswd -c /etc/apache2/.htpasswd erictey, then add an .htaccess file in the same folder.

Stop and look at what you've built

  • ✓ Lubuntu running on real hardware
  • ✓ LAMP stack (Apache + MariaDB + PHP)
  • ✓ Project served from your home folder
  • ✓ SSH from Windows, VS Code editing files remotely
  • ✓ Reachable from any device on the LAN
  • ✓ Vhosts for clean hostnames
  • ✓ HTTPS with green padlock
  • ✓ Backups running on a schedule
  • ✓ Security updates installing themselves
  • ✓ A status page to monitor it all

That's a real server. Not a tutorial sandbox — the same kind of stack that powers a meaningful slice of the working internet.

Now stop installing things and start building. Move on to Part 2 and learn the PHP language itself — by the end, MedTrack will be a real app, not just a folder.