What's Actually Happening

Your Symfony application worked perfectly in development, but after deploying to production, users are seeing 500 Internal Server Error messages. The logs show cache-related exceptions, and when you investigate, you find that the Symfony cache directory contains corrupted or stale data that's preventing your application from functioning properly.

This typically happens after a deployment where the cache wasn't properly warmed, permissions are incorrect, or the cache directory contains mixed content from different application versions. The application tries to read cached configuration, routing, or twig templates that are either incomplete, have wrong permissions, or contain outdated serialized objects that no longer match the current codebase.

The Error You'll See

When cache corruption occurs, you might encounter several different error messages depending on what type of cache data is corrupted:

```bash # In production logs (var/log/prod.log) [2026-04-08T10:15:23.456789+00:00] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Cache\Exception\InvalidArgumentException: "Failed to unserialize cache entry" at /var/www/app/vendor/symfony/cache/Adapter/PhpAdapter.php line 87

[2026-04-08T10:15:24.123456+00:00] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Config\ConfigCacheFactoryException: "Unable to write in the cache directory" at /var/www/app/vendor/symfony/config/ConfigCacheFactory.php line 45

[2026-04-08T10:15:25.789012+00:00] request.CRITICAL: Uncaught PHP Exception RuntimeException: "Failed to write cache file" at /var/www/app/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php line 52

# When running cache:clear command $ php bin/console cache:clear --env=prod

[RuntimeException] Failed to remove directory "/var/www/app/var/cache/pro_/http_cache": rmdir(./var/cache/pro_/http_cache): Permission denied

# When running cache:warmup command $ php bin/console cache:warmup --env=prod

[InvalidArgumentException] The file "/var/www/app/var/cache/prod/pools/system/cache/collection.php" does not exist or is not readable.

# Browser error page HTTP 500 Internal Server Error An error occurred while loading the web debug toolbar. ```

Additional symptoms include: - Blank pages with no error messages in development mode - Inconsistent behavior where some pages work and others don't - Slow performance as the cache isn't being utilized - Missing translations or routing errors after deployment - Twig templates not rendering correctly

Why This Happens

  1. 1.Incomplete Cache Clear During Deployment: Your deployment script ran cache:clear but the command failed partway through, leaving some cache files deleted and others still present. This creates an inconsistent state where the application tries to read partial cache data.
  2. 2.Permission Mismatch: The cache directory was created by a different user (like root during a deployment) than the web server runs as (www-data, nginx, apache). When the application tries to write new cache files, it's denied permission because it doesn't own the directory or files.
  3. 3.Mixed Cache Versions: You deployed new code without clearing the old cache. The cached configuration and serialized objects reference old class names, method signatures, or properties that no longer exist in the new code. When Symfony tries to unserialize this data, it fails because the class structure changed.
  4. 4.Disk Space or Inode Exhaustion: The server ran out of disk space or inodes during cache:warmup, resulting in partially written cache files that are corrupted. The application then tries to read these incomplete files and fails.
  5. 5.Concurrent Cache Access: Multiple processes (like multiple PHP-FPM workers or parallel deployment commands) tried to write to the cache directory simultaneously, causing race conditions that resulted in corrupted files or symbolic links pointing to nowhere.
  6. 6.OPcache Serving Stale Code: PHP's OPcache is still serving old compiled PHP code even though new files exist on disk. The cache files reference the old code structure while the actual PHP classes have changed, causing unserialization failures.
  7. 7.Symbolic Link Issues: If you're using deployment strategies with symbolic links (like Capistrano or Deployer), the cache directory might point to a location that doesn't exist, or the symbolic link was broken during deployment.
  8. 8.App Environment Switch: The application environment variable changed (prod to staging, or vice versa) but the cache directory structure wasn't updated, causing the app to look for cache files in the wrong location.

Step 1: Identify Cache Corruption and Check Current State

First, diagnose the exact nature of the cache problem by examining the cache directory structure, permissions, and recent changes.

