Common causes
- The file or its directory is owned by root or a deploy user and PHP's user cannot write to it
- A parent directory lacks the execute (x) bit, so PHP cannot traverse the path
- Files were created by a CLI command or cron job running as a different user
- SELinux on RHEL-based systems blocks web writes unless the path has httpd_sys_rw_content_t
- The target directory does not exist, so PHP tries to create a file in a non-writable parent
- A read-only filesystem or container volume mounted without write access
How to fix it
- Find out which user PHP runs as. Run ps -o user= -C php-fpm8.3 (or check user = in the FPM pool file). In a script, echo posix_getpwuid(posix_geteuid())['name'];.
- Inspect the full path. Run namei -l /var/www/html/storage/cache/data.json to see owner and mode of each directory. Every directory needs x for the PHP user, and the final directory needs w to create files.
- Fix ownership of writable directories. Give the PHP user ownership of only the writable folders, for example sudo chown -R www-data:www-data storage bootstrap/cache or wp-content/uploads. Keep code files owned by the deploy user.
- Set sane modes. Use 775 for shared writable directories and 664 for files when deploy user and PHP share a group, or 755/644 when PHP owns them. Avoid 777.
- Fix SELinux labels. On RHEL, Alma or Rocky run sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/storage(/.*)?" and sudo restorecon -Rv /var/www/html/storage. Check denials with sudo ausearch -m avc -ts recent.
- Run CLI tasks as the web user. Run artisan, WP-CLI or cron scripts with sudo -u www-data so new files get the same owner as files created by web requests.
Shell (Laravel-style writable dirs)
sudo chown -R deploy:www-data /var/www/html
sudo find /var/www/html -type d -exec chmod 755 {} +
sudo find /var/www/html -type f -exec chmod 644 {} +
sudo chmod -R ug+rwX /var/www/html/storage /var/www/html/bootstrap/cache
namei -l /var/www/html/storage/logs How to stop it happening again
- Set ownership and permissions in your deploy script, not by hand
- Run cron jobs and CLI commands as the same user as PHP-FPM
- Keep writable data in a few dedicated directories
- Use setgid on shared directories (chmod g+s) so new files inherit the group