Scheduling
Manual verification and pruning work for development, but production systems need automation. Laravel's task scheduler lets you run audit:verify and audit:prune on a recurring basis — no custom cron entries required beyond the standard Laravel scheduler.
Recommended Setup
Add both commands to routes/console.php (Laravel 11+ style):
use Illuminate\Support\Facades\Schedule; Schedule::command('audit:verify --notify')->hourly(); Schedule::command('audit:prune')->daily();
This gives you:
- Hourly verification — detects tampering within 60 minutes and sends notifications through your configured channels
- Daily pruning — removes audit logs older than your configured retention period (default: 90 days)
Server Cron Entry
Laravel's scheduler requires a single cron entry on your server. If you haven't set this up already:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
This runs the Laravel scheduler every minute. The scheduler itself determines which commands are due.
Custom Schedules
Verification Frequency
The right verification frequency depends on how critical tamper detection is for your use case:
// High security -- check every 15 minutes Schedule::command('audit:verify --notify')->everyFifteenMinutes(); // Standard -- check every hour Schedule::command('audit:verify --notify')->hourly(); // Low priority -- check once a day Schedule::command('audit:verify --notify')->daily();
Different Retention for Different Models
Schedule multiple prune commands with different retention periods:
// Keep financial logs for 7 years Schedule::command('audit:prune --days=2555 --type="App\Models\Transaction"')->daily(); // Keep general activity logs for 90 days Schedule::command('audit:prune --days=90 --type="App\Models\Post"')->daily(); // Prune everything else at the default retention Schedule::command('audit:prune')->daily();
Time-Based Scheduling
Run heavy operations during off-peak hours:
Schedule::command('audit:verify --notify')->hourly(); Schedule::command('audit:prune')->dailyAt('03:00');
Environment-Specific Scheduling
Use environments() to restrict scheduling to specific environments:
Schedule::command('audit:verify --notify') ->hourly() ->environments(['production', 'staging']); Schedule::command('audit:prune') ->daily() ->environments(['production']);
Preventing Overlaps
For large audit tables, verification can take time. Prevent overlapping runs with withoutOverlapping():
Schedule::command('audit:verify --notify') ->hourly() ->withoutOverlapping(); Schedule::command('audit:prune') ->daily() ->withoutOverlapping();
Output Logging
Capture command output for debugging:
Schedule::command('audit:verify --notify') ->hourly() ->appendOutputTo(storage_path('logs/audit-verify.log')); Schedule::command('audit:prune') ->daily() ->appendOutputTo(storage_path('logs/audit-prune.log'));
Full Example
A production-ready routes/console.php combining all recommendations:
use Illuminate\Support\Facades\Schedule; Schedule::command('audit:verify --notify') ->hourly() ->withoutOverlapping() ->environments(['production']) ->appendOutputTo(storage_path('logs/audit-verify.log')); Schedule::command('audit:prune') ->dailyAt('03:00') ->withoutOverlapping() ->environments(['production']) ->appendOutputTo(storage_path('logs/audit-prune.log'));