Cron-Jobs einrichten — Aufgaben automatisch ausführen lassen

Einsteiger Praxis Veröffentlicht am 9. April 2026 von Gunter Herdrich

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 Werte
  • 8-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.

Tipp: Wenn du dir bei der Syntax unsicher bist, hilft crontab.guru — eine interaktive Web-App, die Cron-Ausdrücke auf Deutsch erklärt und zeigt, wann ein Eintrag das nächste Mal ausgeführt würde.

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
Achtung: 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."
Tipp: Die Benutzer-ID in /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"
Achtung: Shell-Sonderzeichen in Crontab-Zeilen. Das Prozent-Zeichen % 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 journalctl abrufbar — 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:00 oder daily.
  • Verpasste Jobs nachholen: Mit Persistent=true holt 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