```bash # SSH into your production server ssh user@your-production-server

# Navigate to your Symfony application cd /var/www/your-symfony-app

# Check if the cache directory exists ls -la var/cache/

# Example output showing potential issues: # drwxrwxr-x 5 root root 4096 Apr 8 10:00 prod # drwxrwxr-x 3 www-data www-data 4096 Apr 7 15:30 dev

# Notice: prod cache owned by root, dev cache owned by www-data - PROBLEM!

# Check detailed permissions recursively find var/cache -type d -exec ls -ld {} \; | head -20

# Check file permissions within cache find var/cache -type f -exec ls -l {} \; | head -20

# Check if cache directory is writable by web server user sudo -u www-data touch var/cache/test_write && rm var/cache/test_write && echo "Writable" || echo "NOT WRITABLE"

# Check disk space df -h /var/www/your-symfony-app

# Check inode usage (can cause issues even with disk space available) df -i /var/www/your-symfony-app

# Look for cache pool files ls -la var/cache/prod/pools/ 2>/dev/null || echo "Pools directory missing"

# Check for broken symbolic links find var/cache -type l ! -exec test -e {} \; -print

# View recent cache-related errors in Symfony logs tail -100 var/log/prod.log | grep -i cache

# Check system logs for permission errors sudo tail -100 /var/log/nginx/error.log | grep -i "permission denied" sudo tail -100 /var/log/apache2/error.log | grep -i "permission denied" ```

Take note of any permission issues, missing directories, or broken symbolic links. The user owning the cache files should match the user running your PHP processes.

Step 2: Backup Current Cache State for Investigation

Before clearing the cache, save the current state for post-mortem analysis if needed later.

```bash # Create a backup of the corrupted cache mkdir -p /tmp/symfony-cache-backup cp -r var/cache/prod /tmp/symfony-cache-backup/prod-cache-$(date +%Y%m%d-%H%M%S)

# List cached files to understand what was cached find var/cache/prod -type f -name "*.php" -o -name "*.meta" | head -30

# Check for serialized objects in cache files grep -r "O:[0-9]*:" var/cache/prod/ 2>/dev/null | head -10

# Look for the specific cache file mentioned in the error if [ -f "var/cache/prod/pools/system/cache/collection.php" ]; then cat var/cache/prod/pools/system/cache/collection.php fi

# Check container cache (most common corruption point) if [ -f "var/cache/prod/srcApp_KernelProdContainer.php" ]; then head -50 var/cache/prod/srcApp_KernelProdContainer.php fi

# Check routes cache ls -la var/cache/prod/url_matching_routes.php* 2>/dev/null

# Check annotations cache ls -la var/cache/prod/annotations/ 2>/dev/null

# Save current cache statistics echo "Cache backup created at: /tmp/symfony-cache-backup/" > /tmp/cache-backup-report.txt echo "Cache directory size: $(du -sh var/cache/prod)" >> /tmp/cache-backup-report.txt echo "Number of cache files: $(find var/cache/prod -type f | wc -l)" >> /tmp/cache-backup-report.txt cat /tmp/cache-backup-report.txt ```

This backup allows you to investigate the root cause after restoring service, which is important for preventing future occurrences.

Step 3: Clear All Cache Directories Completely

Now remove the corrupted cache completely. This is the most reliable way to fix corruption.

```bash # Stop any running queue workers or background processes # If you use supervisor: sudo supervisorctl stop all

# Or if you use systemd services: sudo systemctl stop your-symfony-worker

# Clear OPcache by restarting PHP-FPM (critical step!) sudo systemctl restart php8.2-fpm # Or for older PHP versions: sudo systemctl restart php7.4-fpm

# Remove the entire cache directory rm -rf var/cache/prod

# Also clear dev and test caches if they exist rm -rf var/cache/dev rm -rf var/cache/test

# Clear the entire cache directory to be thorough rm -rf var/cache/*

# Verify directory is empty ls -la var/cache/

# Clear any session files that might be corrupted rm -rf var/sessions/prod/*

# Clear log files that might contain stale cache references # Keep the directory but clear old files find var/log -type f -name "*.log" -size +50M -exec truncate -s 0 {} \; ```

Important: Restarting PHP-FPM clears OPcache, which is essential. Without this step, PHP will continue serving stale compiled code even after you clear the Symfony cache.

