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