The Error
Your app is throwing this in the logs:
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions)
Warning: session_start(): open(SESSION_PATH/sess_abc123, O_RDWR) failed: Permission denied (13)
Sometimes it comes paired with:
Warning: session_start(): Failed to read session data: files (path: /tmp)
Users are getting logged out randomly, shopping carts are being wiped, login forms don't stick. Classic session failure at the worst possible time.
Root Cause
PHP writes session data to disk. The process running PHP โ usually www-data, apache, or nginx โ doesn't have write permission on the session save path, or the directory doesn't exist at all.
Three things break this:
- Wrong ownership on the session directory (most common by far)
- Wrong permissions โ the directory exists but PHP's user can't write to it
- Wrong path in
php.inipointing somewhere that doesn't exist
On Ubuntu/Debian, PHP-FPM pools run as www-data. But after a distro upgrade or manual package reinstall, the session directory often gets reset to root ownership โ and suddenly nothing works.
Step 1 โ Find Your Session Path
Check where PHP is actually trying to save sessions:
php -r "echo session_save_path();"
Or from a PHP file:
<?php echo ini_get('session.save_path'); ?>
Also check your php.ini:
php --ini
grep -r 'session.save_path' /etc/php/
Common paths to check:
/var/lib/php/sessions(Ubuntu/Debian default)/tmp/var/lib/php/session(CentOS/RHEL)- A custom path set in your pool config or
php.ini
Step 2 โ Check Ownership and Permissions
ls -la /var/lib/php/sessions
You'll probably see something like:
drwx-wx-wt 2 root root 4096 Apr 1 02:14 sessions
Check which user PHP-FPM is running as:
ps aux | grep php-fpm | grep -v grep
Or check the pool config directly:
grep -E '^user|^group' /etc/php/8.*/fpm/pool.d/www.conf
Fix 1 โ Fix Ownership (Most Common Fix)
If your PHP-FPM runs as www-data:
sudo chown root:www-data /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions
On CentOS/RHEL where PHP-FPM runs as apache:
sudo chown root:apache /var/lib/php/sessions
sudo chmod 770 /var/lib/php/sessions
Ubuntu sets the sticky bit (+t) by default โ keep it. It stops one user from deleting another user's session files:
sudo chmod 1770 /var/lib/php/sessions
Fix 2 โ Directory Doesn't Exist
Package updates occasionally wipe the directory entirely. Recreate it:
sudo mkdir -p /var/lib/php/sessions
sudo chown root:www-data /var/lib/php/sessions
sudo chmod 1770 /var/lib/php/sessions
Fix 3 โ Use a Per-Application Session Path
Can't touch system directories? Running multiple apps under different users? Set a custom session path per application instead:
<?php
$sessionDir = __DIR__ . '/../storage/sessions';
if (!is_dir($sessionDir)) {
mkdir($sessionDir, 0700, true);
}
session_save_path($sessionDir);
session_start();
Keep the directory outside the web root โ it must not be reachable via HTTP. Then confirm it's writable:
ls -la storage/sessions/
Fix 4 โ PHP-FPM Pool-Level Config
Rather than touching php.ini globally, set the session path inside the pool config. This keeps sessions isolated per virtual host:
# /etc/php/8.2/fpm/pool.d/myapp.conf
[myapp]
user = www-data
group = www-data
php_value[session.save_path] = /var/lib/php/sessions/myapp
Then create and permission that directory:
sudo mkdir -p /var/lib/php/sessions/myapp
sudo chown www-data:www-data /var/lib/php/sessions/myapp
sudo chmod 700 /var/lib/php/sessions/myapp
sudo systemctl restart php8.2-fpm
Fix 5 โ SELinux Is Blocking Writes (CentOS/RHEL)
Permissions look correct but sessions still fail on CentOS/RHEL? SELinux might be silently denying writes. Check the audit log:
sudo ausearch -m avc -ts recent | grep php
If you see denials, restore the correct SELinux context:
sudo restorecon -R /var/lib/php/sessions
Or switch to permissive mode temporarily to confirm SELinux is the culprit:
sudo setenforce 0
# test sessions
sudo setenforce 1
Verify the Fix
Time to confirm it actually works. Run this test script right after applying the fix:
<?php
session_start();
$_SESSION['test'] = 'ok';
echo 'Session ID: ' . session_id() . PHP_EOL;
echo 'Save path: ' . session_save_path() . PHP_EOL;
echo 'Status: ' . (session_status() === PHP_SESSION_ACTIVE ? 'ACTIVE' : 'FAILED');
Then check that a session file was actually created:
ls -la /var/lib/php/sessions/
You should see files like sess_abc123xyz with recent timestamps. An empty directory after running the test means the write is still failing.
Watch the PHP error log in real time while testing:
sudo tail -f /var/log/php8.2-fpm.log
# or
sudo journalctl -u php8.2-fpm -f
Tips
Not sure what chmod 1770 or 770 actually grants at 2 AM? The Unix Permissions Calculator on ToolCraft lets you visualize permission bits instantly โ before you accidentally open up too much access or lock PHP out entirely.
Prevention
- Add a healthcheck: A cron job running every 5 minutes that verifies the session directory exists and is writable โ and alerts you โ will catch this before users do.
- Lock down the path in php.ini: Don't rely on the default. Explicitly set
session.save_pathso it never silently changes after a package upgrade. - Use Redis or Memcached for sessions on sites handling hundreds of concurrent users:
session.save_handler = rediswithsession.save_path = "tcp://127.0.0.1:6379"eliminates filesystem permission issues entirely. - Monitor session errors: Set up log alerts for
session_start(): Failedso you catch this before users report it. - After OS upgrades: Always run
ls -la /var/lib/php/sessionsโ package updates occasionally reset ownership back to root.