Step 4: Fix Directory Permissions Correctly

Set up the correct permissions so the web server can create and manage cache files.

```bash # Determine your web server user # For nginx with php-fpm: usually www-data # For Apache: usually www-data or apache

# Set correct ownership of the entire var directory sudo chown -R www-data:www-data var/

# If you deploy with a different user, allow group write access sudo chown -R your-deploy-user:www-data var/ sudo chmod -R 775 var/

# Create the cache directory with correct permissions mkdir -p var/cache/prod sudo chown www-data:www-data var/cache/prod sudo chmod 775 var/cache/prod

# Ensure the var directory structure is correct mkdir -p var/log mkdir -p var/sessions/prod sudo chown -R www-data:www-data var/log var/sessions

# Set permissions for the entire project (if needed) # This ensures the web server can read your application files sudo setfacl -R -m u:www-data:rX . sudo setfacl -R -d -m u:www-data:rX .

# Allow www-data to write to var sudo setfacl -R -m u:www-data:rwX var/ sudo setfacl -R -d -m u:www-data:rwX var/

# Verify permissions ls -la var/ ls -la var/cache/

# Test that www-data can write to cache sudo -u www-data php -r "file_put_contents('var/cache/test.txt', 'test');" cat var/cache/test.txt && rm var/cache/test.txt ```

Step 5: Warm Up Cache Manually with Correct User

Generate fresh cache files by running the cache warmup command as the web server user.

```bash # Always run cache warmup as the web server user, not as root sudo -u www-data php bin/console cache:warmup --env=prod --no-debug

# If that fails, try with verbose output to see the error sudo -u www-data php bin/console cache:warmup --env=prod --no-debug -vvv

# Check if cache was created ls -la var/cache/prod/

# Verify cache pools were created ls -la var/cache/prod/pools/

# Check container cache ls -la var/cache/prod/*.php

# Verify the number of cache files created find var/cache/prod -type f | wc -l

# If warmup still fails, try clearing and warming in one command sudo -u www-data php bin/console cache:clear --env=prod --no-debug

# Clear and warmup explicitly sudo -u www-data php bin/console cache:pool:clear cache.global_clearer --env=prod

# For Symfony 5.4+, also clear the system cache pool sudo -u www-data php bin/console cache:pool:clear system --env=prod ```

If cache:warmup still fails, check the verbose output for specific errors. Common issues include missing extensions, wrong database credentials in .env, or missing configuration files.

Step 6: Verify Cache Integrity

After warming up the cache, verify that all critical cache files were created correctly and are readable.

```bash # Check that the container cache exists and is valid php -r " require 'vendor/autoload.php'; \$file = 'var/cache/prod/srcApp_KernelProdContainer.php'; if (file_exists(\$file)) { require_once \$file; echo 'Container cache file is valid PHP' . PHP_EOL; } else { echo 'Container cache file missing!' . PHP_EOL; } "

# Verify cache metadata files find var/cache/prod -name "*.meta" -exec php -r " \$file = \$argv[1]; try { unserialize(file_get_contents(\$file)); echo 'Valid: ' . \$file . PHP_EOL; } catch (Exception \$e) { echo 'CORRUPT: ' . \$file . ' - ' . \$e->getMessage() . PHP_EOL; } " {} \; 2>&1 | grep -i corrupt

# Test that the application can read its cache sudo -u www-data php bin/console about --env=prod

# Check for any cache-related errors sudo -u www-data php bin/console lint:container --env=prod

# Validate routing cache sudo -u www-data php bin/console router:match / --env=prod

# Check that Twig cache is working sudo -u www-data php bin/console debug:twig --env=prod

# List all cache pools sudo -u www-data php bin/console cache:pool:list --env=prod

# Verify permissions on newly created cache files find var/cache/prod -type d -exec ls -ld {} \; | grep -v www-data find var/cache/prod -type f -exec ls -l {} \; | grep -v www-data

# If any files are not owned by www-data, fix them sudo chown -R www-data:www-data var/cache/prod ```

Step 7: Restart All Services and Clear All PHP Caches

Restart PHP-FPM and your web server to ensure all caches are cleared and fresh.

