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


10 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