Cron-Jobs einrichten — Aufgaben automatisch ausführen lassen
Cron ist ein Daemon, der im Hintergrund läuft und gespeicherte Zeitpläne abarbeitet. Jede Minute wacht er auf, prüft alle Crontabs und führt passende Befehle aus — egal ob du am Rechner sitzt oder nicht. Wenn ja, führt er ihn aus — egal ob du am Rechner sitzt oder nicht. Das macht cron zum einfachsten Weg, wiederkehrende Aufgaben zu automatisieren: Backups, Log-Rotationen, Update-Checks, Benachrichtigungen.
Was cron nicht ist: ein Job-Manager mit Fehlerbehandlung, Abhängigkeiten oder Retry-Logik. Für einfache zeitgesteuerte Befehle reicht er vollkommen aus. Für komplexere Setups gibt es systemd-Timer — dazu am Ende mehr. Und falls du Cron bisher gemieden hast, weil du dir die Syntax nicht merken konntest: Nach diesem Artikel wirst du die fünf Felder auswendig kennen.
Die Crontab-Syntax: Fünf Felder, eine Zeile
Jeder Cron-Eintrag besteht aus genau einer Zeile mit fünf Zeitfeldern, gefolgt vom Befehl:
# .------------ Minute (0 - 59)
# | .--------- Stunde (0 - 23)
# | | .------ Tag im Monat (1 - 31)
# | | | .--- Monat (1 - 12)
# | | | | . Wochentag (0 - 7, Sonntag = 0 oder 7)
# | | | | |
* * * * * /pfad/zum/befehl
Der Sternchen * steht für „jeden möglichen Wert“. Ein Eintrag mit fünf Sternchen läuft jede Minute:
* * * * * echo "Jede Minute" >> /tmp/test.log
Sonderzeichen im Überblick:
*— jeder Wert (immer)5— genau dieser Wert (z. B. Minute 5)5,15,45— Aufzählung, mehrere Werte8-17— Bereich (von Stunde 8 bis 17)*/6— jeder 6. Schritt (Minute 0, 6, 12, 18, 24, 30, 36, 42, 48, 54)10-50/10— Bereich mit Schritt (10, 20, 30, 40, 50)
Ein paar Beispiele, um ein Gefühl für die Syntax zu bekommen:
# Täglich um 2:00 Uhr nachts:
0 2 * * * /usr/local/bin/backup.sh
# Jeden Montag um 7:30 Uhr:
30 7 * * 1 /usr/local/bin/wochenstart.sh
# Alle 15 Minuten:
*/15 * * * * /usr/local/bin/status-check.sh
# Jeden 1. und 15. des Monats um 10:00 Uhr:
0 10 1,15 * * /usr/local/bin/abrechnung.sh
# Werktags (Mo-Fr) von 9 bis 17 Uhr, jede volle Stunde:
0 9-17 * * 1-5 /usr/local/bin/sync.sh
Neben der manuellen Syntax gibt es einige vordefinierte Makros, die leichter lesbar sind:
@reboot /usr/local/bin/start-bei-systemstart.sh
@hourly /usr/local/bin/jede-stunde.sh
@daily /usr/local/bin/taeglich.sh
@weekly /usr/local/bin/woechentlich.sh
@monthly /usr/local/bin/monatlich.sh
@yearly /usr/local/bin/jaehrlich.sh
@reboot ist besonders nützlich: Der Befehl wird einmalig ausgeführt, wenn cron nach einem Neustart startet — nicht sofort beim Einloggen, sondern sobald der cron-Daemon läuft.
Crontab bearbeiten: -e, -l, -r
Jeder Benutzer hat seine eigene Crontab-Datei, die cron separat verwaltet. Du bearbeitest sie ausschließlich über den Befehl crontab -e — nie direkt die Systemdatei anfassen.
# Eigene Crontab öffnen und bearbeiten:
$ crontab -e
# Aktuelle Crontab anzeigen (ohne zu öffnen):
$ crontab -l
# Eintrag kommentieren statt löschen — sicherer Weg:
# 0 2 * * * /usr/local/bin/backup.sh
Beim ersten Aufruf von crontab -e fragt das System nach dem gewünschten Editor. Auf Ubuntu und Debian-basierten Systemen erscheint eine Auswahl:
$ crontab -e
no crontab for gunter - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
4. /bin/ed
Choose 1-4 [1]:
Für Einsteiger empfehle ich nano (Option 1). Den Editor dauerhaft ändern:
# Einmalig für die Zukunft ändern:
$ select-editor
# Oder als Umgebungsvariable vor crontab:
$ EDITOR=nano crontab -e
# Dauerhaft in ~/.bashrc setzen:
$ echo 'export EDITOR=nano' >> ~/.bashrc
$ source ~/.bashrc
crontab -r löscht die gesamte Crontab sofort und ohne Rückfrage. Kein Bestätigungsdialog, kein Papierkorb. Wenn du nur einen einzelnen Eintrag entfernen willst, öffne die Crontab mit crontab -e und lösche die jeweilige Zeile. Alternativ: Vor jeder Änderung ein Backup anlegen mit crontab -l > ~/crontab-backup.txt.crontab -l ist der schnellste Weg, die aktuellen Einträge zu überblicken, ohne etwas zu verändern:
$ crontab -l
# Crontab von Gunter Herdrich, zuletzt geändert 2026-04-10
0 2 * * * /home/gunter/.local/bin/backup.sh >> /var/log/backup.log 2>&1
*/6 * * * * /home/gunter/.local/bin/status.sh
@weekly /home/gunter/.local/bin/log-cleanup.sh
Praxisbeispiele: Vier typische Cron-Jobs
Die Theorie ist schnell erklärt — am besten lernst du Cron durch Beispiele, die du direkt übernehmen und anpassen kannst.
Tägliches Backup um 2 Uhr nachts
Dieser Job sichert den Home-Ordner per rsync auf ein externes Laufwerk. Der Befehl läuft jeden Tag um 2:00 Uhr, Ausgabe landet im Log:
0 2 * * * rsync -a --delete /home/gunter/ /mnt/backup/gunter/ >> /var/log/backup-home.log 2>&1
Wenn rsync noch nicht vertraut ist: Die Seite Backups mit rsync erklärt die wichtigsten Optionen und zeigt, wie du inkrementelle Backups mit Zeitstempel einrichtest.
Wöchentlicher Log-Cleanup
Alte Log-Dateien im Home-Verzeichnis älter als 30 Tage werden jeden Sonntag um 3:15 Uhr gelöscht:
15 3 * * 0 find /home/gunter/logs/ -name "*.log" -mtime +30 -delete
Der Wochentag 0 steht für Sonntag. Alternativ funktioniert auch 7 für Sonntag — cron akzeptiert beides.
System-Update-Check alle 6 Stunden
Dieser Job prüft, ob Updates verfügbar sind, und schreibt die Anzahl in eine Datei — ohne automatisch zu installieren:
0 */6 * * * apt list --upgradable 2>/dev/null | wc -l > /home/gunter/.update-count
Danach kannst du mit einem einfachen cat ~/.update-count sehen, wie viele Pakete auf ein Update warten. Oder das Ergebnis in einem Shell-Script auswerten und eine Benachrichtigung senden.
Desktop-Benachrichtigung
Cron-Jobs laufen ohne grafische Oberfläche — aber mit einem kleinen Trick kannst du Desktop-Notifications verschicken. Das erfordert zwei zusätzliche Variablen für die Display- und D-Bus-Verbindung:
DISPLAY=:0
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
0 9 * * 1-5 notify-send "Guten Morgen" "$(date +%A), Zeit für den ersten Kaffee."
/run/user/1000/bus ist bei den meisten Einzelplatz-Systemen 1000. Deine eigene ID findest du mit id -u. Bei anderen Werten den Pfad entsprechend anpassen.Ausgabe und Logging
Cron führt Befehle ohne Terminal aus — Ausgabe auf stdout und stderr verschwindet, wenn du sie nicht explizit weiterleitest. Standardmäßig versucht cron, Ausgabe per E-Mail an den lokalen Systemuser zu schicken. Auf Desktop-Systemen ist das in der Regel nicht konfiguriert und landet im Nirwana.
Ausgabe in eine Log-Datei umleiten:
# Nur stdout in Datei (stderr erscheint weiterhin in der Cron-Mail oder geht verloren):
0 2 * * * /home/gunter/.local/bin/backup.sh >> /var/log/backup.log
# Stdout UND stderr in dieselbe Datei (empfohlen):
0 2 * * * /home/gunter/.local/bin/backup.sh >> /var/log/backup.log 2>&1
# Stderr separat loggen:
0 2 * * * /home/gunter/.local/bin/backup.sh >> /var/log/backup.log 2>> /var/log/backup-errors.log
Die Notation 2>&1 leitet stderr (Dateideskriptor 2) auf stdout (Dateideskriptor 1) um — beides landet dann zusammen in der Logdatei. Das doppelte >> hängt die Ausgabe an die Datei an, statt sie zu überschreiben.
Stumme Jobs mit /dev/null:
# Ausgabe komplett unterdrücken (nur sinnvoll, wenn der Job nie Fehler produziert):
*/5 * * * * /home/gunter/.local/bin/heartbeat.sh > /dev/null 2>&1
E-Mail-Benachrichtigungen steuern mit MAILTO:
# Ganz oben in der Crontab setzen:
MAILTO="" # Keine E-Mails für alle Jobs in dieser Crontab
# Oder für einen bestimmten Job übersteuern:
MAILTO="[email protected]"
0 2 * * * /home/gunter/.local/bin/backup.sh
Auf Desktop-Systemen ohne lokalen Mailserver empfehle ich, entweder MAILTO="" zu setzen und Ausgabe in Log-Dateien zu leiten, oder beide Ströme nach /dev/null umzuleiten — je nachdem, ob du Ausgabe später analysieren willst oder nicht.
Häufige Fehler und wie du sie vermeidest
Die meisten Probleme mit Cron-Jobs entstehen aus einem einzigen Grund: Cron läuft in einer sehr reduzierten Umgebung — kein Display, kein vollständiges PATH, keine Umgebungsvariablen aus deiner Shell-Sitzung.
Das PATH-Problem
Deine interaktive Bash-Sitzung hat einen langen PATH mit Dutzenden Verzeichnissen. Cron startet mit einem minimalen Standard-PATH, der je nach Distribution nur /usr/bin:/bin oder ähnliches enthält:
# Testen: was ist der Cron-PATH?
* * * * * env > /tmp/cron-env.txt
# Inhalt von /tmp/cron-env.txt (typisches Beispiel):
HOME=/home/gunter
LOGNAME=gunter
PATH=/usr/bin:/bin
SHELL=/bin/sh
Lösung: Immer absolute Pfade verwenden, oder PATH in der Crontab oben definieren:
# Oben in der Crontab — gilt für alle Jobs darunter:
PATH=/usr/local/bin:/usr/bin:/bin:/home/gunter/.local/bin
# Oder pro Job mit absolutem Pfad:
0 2 * * * /usr/bin/rsync -a /home/gunter/ /mnt/backup/gunter/
Relative Pfade vermeiden
Cron setzt das Arbeitsverzeichnis auf das Home-Verzeichnis des Benutzers — nicht auf das Verzeichnis, in dem das Script liegt. Relative Pfade im Script funktionieren deshalb nur zuverlässig, wenn du explizit cd ans Anfang des Scripts stellst:
#!/bin/bash
# Erstes: ins eigene Verzeichnis wechseln
cd "$(dirname "$0")" || exit 1
# Jetzt funktionieren relative Pfade:
./hilfsskript.sh
cat config.txt
Fehlende Umgebungsvariablen
Werte aus ~/.bashrc, ~/.profile oder ~/.bash_profile stehen in Cron-Jobs nicht zur Verfügung — diese Dateien werden von Cron nicht geladen. Wenn dein Script auf Variablen wie $HOME, $LANG oder eigene Custom-Variablen angewiesen ist, definiere sie direkt in der Crontab oder am Anfang des Scripts:
# In der Crontab:
LANG=de_DE.UTF-8
HOME=/home/gunter
0 2 * * * /home/gunter/.local/bin/backup.sh
# Oder im Script selbst:
#!/bin/bash
export LANG=de_DE.UTF-8
export HOME=/home/gunter
Berechtigungsprobleme
Das Script muss ausführbar sein und auf alle benötigten Dateien zugreifen dürfen. Cron führt deine Crontab als dein Benutzer aus — aber root-Rechte oder sudo sind nicht automatisch vorhanden:
# Script ausführbar machen:
$ chmod +x /home/gunter/.local/bin/backup.sh
# Testen: Script manuell als eigener Benutzer ausführen und auf Fehler prüfen:
$ /home/gunter/.local/bin/backup.sh
# Cron-Log auf Fehler prüfen (systemd-basierte Systeme):
$ journalctl -u cron --since "1 hour ago"
% hat in Crontab eine Sonderbedeutung: Es wird zu einem Newline-Zeichen umgewandelt. Wenn du % in einem Befehl brauchst (z. B. in date +%Y-%m-%d), musst du es mit Backslash escapen: date +%Y-%m-%d. Oder du lagerst den Befehl in ein separates Script aus — dort gelten normale Shell-Regeln.Systemd-Timer als Alternative
Auf modernen Linux-Systemen (Ubuntu 16.04+, Fedora, Arch, Debian 8+) ist systemd der Init-Prozess — und bringt eine eigene Zeitsteuerung mit: systemd-Timer. Sie sind aufwändiger zu konfigurieren als Crontab-Einträge, bieten aber einige Vorteile:
- Protokollierung in journald: Ausgabe landet automatisch im System-Journal und ist per
journalctlabrufbar — kein manuelles Log-Redirect nötig. - Abhängigkeiten: Timer können von anderen systemd-Units abhängen — z. B. erst nach Netzwerkverbindung starten.
- Kalender-Syntax: Flexiblere Zeitangaben wie
Mon *-*-* 07:00oderdaily. - Verpasste Jobs nachholen: Mit
Persistent=trueholt systemd Jobs nach, die ausgefallen sind weil das System ausgeschaltet war.
Ein einfacher systemd-Timer besteht aus zwei Dateien unter ~/.config/systemd/user/:
# ~/.config/systemd/user/backup.service
[Unit]
Description=Tägliches Home-Backup
[Service]
ExecStart=/home/gunter/.local/bin/backup.sh
Type=oneshot
# ~/.config/systemd/user/backup.timer
[Unit]
Description=Tägliches Backup um 2 Uhr
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
# Timer aktivieren und starten:
$ systemctl --user enable backup.timer
$ systemctl --user start backup.timer
# Status prüfen:
$ systemctl --user list-timers
# Ausgabe ansehen:
$ journalctl --user -u backup.service
Cron oder systemd-Timer? Für einfache, regelmäßige Aufgaben reicht Cron vollkommen — eine Zeile in der Crontab ist schneller eingerichtet als zwei systemd-Dateien. Systemd-Timer lohnen sich, wenn du Logging, Abhängigkeiten oder das Nachholen verpasster Jobs brauchst. Auf Desktop-Systemen, die nicht immer laufen, ist Persistent=true besonders nützlich: Das verpasste Backup von 2 Uhr wird dann beim nächsten Systemstart nachgeholt.
Wer sich noch nicht sicher ist, welche Lösung passt: Starte mit cron. Eine einzelne Crontab-Zeile ist in zwei Minuten geschrieben und funktioniert auf jedem Linux-System, das älter als zehn Jahre ist. Wenn du später merkst, dass du Logging, Abhängigkeiten oder Retry-Verhalten brauchst, ist der Wechsel zu systemd-Timer jederzeit möglich — dein Bash-Script, das du als Cron-Job gewöhntest, läuft ohne Änderung auch in einer systemd-Service-Unit.
Weitere Automatisierungsmöglichkeiten, die gut mit Cron zusammenspielen:
- Eigene Bash-Scripts für deine Cron-Jobs schreiben: Shellscripts
- Inkrementelle Backups mit rsync einrichten und dann per Cron automatisieren: Backups mit rsync
- Pipes und Redirects, die du in Crontab-Zeilen verwendest, erklärt die Seite über Komfortfunktionen: Komfortfunktionen in der Bash