```bash # Restart PHP-FPM to clear OPcache sudo systemctl restart php8.2-fpm

# Or restart all PHP services sudo systemctl restart php*.service

# Restart your web server # For Nginx: sudo systemctl restart nginx

# For Apache: sudo systemctl restart apache2

# If using a reverse proxy or load balancer, restart that too # For HAProxy: sudo systemctl restart haproxy

# For Varnish: sudo systemctl restart varnish

# Clear any external caches # Redis: redis-cli FLUSHDB # Or flush specific Symfony cache prefix: redis-cli --scan --pattern "symfony:*" | xargs -L 1000 redis-cli DEL

# Memcached: echo "flush_all" | nc localhost 11211

# Restart any queue workers sudo supervisorctl start all # Or: sudo systemctl start your-symfony-worker

# Check that services are running sudo systemctl status php8.2-fpm sudo systemctl status nginx ```

Step 8: Test Application Endpoints

Now test that your application is working correctly by hitting various endpoints.

```bash # Test the homepage curl -I https://your-domain.com/

# Should see HTTP/2 200 or similar success response

# Test a specific page curl -I https://your-domain.com/some-page

# Check for any PHP errors in the response curl -s https://your-domain.com/ | grep -i "error|exception|fatal"

# Test JSON API endpoints curl -H "Accept: application/json" https://your-domain.com/api/some-endpoint

# Check the application logs for errors tail -50 var/log/prod.log

# Monitor logs in real-time while testing tail -f var/log/prod.log &

# Make test requests and watch for errors curl -s https://your-domain.com/ > /dev/null curl -s https://your-domain.com/about > /dev/null curl -s https://your-domain.com/contact > /dev/null

# Stop log monitoring pkill -f "tail -f var/log/prod.log"

# Check for any fatal errors grep -i "fatal|error|exception" var/log/prod.log | tail -20 ```

Also test from a browser to ensure the user experience is correct and no JavaScript errors are occurring.

Step 9: Update Deployment Script to Prevent Recurrence

Modify your deployment process to prevent cache corruption in future deployments.

```bash # Edit your deployment script (adjust path based on your deployment tool) # For Deployer: nano deploy.php

# For Capistrano: nano config/deploy.rb

# For a custom bash script: nano scripts/deploy.sh ```

Add or update these deployment steps:

```php // For Deployer (deploy.php) task('cache:clear_and_warmup', function () { // Run as www-data user, not as root run('sudo -u www-data {{bin/console}} cache:clear --env={{symfony_env}} --no-debug'); run('sudo -u www-data {{bin/console}} cache:warmup --env={{symfony_env}} --no-debug'); });

task('deploy', [ 'deploy:prepare', 'deploy:lock', 'deploy:release', 'deploy:update_code', 'deploy:shared', 'deploy:vendors', 'deploy:writable', // Clear cache BEFORE updating code 'cache:clear_and_warmup', 'database:migrate', 'deploy:symlink', 'deploy:unlock', 'cleanup', // Restart PHP-FPM to clear OPcache 'php:restart', ])->desc('Deploy your project');

task('php:restart', function () { run('sudo systemctl restart php8.2-fpm'); }); ```

bash
# For a custom bash deployment script:
nano scripts/deploy.sh

```bash #!/bin/bash set -e

PROJECT_DIR="/var/www/your-symfony-app" RELEASE_DIR="$PROJECT_DIR/releases/$(date +%Y%m%d%H%M%S)" SHARED_DIR="$PROJECT_DIR/shared" WWW_USER="www-data"

# Clone/update code git clone git@github.com:your-repo.git "$RELEASE_DIR" cd "$RELEASE_DIR"

# Install dependencies composer install --no-dev --optimize-autoloader --no-interaction

# Link shared directories ln -sfn "$SHARED_DIR/.env.local" "$RELEASE_DIR/.env.local" ln -sfn "$SHARED_DIR/var/log" "$RELEASE_DIR/var/log" ln -sfn "$SHARED_DIR/var/sessions" "$RELEASE_DIR/var/sessions"

# Set permissions chown -R "$WWW_USER:$WWW_USER" "$RELEASE_DIR/var" chmod -R 775 "$RELEASE_DIR/var"

# Clear cache completely rm -rf "$RELEASE_DIR/var/cache/prod"

# Warm up cache as web user sudo -u "$WWW_USER" php "$RELEASE_DIR/bin/console" cache:warmup --env=prod --no-debug

# Run migrations sudo -u "$WWW_USER" php "$RELEASE_DIR/bin/console" doctrine:migrations:migrate --env=prod --no-interaction

# Switch to new release ln -sfn "$RELEASE_DIR" "$PROJECT_DIR/current"

# Restart PHP-FPM to clear OPcache sudo systemctl restart php8.2-fpm

# Restart workers sudo supervisorctl restart all ```

