Redirecting Output and Logs
Automation without logs is guessing. Cron jobs often fail silently unless you redirect output. This page shows the essential operators and safe logging patterns for production scripts.
>overwrites;>>appends.2>captures stderr;2>&1merges stderr into stdout.- Always log to a directory with correct permissions.
- Avoid logging secrets.
stdout vs stderr
- stdout: normal output
- stderr: errors and diagnostics
Cron only emails output if a mail system is configured. If you want reliable logs, redirect to files.
Core operators
| Operator | Meaning |
|---|---|
> | write stdout, overwrite file |
>> | write stdout, append to file |
2> | write stderr, overwrite file |
2>> | write stderr, append to file |
2>&1 | merge stderr into stdout |
Examples
Redirect stdout (overwrite)
* * * * * /path/script.sh > /var/log/script.log
Redirect stdout (append)
* * * * * /path/script.sh >> /var/log/script.log
Redirect stderr only
* * * * * /path/script.sh 2>> /var/log/script.err
Redirect both stdout and stderr to one file
* * * * * /path/script.sh >> /var/log/script.log 2>&1
Separate stdout and stderr
* * * * * /path/script.sh >>/var/log/script.out 2>>/var/log/script.err
Create a log directory safely
sudo mkdir -p /var/log/automation
sudo chown root:root /var/log/automation
sudo chmod 750 /var/log/automation
Logging from scripts
If you control the script, you can redirect inside the script.
#!/usr/bin/env bash
set -euo pipefail
LOG="/var/log/automation/job.log"
exec >>"$LOG" 2>&1
echo "[$(date -Is)] start"
echo "doing work"
echo "[$(date -Is)] done"
Do not write secrets to logs. If your script prints environment variables or command lines containing passwords, fix the script before enabling logging.
systemd timers and journald
systemd services capture stdout/stderr into journald by default. This is one reason timers are easier to debug.
journalctl -u wp-backup.service --since '24 hours ago' --no-pager
Send output to syslog/journald with logger
If you prefer central logs, you can send messages via logger.
/usr/local/bin/job 2>&1 | logger -t myjob
Cron entry example:
*/10 * * * * /usr/local/bin/job 2>&1 | /usr/bin/logger -t myjob
This avoids log files growing unbounded on disk (but you still need journald retention configured).
Use tee when you want both console and file
tee writes to a file and stdout.
It is useful when you run a command interactively and want to capture output.
/usr/local/bin/job 2>&1 | tee -a /var/log/automation/job.log
Debug mode (temporarily)
If you need to see each command as it executes:
set -x
command_one
command_two
set +x
Do not leave set -x enabled in scripts that handle secrets.
It can print sensitive values into logs.
Log rotation
If you log to files under /var/log, use logrotate.
/var/log/automation/*.log {
daily
rotate 14
compress
missingok
notifempty
create 0640 root adm
}
Permissions for log files
Logs should not be world-readable if they may contain sensitive paths or identifiers.
sudo chown root:adm /var/log/automation/job.log || true
sudo chmod 640 /var/log/automation/job.log || true
Next steps
- Cron basics:
opt/docker-data/apps/docusaurus/site/docs/server/linux-server/11-automation-task-execution/cron.mdx - Cron vs systemd:
opt/docker-data/apps/docusaurus/site/docs/server/linux-server/11-automation-task-execution/cron-vs-systemd.mdx