Synopsis #
OpenBSD uses cron(8)
for scheduled commands and runs the base-system daily, weekly, and monthly maintenance scripts from root’s crontab. Local jobs should run as the least-privileged suitable account, use explicit paths, prevent unintended overlap, and report failure to a monitored mailbox.
This guide adds one local job, verifies its non-interactive behavior, and explains when to use a user crontab or a daily.local file. It does not place credentials directly in a crontab or modify the base /etc/daily, /etc/weekly, or /etc/monthly scripts.
Choose the Execution Account #
A job should run as the account that already owns the files and service authority it needs. Root is appropriate only when the task genuinely requires root-owned files, devices, or system controls. A package daemon’s own account or a dedicated service account is normally safer for application work.
Before scheduling the job, define:
- the files and commands it may read or change;
- its maximum expected runtime;
- whether two instances may run concurrently;
- where ordinary output and errors will go;
- how a missed run will be detected;
- how the job will be disabled and its effects reversed.
Write a Non-Interactive Script #
Place multi-step logic in a reviewed script instead of embedding it in one long crontab line. This example creates a read-only filesystem-capacity check:
#!/bin/sh
PATH=/bin:/usr/bin:/sbin:/usr/sbin
export PATH
exec df -h
Install it with explicit ownership and mode:
# install -o root -g wheel -m 750 check-filesystems /usr/local/libexec/check-filesystems
Use absolute paths for locally installed programs because cron does not read interactive shell profiles. Load secrets from a root- or service-owned file with restrictive permissions, or use the application’s supported credentials mechanism. Do not place passwords, tokens, or private keys in the crontab command.
Test the Exact Account and Environment #
Run the script as its intended account before scheduling it. For a root job:
# env -i HOME=/root LOGNAME=root USER=root PATH=/bin:/usr/bin:/sbin:/usr/sbin /usr/local/libexec/check-filesystems
The minimal environment exposes hidden dependencies on a working directory, shell profile, terminal, agent, or unset variable. Confirm the exit status as well as the displayed output:
# echo $?
An exit status of zero must mean success for monitoring and cron-mail policy to work correctly.
Install a User Crontab Entry #
Edit the intended account’s crontab with crontab(1) . For root:
# crontab -e
OpenBSD crontab(5)
supports -s before the command to prevent overlapping instances. The following entry runs at 03:17 and mails output to the local admin alias:
SHELL=/bin/sh
PATH=/bin:/usr/bin:/sbin:/usr/sbin
MAILTO=admin
17 3 * * * -s /usr/local/libexec/check-filesystems
Cron logs command execution. By default, command output is mailed to the crontab owner or MAILTO. Do not set an empty MAILTO merely to silence an unexplained failure. The OpenBSD-specific -n command flag mails output only when a successful command is not the outcome; adopt it only after the script uses meaningful exit statuses.
List the installed entry without editing it:
# crontab -l
System /etc/crontab entries contain an additional user field. User crontabs do not. Prefer crontab -e for a job owned by one account rather than mixing local jobs into the system file.
Use the Daily, Weekly, and Monthly Framework #
daily(8) documents the base maintenance scripts. Their output is mailed to root, so root mail must resolve to an account that is actually read.
Do not edit /etc/daily, /etc/weekly, or /etc/monthly. Add site-specific work to:
/etc/daily.local;/etc/weekly.local;/etc/monthly.local.
These local files run before the corresponding base script. They are suitable for short system-wide checks and documented variables such as ROOTBACKUP or CHECKFILESYSTEMS. A long-running application task is usually clearer in its own service-account crontab.
For example, /etc/daily.local can enable the documented no-write filesystem check:
CHECKFILESYSTEMS=1
This causes the daily script to run fsck(8)
with -n; it does not authorize repairs on mounted filesystems.
Direct and Test Maintenance Mail #
The periodic scripts and cron depend on local mail delivery. Map root and local operational aliases to a monitored account in /etc/mail/aliases, then rebuild the aliases with newaliases(8)
.
Send a deliberate test from the scheduled account and confirm receipt before relying on silent success. Also review /var/cron/log and local mail after the first scheduled run.
Account for Systems That Sleep or Power Off #
Cron does not automatically replay every job missed while a workstation was suspended or powered off. The daily(8) manual specifically warns that periodic scripts may never run on hosts that are not available at their scheduled times.
For such a host, select a schedule during normal uptime, run a startup check that determines whether work is due, or move the task to an always-on management system. Any catch-up mechanism must record the last successful completion and remain safe when invoked more than once.
Remove or Recover a Job #
Disable a failing job by commenting out or removing only its crontab entry, then preserve its output and logs for diagnosis. Do not delete the script or its data before determining whether it was interrupted mid-change.
After correction, rerun the script manually with the minimal environment, reinstall the crontab, and watch one scheduled execution. Remove obsolete scripts, credential files, lock state, and aliases only after confirming that no remaining job references them.