Are you an LLM? You can read better optimized documentation at /analyzers/reliability/directory-write-permissions.md for this page in Markdown format
Directory Write Permissions Analyzer
| Analyzer ID | Category | Severity | Time To Fix |
|---|---|---|---|
directory-write-permissions | ✅ Reliability | Critical | 10 minutes |
What This Checks
- Verifies that
storage/directory is writable - Verifies that
bootstrap/cache/directory is writable - Checks custom directories if configured (via published config file)
- Tests actual write permissions, not just existence
- Validates both relative and absolute paths from configuration
- Reports all failed directories with actionable fix commands
- Supports symlinked directories
Why It Matters
- Application crashes: Laravel requires writable storage for logs, sessions, cache, and compiled views - without it, your app will fail
- Silent failures: Missing write permissions can cause intermittent errors that are hard to debug in production
- Security logs: Without writable storage, security events and errors won't be logged, hiding potential attacks
- Performance degradation: Cache and compiled views require write access - without it, your app runs significantly slower
- Session management: User sessions require writable storage - login/authentication will fail without it
- File uploads: User file uploads to
storage/will fail silently or with cryptic errors - Deployment issues: Fresh deployments often fail due to incorrect directory permissions, especially in Docker/CI environments
How to Fix
Quick Fix (5 minutes)
- Identify which directories are not writable:
bash
# Check current permissions
ls -la storage/
ls -la bootstrap/cache/1
2
3
2
3
- Fix permissions based on your environment:
Development (Local Machine):
bash
# Make directories writable
chmod -R 775 storage bootstrap/cache1
2
2
Production (Linux Server with www-data user):
bash
# Set correct owner and permissions
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache1
2
3
2
3
Docker Container:
bash
# In your Dockerfile
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
RUN chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache1
2
3
2
3
Windows:
powershell
# Right-click folders → Properties → Security tab
# Grant "Full Control" to your web server user
# Or run as Administrator:
icacls storage /grant Users:F /t
icacls bootstrap\cache /grant Users:F /t1
2
3
4
5
2
3
4
5
Proper Fix (10 minutes)
- Configure deployment automation to set permissions:
yaml
# .github/workflows/deploy.yml
- name: Set Directory Permissions
run: |
chmod -R 775 storage bootstrap/cache
chown -R www-data:www-data storage bootstrap/cache1
2
3
4
5
2
3
4
5
- Add post-deployment script:
bash
#!/bin/bash
# deploy/fix-permissions.sh
# Set ownership
chown -R www-data:www-data storage bootstrap/cache
# Set directory permissions (775 = rwxrwxr-x)
chmod -R 775 storage bootstrap/cache
# Ensure new files inherit correct permissions
chmod g+s storage bootstrap/cache
echo "✓ Directory permissions configured"1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- Configure umask in your web server:
Nginx + PHP-FPM (/etc/php/8.1/fpm/pool.d/www.conf):
ini
; Set umask so new files are group-writable
php_admin_value[umask] = 00021
2
2
Apache (.htaccess or VirtualHost):
apache
<IfModule mod_php.c>
php_value umask 0002
</IfModule>1
2
3
2
3
- Update your
.gitignoreto exclude storage files:
/storage/*.key
/storage/app/*
!/storage/app/.gitignore
/storage/framework/cache/*
!/storage/framework/cache/.gitignore
/storage/framework/sessions/*
!/storage/framework/sessions/.gitignore
/storage/framework/testing/*
!/storage/framework/testing/.gitignore
/storage/framework/views/*
!/storage/framework/views/.gitignore
/storage/logs/*
!/storage/logs/.gitignore1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- Configure custom writable directories (optional):
If you need to check additional directories beyond the defaults (storage and bootstrap/cache), publish the config:
bash
php artisan vendor:publish --tag=shieldci-config1
Then in config/shieldci.php:
php
'writable_directories' => [
'storage',
'bootstrap/cache',
'public/uploads', // If you store uploads here
'resources/compiled', // Custom compiled assets
],1
2
3
4
5
6
2
3
4
5
6
TIP
By default, the analyzer checks storage and bootstrap/cache directories. You only need to publish the config if you want to check additional directories.
References
- Laravel Installation - Directory Permissions
- Linux File Permissions Guide
- Docker Security Best Practices
- Nginx + PHP-FPM Configuration
Related Analyzers
- Environment File Existence Analyzer - Ensures .env file exists and is readable
- Cache Status Analyzer - Validates cache connectivity and functionality