Step 10: Set Up Monitoring and Alerts

Configure monitoring to detect cache issues early before they cause outages.

bash
# Create a health check script
nano scripts/check-symfony-cache-health.sh

```bash #!/bin/bash # Symfony Cache Health Check Script

APP_DIR="/var/www/your-symfony-app" LOG_FILE="/var/log/symfony-cache-health.log" ALERT_EMAIL="ops@your-company.com"

# Check if cache directory exists and is writable if [ ! -d "$APP_DIR/var/cache/prod" ]; then echo "$(date): CRITICAL - Cache directory missing" >> "$LOG_FILE" echo "Symfony cache directory missing on production" | mail -s "CACHE ALERT" "$ALERT_EMAIL" exit 2 fi

# Check write permissions if ! sudo -u www-data test -w "$APP_DIR/var/cache/prod"; then echo "$(date): CRITICAL - Cache directory not writable" >> "$LOG_FILE" echo "Symfony cache directory not writable on production" | mail -s "CACHE ALERT" "$ALERT_EMAIL" exit 2 fi

# Check disk space (should have at least 10% free) DISK_USAGE=$(df "$APP_DIR" | tail -1 | awk '{print $5}' | sed 's/%//') if [ "$DISK_USAGE" -gt 90 ]; then echo "$(date): WARNING - Disk usage at ${DISK_USAGE}%" >> "$LOG_FILE" echo "Disk usage at ${DISK_USAGE}% on production server" | mail -s "DISK ALERT" "$ALERT_EMAIL" exit 1 fi

# Check inode usage INODE_USAGE=$(df -i "$APP_DIR" | tail -1 | awk '{print $5}' | sed 's/%//') if [ "$INODE_USAGE" -gt 90 ]; then echo "$(date): WARNING - Inode usage at ${INODE_USAGE}%" >> "$LOG_FILE" echo "Inode usage at ${INODE_USAGE}% on production server" | mail -s "INODE ALERT" "$ALERT_EMAIL" exit 1 fi

# Check for recent cache errors in logs ERRORS=$(grep -i "cache.*exception|failed to.*cache" "$APP_DIR/var/log/prod.log" 2>/dev/null | tail -10) if [ -n "$ERRORS" ]; then echo "$(date): WARNING - Cache errors found in logs" >> "$LOG_FILE" echo "Recent cache errors:\n$ERRORS" | mail -s "CACHE WARNINGS" "$ALERT_EMAIL" exit 1 fi

echo "$(date): OK - Cache healthy" >> "$LOG_FILE" exit 0 ```

```bash # Make script executable chmod +x scripts/check-symfony-cache-health.sh

# Add to crontab (run every 5 minutes) crontab -e ```

Add this line: `` */5 * * * * /var/www/your-symfony-app/scripts/check-symfony-cache-health.sh

```bash # Also add to your monitoring system (Prometheus, Nagios, etc.) # Example for Prometheus Node Exporter textfile collector: mkdir -p /var/lib/node_exporter/textfile_collector

cat > /var/lib/node_exporter/textfile_collector/symfony_cache.prom << 'EOF' symfony_cache_check_status{status="ok"} 1 symfony_cache_last_check_timestamp $(date +%s) EOF

# Create a more detailed check script for Nagios/Sensu/etc. nano scripts/nagios-symfony-cache.sh ```

