Pruning

Audit logs grow over time. Regulatory frameworks like GDPR encourage data minimization — keeping data only as long as it serves a legitimate purpose. The audit:prune command deletes audit logs older than a configurable retention period, keeping your database lean without manual intervention.

Basic Usage

php artisan audit:prune

By default, this deletes only light logs (where hash is NULL) older than the configured retention period (default: 90 days). Chained audit logs (with a hash) are preserved to maintain chain integrity. The command asks for confirmation before deleting.

Configuration

Set the default retention period in config/audit-chain.php:

'retention' => [
    'days' => 90,
],

Command Options

Custom Retention Period

Override the configured retention period with the --days flag:

# Keep only the last 30 days
php artisan audit:prune --days=30

# Keep a full year
php artisan audit:prune --days=365

Filter by Model Type

Prune logs for a specific model only, leaving other audit logs untouched:

php artisan audit:prune --type="App\Models\PageView"

This is useful when different models have different retention requirements — you might keep financial transaction logs for 7 years but prune page view logs after 30 days.

Including Chained Logs

By default, audit:prune only deletes light logs (no hash chain). To also prune chained audit logs, use the --include-chained flag. This requires the --force flag and displays a warning, because deleting chained logs affects chain verification:

php artisan audit:prune --days=365 --include-chained --force

When chained logs are pruned, the hash of the last deleted log is saved in the audit_chain_state.checkpoint_hash column. This checkpoint acts as the starting point for future chain verification — allowing audit:verify to pick up from where the pruned chain left off instead of failing on the first entry.

Force Mode

Skip the confirmation prompt with --force. Required when using --include-chained:

php artisan audit:prune --force

Combining Flags

php artisan audit:prune --days=30 --type="App\Models\PageView"
php artisan audit:prune --days=365 --include-chained --force

How It Works

The command deletes records in chunks of 1,000 to avoid locking the database table for extended periods. This is safe for large tables with millions of audit logs — the command progressively deletes batches until all matching records are removed.

$ php artisan audit:prune --days=90 --no-interaction
About to delete 15,234 audit log(s) older than 90 day(s).
  Deleted 1000 / 15234...
  Deleted 2000 / 15234...
  ...
  Deleted 15234 / 15234...
Pruned 15234 audit log(s).

If no logs match the retention criteria, the command exits cleanly:

No audit logs older than 90 day(s) found.

Scheduling

For automated cleanup, schedule the prune command in routes/console.php:

Schedule::command('audit:prune')->daily();

See Scheduling for a complete cron setup.

Choosing a Retention Period

Your retention period depends on your compliance requirements:

Regulation Typical Retention
GDPR As short as justifiable (data minimization principle)
SOX 7 years
HIPAA 6 years
PCI DSS 1 year
NIS2 No specific minimum, but sufficient for incident investigation

Set retention.days to satisfy your most demanding obligation. Use the --type flag and scheduling to apply different retention periods to different models.