Why Debian Uses Cron Instead of PHP’s Built-in Session Garbage Collector


1 views

If you've worked with PHP on Debian or Ubuntu, you might have noticed something unusual in the default configuration. Unlike most PHP setups, these distributions disable PHP's built-in session garbage collector by setting:

session.gc_probability = 0

Instead, they rely on a cron job located at /etc/cron.d/php5 (or similar paths for newer PHP versions):

09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null \; -delete

The Debian maintainers made this design choice for several technical reasons:

1. Predictable Cleanup Timing: PHP's garbage collector runs probabilistically on session initialization, which can lead to uneven server load. The cron approach provides consistent, scheduled cleanup.

2. Better Resource Control: The cron job runs as root and can properly clean up sessions from all users and processes, avoiding permission issues.

3. More Precise Cleanup: The fuser check ensures active sessions aren't deleted, something PHP's GC can't do.

While the Debian approach works well for most setups, there are cases where enabling PHP's built-in GC might be better:

// In your php.ini
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440

Consider this if:

  • Your application creates many short-lived sessions
  • You're running in a containerized environment without cron
  • You need immediate session cleanup rather than waiting for the cron job

For most production environments, the cron method is superior. Here's how to customize it:

# Adjust the frequency (e.g., every 5 minutes)
*/5 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null \; -delete

# For multiple session save paths
*/15 * * * * root find /var/lib/php/sessions /path/to/other/sessions -type f -cmin +1440 -delete

To verify your session cleanup is working, you can use this simple PHP script:

<?php
$sessionPath = ini_get('session.save_path');
$files = glob("$sessionPath/sess_*");
echo "Current sessions: " . count($files) . "\n";

// Create a test session
session_start();
$_SESSION['test'] = time();
session_write_close();
?>

Run this script before and after the cron job executes to see the cleanup in action.


Debian-based systems (including Ubuntu) implement a distinctive architecture for PHP session cleanup that differs from PHP's native garbage collection mechanism. Instead of relying on session.gc_probability, they deploy a cron-based solution:


# /etc/php5/fpm/php.ini (Debian default)
session.gc_probability = 0
session.gc_divisor = 1000

The cron job executes at the 9th and 39th minute of every hour with this complex command:


09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && 
find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) 
! -execdir fuser -s {} 2>/dev/null \; -delete

Several technical considerations drove this design decision:


// Traditional PHP garbage collection risks:
1. Race conditions during concurrent requests
2. Unpredictable performance spikes
3. Difficulty in monitoring cleanup effectiveness
4. Potential session loss during high-traffic periods

The Debian solution provides:

  • Precise control over cleanup timing
  • Predictable server load patterns
  • Better filesystem operation batching
  • Consistent behavior across all PHP processes

For modern PHP versions (7.4+), Debian uses this adapted approach:


# /etc/cron.d/php
*/30 * * * * root [ -x /usr/lib/php/sessionclean ] && [ -d /var/lib/php/sessions ] && 
/usr/lib/php/sessionclean /var/lib/php/sessions $(/usr/lib/php/sessionclean -t)
Metric Native GC Debian Cron
Cleanup Timing Random during requests Scheduled off-peak
Filesystem I/O Distributed Batched
Predictability Low High

To switch back to PHP's native GC (not recommended on Debian):


// /etc/php/7.4/fpm/php.ini
session.gc_probability = 1
session.gc_divisor = 100
session.gc_maxlifetime = 1440

// Disable the cron job
sudo chmod -x /usr/lib/php/sessionclean

Verify cron job execution with:


# Check cron logs
grep CRON /var/log/syslog

# Verify cleanup results
ls -la /var/lib/php/sessions | wc -l

# Monitor disk usage
watch -n 60 du -sh /var/lib/php/sessions