```bash #!/bin/bash # Nagios plugin for Symfony cache health

STATE_OK=0 STATE_WARNING=1 STATE_CRITICAL=2 STATE_UNKNOWN=3

APP_DIR="/var/www/your-symfony-app"

# Check cache warmup time START=$(date +%s.%N) sudo -u www-data php "$APP_DIR/bin/console" cache:warmup --env=prod --no-debug > /dev/null 2>&1 END=$(date +%s.%N) WARMUP_TIME=$(echo "$END - $START" | bc)

if [ $(echo "$WARMUP_TIME > 10" | bc) -eq 1 ]; then echo "CACHE WARNING - Warmup took ${WARMUP_TIME}s" exit $STATE_WARNING fi

# Check cache directory size CACHE_SIZE=$(du -sm "$APP_DIR/var/cache/prod" | cut -f1) if [ "$CACHE_SIZE" -gt 100 ]; then echo "CACHE WARNING - Cache size ${CACHE_SIZE}MB" exit $STATE_WARNING fi

echo "CACHE OK - Warmup ${WARMUP_TIME}s, Size ${CACHE_SIZE}MB" exit $STATE_OK ```

Checklist for Fixing Symfony Cache Corruption

StepActionCommandStatus
1Identify cache corruptionls -la var/cache/, tail var/log/prod.log
2Backup corrupted cachecp -r var/cache/prod /tmp/backup/
3Clear cache directoriesrm -rf var/cache/prod
4Fix directory permissionschown -R www-data:www-data var/
5Warm up cache manuallysudo -u www-data php bin/console cache:warmup --env=prod
6Verify cache integrityphp bin/console about --env=prod
7Restart PHP-FPM and web serversystemctl restart php8.2-fpm nginx
8Test application endpointscurl -I https://your-domain.com/
9Update deployment scriptAdd cache:warmup as www-data user
10Set up monitoringCreate health check script and cron job

Verify the Fix

After completing all steps, verify that your Symfony application is working correctly:

```bash # 1. Check cache directory exists and has correct permissions ls -la var/cache/prod # Should show www-data ownership and files present

# 2. Verify no cache errors in recent logs tail -100 var/log/prod.log | grep -i cache # Should return no results or only old entries

# 3. Test application responds successfully curl -I https://your-domain.com/ # Should return HTTP 200

# 4. Verify cache pools are working sudo -u www-data php bin/console cache:pool:list --env=prod # Should list all cache pools

# 5. Test cache can be written and read sudo -u www-data php bin/console cache:item:set test_key test_value --env=prod sudo -u www-data php bin/console cache:item:get test_key --env=prod # Should show "test_value"

# 6. Verify cache warmup is fast time sudo -u www-data php bin/console cache:warmup --env=prod # Should complete in under 5 seconds for a typical application

# 7. Check PHP-FPM status sudo systemctl status php8.2-fpm # Should show active (running)

# 8. Monitor for cache errors over time watch -n 5 'tail -20 var/log/prod.log | grep -i cache' # Run for a few minutes while hitting the site

# 9. Verify no permission errors in web server logs sudo tail -100 /var/log/nginx/error.log | grep -i "permission denied" # Should return no results

# 10. Run a full test suite if available php bin/phpunit --env=prod # All tests should pass ```

  • [Fix PHP Composer Dependency Conflict](/articles/fix-php-composer-dependency-conflict) - Resolve Composer lock file conflicts
  • [Fix PHP-FPM Pool Exhausted](/articles/fix-php-fpm-pool-exhausted) - When PHP-FPM runs out of worker processes
  • [Fix Nginx 502 Bad Gateway to PHP-FPM](/articles/fix-nginx-502-bad-gateway-to-php-fpm) - Connection issues between Nginx and PHP-FPM
  • [Fix Opcache Serving Old PHP Code After Deploy](/articles/fix-opcache-serving-old-php-code-after-deploy) - Stale OPcache issues
  • [Fix Laravel Migration Locked](/articles/fix-laravel-migration-locked) - Laravel-specific cache issues
  • [Fix Site Down After PHP Update](/articles/fix-site-down-after-php-update) - Post-update troubleshooting
  • [Fix WordPress PHP Version Incompatible](/articles/fix-wordpress-php-version-incompatible) - PHP version compatibility issues