Introduction
Symfony's cache and log directories (var/cache/, var/log/) are written by both the CLI user (running console commands) and the web server user (running PHP-FPM). When these users differ, permission conflicts cause Failed to write cache file, Permission denied, or Unable to create directory errors. The issue is particularly acute during cache warmup when Symfony rebuilds the entire cache directory, and concurrent requests try to write to the same files. Without proper ACL configuration, one user creates files the other cannot write to, causing intermittent failures that are hard to reproduce.
Symptoms
[RuntimeException]
Failed to write cache file "/var/www/var/cache/prod/ContainerXYZ/MyApp_KernelProdContainer.php".Or:
Warning: file_put_contents(/var/www/var/cache/prod/pools/xyz): Failed to open stream: Permission deniedOr during deployment:
cache:clear
// Clearing the cache for the prod environment with debug false
[OK] Cache for the "prod" environment (debug=false) was successfully cleared.
[ERROR] Unable to write in the "/var/www/var/cache/prod" directoryCommon Causes
- CLI user differs from web server user: www-data vs deploy user
- Cache directory owned by wrong user: Files created by CLI not writable by web server
- setfacl not configured: ACL not set for shared directory access
- Concurrent cache warmup: Multiple processes warmup cache simultaneously
- Umask too restrictive: Default umask 022 prevents group write
- Docker volume permissions: Host-mounted volumes have wrong ownership
Step-by-Step Fix
Step 1: Configure ACL for shared directories
```bash # Set ACL for both web server user and CLI user # Run as root or with sudo
HTTPDUSER=$(ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1)
# Set ACL on var directory sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var
# Verify getfacl var/cache/ # Should show both users with rwx permissions ```
Step 2: Configure umask for deployment
```bash # In deployment script umask 0002 # Group-writable files (664) and directories (775)
# Or in PHP-FPM pool config ; /etc/php/8.2/fpm/pool.d/www.conf user = www-data group = www-data listen.owner = www-data listen.group = www-data php_admin_value[umask] = 002 ```
Step 3: Handle cache warmup safely
```bash #!/bin/bash # deploy.sh - Safe cache warmup
cd /var/www
# Install dependencies composer install --no-dev --optimize-autoloader
# Warm up cache with correct permissions sudo -u www-data php bin/console cache:warmup --env=prod
# OR: Use the two-phase warmup approach php bin/console cache:clear --env=prod # Clears and warms in temp directory # Then atomically swap the cache directory (Symfony does this automatically)
# Verify cache is writable php bin/console cache:pool:clear cache.global_clearer --env=prod ```
Prevention
- Configure ACL with setfacl for both web server and CLI users
- Set umask 002 in deployment scripts and PHP-FPM configuration
- Use the same user for CLI commands as the web server when possible
- Never manually delete var/cache/ directories while the application is running
- Use cache:clear instead of rm -rf var/cache for safe cache management
- Add permission checks to deployment scripts to verify writable directories
- Monitor for permission denied errors in production logs