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

bash
[RuntimeException]
Failed to write cache file "/var/www/var/cache/prod/ContainerXYZ/MyApp_KernelProdContainer.php".

Or:

bash
Warning: file_put_contents(/var/www/var/cache/prod/pools/xyz): Failed to open stream: Permission denied

Or during deployment:

bash
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" directory

Common 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