KI-Agenten im Hintergrund ausführen — cron · launchd · Task Scheduler
Planungsmuster zum automatischen Ausführen von KI-Agenten wie Claude Code — macOS launchd · Linux cron · Windows Task Scheduler im Vergleich.
KI-Agenten wie Claude Code eignen sich nicht nur für den interaktiven Einsatz am Keyboard. Nächtliche PR-Reviews, Issue-Triage, README-Synchronisierung, Log-Zusammenfassungen — der Mehrwert wächst, wenn sie unbeaufsichtigt laufen.
Ich denke, was autonome Agenten so wertvoll macht, ist nicht die Anzahl der Aufgaben, die sie ausführen, sondern die Zuverlässigkeit, mit der sie es tun — weil ein Agent, der nachts schweigend scheitert, schlimmer ist als keiner. Früher haben wir KI-Tools hauptsächlich interaktiv eingesetzt; heute verlagert sich die Arbeit zunehmend in geplante, unbeaufsichtigte Läufe, vielmehr als Erweiterung des Teams als als Ersatz manueller Arbeit.
Dieser Leitfaden vergleicht die Standard-Planungstools auf drei Betriebssystemen (macOS launchd, Linux cron, Windows Task Scheduler) anhand desselben Szenarios und beleuchtet KI-Agenten-spezifische Fallen (API-Key-Sicherheit, Log-Erfassung, Fehlerbenachrichtigungen).
TL;DR
| Betriebssystem | Tool | Definition | Logs |
|---|---|---|---|
| macOS | launchd (LaunchAgent) | ~/Library/LaunchAgents/<label>.plist | StandardOutPath/StandardErrorPath |
| Linux | cron | crontab -e oder /etc/cron.d/<name> | MTA oder Umleitung >> log.txt 2>&1 |
| Windows | Task Scheduler | GUI oder PowerShell Register-ScheduledTask | Event Viewer oder Dateiausgabe |
Fünf häufige Fallen:
- Leerer PATH — Befehle werden nicht gefunden
- API-Keys im Klartext in plist/crontab (NICHT empfohlen)
- Gleichzeitige Ausführungen desselben Jobs können sich gegenseitig stören
- Stilles Versagen — keine Fehlerbenachrichtigung
- Ruhender Computer — Job wird vollständig übersprungen
§1–§3 behandeln betriebssystemspezifische Muster; §4 die Fallen.
Voraussetzungen
- Ein KI-Agent zum Ausführen (z. B. Claude Code, Cursor CLI oder ein eigenes Skript)
- API-Keys über Umgebungsvariablen oder einen Secret-Manager verwaltet
- Ein Shell-Skript (z. B.
~/agents/daily-pr-review.sh)
1. macOS — launchd LaunchAgent — 10 Min.
cron ist auf macOS verfügbar, aber Apple empfiehlt launchd. Nachholläufe nach Schlafmodus oder erneuter Anmeldung sind damit zuverlässiger.
1.1 Die plist schreiben
~/Library/LaunchAgents/dev.local.daily-pr-review.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>dev.local.daily-pr-review</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-lc</string>
<string>$HOME/agents/daily-pr-review.sh</string>
</array>
<!-- Umgebung: PATH kann leer sein, daher explizit setzen -->
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
<!-- ANTHROPIC_API_KEY NICHT hier eintragen — siehe §4.2 -->
</dict>
<!-- Täglich um 09:00 -->
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key><integer>9</integer>
<key>Minute</key><integer>0</integer>
</dict>
<!-- Nachholen nach Schlafmodus -->
<key>RunAtLoad</key><false/>
<key>StartOnMount</key><false/>
<!-- Logs -->
<key>StandardOutPath</key>
<string>/tmp/daily-pr-review.out.log</string>
<key>StandardErrorPath</key>
<string>/tmp/daily-pr-review.err.log</string>
</dict>
</plist>1.2 Registrieren + Starten
# Registrieren
launchctl load ~/Library/LaunchAgents/dev.local.daily-pr-review.plist
# Sofort starten (Zeitplan überspringen)
launchctl start dev.local.daily-pr-review
# Status
launchctl list | grep daily-pr-review
# Deregistrieren
launchctl unload ~/Library/LaunchAgents/dev.local.daily-pr-review.plist1.3 Nachholen nach Schlafmodus
cron überspringt Läufe, die in den Schlafmodus fallen. launchd hingegen führt den Job einmal nach dem Aufwachen nach (Hinweis: StartCalendarInterval holt dabei nur einmal nach).
Für stündliches Nachholen:
<key>StartInterval</key>
<integer>3600</integer> <!-- jede Stunde -->→ Läuft einmal nach dem Aufwachen, anders als cron.
2. Linux — cron — 5 Min.
2.1 crontab bearbeiten
crontab -eBeispiel:
# m h dom mon dow Befehl
0 9 * * * /home/user/agents/daily-pr-review.sh >> /home/user/logs/pr-review.log 2>&1| Feld | Wert | Bedeutung |
|---|---|---|
0 | 0 | zur vollen Stunde |
9 | 9 | 9 Uhr |
* * * | jeder | jeden Tag |
Täglich 09:00, stdout/stderr an Logdatei anhängen.
2.2 PATH explizit setzen
Der PATH von cron ist stark eingeschränkt. Er sollte im Skript selbst oder am Anfang der crontab gesetzt werden:
PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/sbin
SHELL=/bin/bash
0 9 * * * $HOME/agents/daily-pr-review.sh >> $HOME/logs/pr-review.log 2>&12.3 System-cron (/etc/cron.d/)
Für root-Registrierung auf Servern:
sudo tee /etc/cron.d/daily-pr-review > /dev/null <<'EOF'
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 9 * * * appuser /home/appuser/agents/daily-pr-review.sh >> /var/log/agent.log 2>&1
EOFDas Feld appuser gibt den Ausführungsbenutzer an. Wichtig für die Sicherheit.
2.4 systemd Timer (moderne Alternative)
Eine moderne Alternative zu cron. Unit-Dateien vereinen Neustart-Logik, Fehlerbehandlung und zentrale Protokollierung.
# /etc/systemd/system/pr-review.service
[Unit]
Description=Täglicher PR-Review-Agent
[Service]
Type=oneshot
User=appuser
ExecStart=/home/appuser/agents/daily-pr-review.sh
StandardOutput=journal
StandardError=journal# /etc/systemd/system/pr-review.timer
[Unit]
Description=Täglichen PR-Review um 09:00 ausführen
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true # nach Boot ausführen, wenn Zeit während Downtime verstrichen
[Install]
WantedBy=timers.targetsudo systemctl enable --now pr-review.timer
journalctl -u pr-review.service # LogsPersistent=true ist der entscheidende Vorteil gegenüber cron: Der Timer holt verpasste Läufe nach Server-Ausfallzeiten automatisch nach.
3. Windows — Task Scheduler — 10 Min.
Die GUI funktioniert, ist aber nicht reproduzierbar. Die PowerShell-Variante ist die empfohlene Methode.
3.1 Per PowerShell registrieren
# Aktion
$action = New-ScheduledTaskAction `
-Execute "wsl.exe" `
-Argument "-d Ubuntu -e bash -lc '~/agents/daily-pr-review.sh'"
# Auslöser: täglich 09:00
$trigger = New-ScheduledTaskTrigger -Daily -At 9am
# Einstellungen: auch ohne angemeldeten Benutzer ausführen; aus Schlaf aufwecken
$settings = New-ScheduledTaskSettingsSet `
-StartWhenAvailable `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-ExecutionTimeLimit (New-TimeSpan -Hours 1) `
-RestartCount 2 `
-RestartInterval (New-TimeSpan -Minutes 10)
# Registrieren
Register-ScheduledTask `
-TaskName "DailyPRReview" `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Description "Claude Code tägliches PR-Review in WSL ausführen"3.2 In der GUI bestätigen
Win + R → taskschd.msc → Task Scheduler-Bibliothek → „DailyPRReview".
3.3 Sofort testen
Start-ScheduledTask -TaskName "DailyPRReview"
Get-ScheduledTaskInfo -TaskName "DailyPRReview" | Select LastRunTime, LastTaskResultLastTaskResult: 0 bedeutet Erfolg.
3.4 WSL aufrufen
Wie oben gezeigt: wsl.exe -d Ubuntu -e bash -lc '...'. Umgebungsvariablen und API-Keys werden in WSLs ~/.bashrc oder einer separaten env-Datei verwaltet.
3.5 Log-Erfassung
Das eigene Log des Task Schedulers ist wenig detailliert. Besser direkt im Skript umleiten:
# ~/agents/daily-pr-review.sh
#!/usr/bin/env bash
set -euo pipefail
LOG="$HOME/logs/pr-review-$(date +%Y%m%d).log"
mkdir -p "$(dirname "$LOG")"
exec >> "$LOG" 2>&1
echo "[$(date)] starte..."
# Agenten ausführen
claude-code --resume --task pr-review --quiet
echo "[$(date)] fertig."4. KI-Agenten-spezifische Fallen
4.1 Leerer PATH — häufigste Ursache für „läuft nicht"
Der interaktive Shell-PATH unterscheidet sich vom launchd/cron-PATH. Den korrekten PATH am Anfang des Skripts explizit setzen:
#!/usr/bin/env bash
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:$PATH"Alternativ über EnvironmentVariables in der plist oder den PATH=-Header in der crontab.
Prüfen:
launchctl getenv PATH # macOS
sudo -u appuser env | grep PATH # Linux-cron-Benutzer4.2 API-Keys niemals in plist/crontab eintragen
ANTHROPIC_API_KEY=sk-ant-... in einem plist-EnvironmentVariables-Block ist gefährlich:
- Über
ls -laeinsehbar - Wird im Klartext in Time Machine / iCloud Backup gespeichert
- Andere Benutzer auf demselben System können ihn per
cat ~/Library/LaunchAgents/*.plistauslesen
Stattdessen:
- macOS: Keychain + Skript liest per
security find-generic-password -s anthropic-api -w - Linux: separate Datei
~/.config/agent/envmitchmod 600+sourceim Skript - Windows:
Get-Credentialoder Credential Manager
Skript (macOS):
#!/usr/bin/env bash
set -euo pipefail
export ANTHROPIC_API_KEY="$(security find-generic-password -s anthropic-api -w 2>/dev/null)"
[ -z "$ANTHROPIC_API_KEY" ] && { echo "API-Key im Keychain fehlt"; exit 1; }
# ... Agenten ausführenKey einmalig registrieren:
security add-generic-password -s anthropic-api -a "$USER" -w 'sk-ant-...'4.3 Gleichzeitige Ausführungen verhindern — flock / Single-Instance-Sperre
Doppelte Ausführungen bedeuten doppelte API-Kosten und Race Conditions. Mit flock gegen gleichzeitige Läufe absichern:
#!/usr/bin/env bash
exec 200>/tmp/daily-pr-review.lock
flock -n 200 || { echo "läuft bereits"; exit 0; }
# ... Agenten ausführenflock -n = nicht-blockierend; sofortiger Ausstieg, wenn die Sperre bereits gehalten wird.
4.4 Fehler nicht lautlos schlucken — Benachrichtigungen einrichten
Standardmäßig landet stderr in einer Logdatei — und bleibt dort unbemerkt. Mit trap aktiv benachrichtigen:
#!/usr/bin/env bash
set -euo pipefail
notify_failure() {
local code=$?
if [ $code -ne 0 ]; then
# macOS — terminal-notifier oder osascript
osascript -e "display notification \"PR-Review fehlgeschlagen (exit $code)\" with title \"Agent\""
# Oder Slack-Webhook / E-Mail / Sentry
# curl -X POST -H 'Content-Type: application/json' \
# -d "{\"text\":\"Agent fehlgeschlagen: exit $code\"}" "$SLACK_WEBHOOK_URL"
fi
}
trap notify_failure EXIT
# Agenten ausführen4.5 Nachholen nach Schlaf / Boot
- macOS launchd:
StartCalendarIntervalholt einmal nach dem Aufwachen nach - Linux systemd timer:
Persistent=trueverwenden - Windows: Task Scheduler-Option „Aufgabe so bald wie möglich nach einem verpassten geplanten Start ausführen" aktivieren
Nur cron unterstützt kein Sleep-Nachholen — das ist der Hauptgrund, warum es auf Laptops kaum eingesetzt wird.
5. Echtes Szenario — Claude Code tägliches PR-Review
~/agents/daily-pr-review.sh:
#!/usr/bin/env bash
set -euo pipefail
# 1. PATH + Secrets
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
export ANTHROPIC_API_KEY="$(security find-generic-password -s anthropic-api -w)"
export GITHUB_TOKEN="$(security find-generic-password -s github-token -w)"
# 2. Sperre
exec 200>/tmp/daily-pr-review.lock
flock -n 200 || exit 0
# 3. Log
LOG="$HOME/logs/pr-review-$(date +%Y%m%d).log"
mkdir -p "$(dirname "$LOG")"
exec >> "$LOG" 2>&1
echo "[$(date)] starte tägliches PR-Review..."
# 4. Arbeit
cd "$HOME/code/my-repo"
git fetch --quiet origin
gh pr list --state open --json number,title,author --jq '.[] | select(.author.login != "me")' | \
while read -r pr; do
num=$(echo "$pr" | jq -r .number)
echo "Überprüfe PR #$num..."
claude-code --task "Review PR #$num — Fokus auf Sicherheit, Performance, fehlende Tests" --quiet
done
echo "[$(date)] fertig."Als LaunchAgent registrieren → läuft täglich automatisch um 09:00.
6. Fehlerbehebung
launchd „Service exited with abnormal code: 127"
Leerer PATH oder nicht ausführbares Skript. chmod +x ~/agents/*.sh ausführen und den PATH in der plist explizit setzen.
cron läuft nie und es gibt kein Log
- crontab-Syntaxfehler: per
crontab -lprüfen - Ohne MTA (mailutils) geht stderr verloren — explizit
2>&1 >> logumleiten - Ausführungsversuche mit
journalctl -u cron(bzw. cronie) prüfen
Task Scheduler „Last Task Result: 0x1"
Das Skript wurde mit Exit-Code 1 beendet. Direkt in WSL debuggen und $LASTEXITCODE prüfen.
claude-code Befehl nicht gefunden
Dem Skript-PATH fehlt ~/.npm-global/bin oder das npm-Global-bin-Verzeichnis. Das Verzeichnis von which claude-code zum PATH hinzufügen.
Nächste Schritte
- Claude Code einrichten — Voraussetzung
- Claude Code Hooks — Automatisierungsereignisse und Hook-Muster
- Multi-Agenten-Workflow — Zusammenarbeit mehrerer Agenten
Referenzen
Changelog
- 2026-05-12 — Erster Entwurf (devAlice M2 Seed-Erweiterung)