FreeBSD periodic Operations: Job Schedules, Output, and Custom Checks
Operate FreeBSD periodic jobs with correct cron schedules, configuration layering, report routing, script status codes, and safe custom maintenance checks.
FreeBSD’s periodic(8) framework runs executable maintenance scripts in named directories. cron(8) decides when to invoke those groups; periodic discovers and runs the scripts; periodic.conf(5) controls their behavior and output. Keeping those responsibilities separate makes the system easier to audit. A missing report may mean cron did not launch, a script was not executable, output was masked, mail was not delivered, or a local check never ran at all.
This guide covers routine system and site-specific maintenance. It does not replace application schedulers or service-specific lifecycle controls. In particular, a periodic job should not be used to hide a service that needs continuous supervision, and running all daily tasks manually is not a harmless syntax check.
Understand the schedule that invokes periodic
The stock system crontab contains entries for daily, weekly, and monthly groups. The current Handbook illustrates daily execution at 03:01 local time, weekly execution at 04:15 Saturday, and monthly execution at 05:30 on the first day. Inspect the actual host rather than assuming its schedule matches an example:
grep -n 'periodic' /etc/crontab
service cron status
date
/etc/crontab is the system crontab and includes a username field. A personal crontab created with crontab -e does not contain that field. Copying a system entry into root’s personal crontab with the extra root token causes the command to be parsed incorrectly. Preserve unrelated entries when changing system schedules, and verify local time and timezone before moving a maintenance window.
The built-in groups are daily, weekly, monthly, and security; the last is a standard system group, but security-specific policy is outside this guide. FreeBSD does not define a generic periodic hourly group in the utility’s standard interface. A package may provide its own hourly scripts or a site may schedule a particular directory, but that is an explicit extension that must be inspected rather than assumed to exist.
Know which scripts are actually eligible to run
The base system stores scripts under /etc/periodic/<group>. The framework can also search local directories named by local_periodic in periodic.conf(5); the conventional package location is /usr/local/etc/periodic. The periodic(8) command executes files only when they have the executable bit set. A script sitting in the right directory but not executable is silently ignored.
Inspect the active schedule, local directory configuration, installed scripts, and recent output before adding a duplicate job:
grep -n 'periodic' /etc/crontab
grep -n 'local_periodic' /etc/defaults/periodic.conf /etc/periodic.conf 2>/dev/null
find /etc/periodic /usr/local/etc/periodic -type f -print 2>/dev/null
Do not edit /etc/defaults/periodic.conf; it describes system defaults. Local overrides belong in /etc/periodic.conf or the local override file supported by the installed manual page. These files are shell-sourced by periodic scripts, so use valid shell assignments, quote strings, and review values as executable configuration rather than treating them as a generic key-value database. sysrc manages rc configuration and is not a replacement for editing periodic variables.
Before extending a group, determine whether a package already installed a script that satisfies the requirement. Two jobs that both prune a cache, create a snapshot, or rotate a file can create an unexpected retention or load pattern. Document one owner, one schedule, and one expected output for each maintenance action.
Treat script exit status as report classification
Periodic scripts use a small status protocol to control how their output is presented. Exit 0 means nothing notable occurred; 1 means the script has information to report; 2 indicates warnings about invalid configuration; values greater than 2 produce output that must not be masked. The daily_show_success, daily_show_info, and daily_show_badconfig settings control masking for the daily group, with corresponding names for other groups.
These codes are not a universal application-health schema. A script returning 1 does not necessarily mean its work failed, and an exit 0 from periodic does not prove every downstream service is healthy. Design each script so its message and return code match the framework’s reporting convention, then monitor the delivered output and the presence of expected jobs. Do not silence information by setting every *_show_* option to NO without deciding how operators will still detect meaningful changes.
A custom check should have a bounded runtime, explicit command paths, deterministic output, and safe repeated execution. For example, a site check might report filesystem capacity and use return 1 for a configured warning threshold, while returning a value greater than 2 when it cannot collect a valid measurement. The threshold is site policy, not a universal FreeBSD default. If a job changes state, make the action idempotent or protect it against overlapping runs; cron and periodic are schedulers, not distributed locks or exactly-once workflow engines.
Route and retain output deliberately
daily_output, weekly_output, and monthly_output in /etc/periodic.conf control where group output goes. The Handbook documents mailing output to the configured recipients or writing it to a log path such as /var/log/daily.log; FreeBSD’s newsyslog configuration knows about the standard daily, weekly, and monthly log paths when those files exist. Choose one monitored destination and prove delivery. A root mailbox that no person or alerting system reads is not operational monitoring.
# /etc/periodic.conf
daily_output="[email protected]"
weekly_output="[email protected]"
monthly_output="[email protected]"
# To log instead, select the path policy deliberately:
# daily_output="/var/log/daily.log"
Do not configure both mail and file behavior through an ambiguous value. The manual interprets an absolute path as a log destination and a nonempty non-path as a space-separated recipient list. If output is redirected to custom files, add or verify rotation, permissions, capacity monitoring, and a way to distinguish an empty run from a failed run.
The framework’s environment is intentionally small. periodic sets a path containing standard system directories, not arbitrary locations such as /usr/local/bin. A local script that depends on third-party tools should set its own PATH or invoke those tools by absolute path. Test under the same restricted environment that cron supplies, not only in an administrator’s interactive shell.
Add a custom check without changing the base scripts
Place a site-owned executable under the local daily directory, for example /usr/local/etc/periodic/daily/450-var-capacity. A script can inspect /var, print one concise result, and return a status that matches the framework’s conventions. The exact alert threshold, filesystem, and escalation path must be set by the service owner. Avoid editing files under /etc/periodic that package or base updates manage.
Validate shell syntax and permissions before installation, then test the check itself with representative healthy and warning inputs in a staging host. After deployment, verify the framework discovers it from the local periodic directory and the expected daily report contains the message. Running periodic daily executes every eligible daily script, including cleanup and maintenance actions; on production, use it only when that complete group is safe to run immediately and the operator understands its effects.
If you must invoke one custom script directly for a narrow test, remember that direct execution bypasses periodic’s output collection and masking behavior. It tests the script’s logic, not the complete report path. Conversely, invoking periodic /absolute/path/to/directory runs every executable in that directory. Use a staging directory containing only the test script if validating the framework runner itself.
Diagnose missing, late, or empty reports
Trace the chain in order. First confirm cron is enabled and the schedule exists in /etc/crontab. Next check that the requested group directory exists, local_periodic points where expected, and the job has execute permission. Then run the specific script in a controlled environment and capture both standard output and exit status. Finally inspect the *_output destination, mail queue or log file, newsyslog rotation, and monitoring alert.
For an output that disappears, inspect the group’s *_show_success, *_show_info, and *_show_badconfig settings before assuming a script produced nothing. For a job that works interactively but fails under cron, compare PATH, HOME, SHELL, user identity, working directory assumptions, mounted filesystems, and required credentials. For a report that arrives late, compare the scheduled time to runtime and host load, then check for a prior run still active. Do not solve every delay by scheduling more frequent duplicate jobs.
Keep job runtimes bounded and avoid touching remote mounts or waiting indefinitely on network services from an early-morning maintenance check. A hung path access can stall the script and make later jobs difficult to distinguish from missing work. Log a start and completion timestamp for long-running jobs, set an application-level timeout where available, and alert if the expected completion window passes.
Acceptance criteria for a production periodic design
A production-ready setup has one reviewed schedule per job, known ownership of every script, valid shell and executable modes, an explicit output destination, rotation or mailbox monitoring, and a tested way to distinguish success, notable information, invalid configuration, and real errors. It also has a record of the expected runtime and a response owner for a missing report.
Validate on staging with one success case, one report-worthy case, one invalid-configuration case, and one execution failure. Confirm what the runner masks or delivers for each return code, then test the real mail or log path from the host. Reboot or wait through a scheduled interval and verify the actual cron invocation, rather than treating a manual run as proof of scheduling.
The framework is intentionally composable: cron schedules groups, periodic filters and collects script output, configuration selects policy, and each script performs one bounded task. Reliability comes from testing the whole chain and preserving clear ownership, not from adding more cron lines until output appears.
Related:
- How to Automate ZFS Snapshots with periodic
- FreeBSD System Logging in Production: syslogd, newsyslog, and Rotation
Sources: