I recently encountered a baffling issue where PHP scripts couldn't access files in the /tmp directory, despite having all the right permissions and configurations. The system runs nginx with PHP-FPM, using 'http' as the service account.
// Test case that exposes the issue
touch("/tmp/boo");
var_dump(file_exists("/tmp/boo")); // Returns true
// Yet no "boo" file appears in the actual /tmp directory
The key environmental factors:
- tmpfs mounted at /tmp (rw permissions)
- No open_basedir restrictions
- Files owned by http user (nginx/PHP service account)
- Direct OS commands work:
sudo -u http cat /tmp/file
The most telling behavior was that PHP would report successful file operations but the files wouldn't actually appear in /tmp. This pointed to a namespace isolation issue rather than a simple permission problem.
Error logs showed:
PHP Warning: file(/tmp/ydlw/pid): failed to open stream: No such file or directory
After extensive testing, the issue stemmed from PHP-FPM's process isolation. Many modern PHP-FPM configurations implement either:
- A chroot jail
- Namespace isolation (particularly common with containerized setups)
- Private /tmp mounts
Here are several approaches to resolve this:
1. Check PHP-FPM Configuration
; In php-fpm.conf or pool configuration
; Disable chroot if present
chroot =
; Ensure proper paths are accessible
security.limit_extensions = .php
2. Verify Mount Namespaces
Check if PHP-FPM uses a private /tmp mount:
# Compare mount points between PHP-FPM and shell
sudo -u http ls /proc/$(pgrep -u http php-fpm | head -1)/root/tmp
3. Alternative Temporary Directories
Consider using sys_get_temp_dir() or explicit paths:
$tempDir = ini_get('upload_tmp_dir') ?: sys_get_temp_dir();
$tempFile = tempnam($tempDir, 'php_');
file_put_contents($tempFile, "test data");
Create a diagnostic script:
<?php
$testFile = '/tmp/php_test_'.time();
file_put_contents($testFile, 'test');
echo "PHP reports: ";
var_dump(file_exists($testFile));
echo "Actual check: ";
system("sudo -u http ls -la $testFile 2>&1");
?>
If the issue persists, consider these advanced checks:
# Check mount namespace
sudo ls -la /proc/$(pgrep -u http php-fpm | head -1)/ns
# Check for overlay mounts
mount | grep tmp
For permanent solutions in php-fpm pool configuration:
[www]
; Ensure these are not set
;chroot = /path
;php_admin_value[open_basedir] = /path
; Alternative if namespacing is required
php_admin_value[sys_temp_dir] = /actual/tmp/path
I recently encountered a puzzling situation where PHP scripts couldn't access files in the /tmp
directory, despite all permissions being correctly set. The twist? This was specifically happening with tmpfs-mounted /tmp
, while regular file system access worked fine elsewhere.
The problem manifested with these characteristics:
- PHP could access
/etc
,/usr
,/proc
, and/home
without issues open_basedir
was not set or restricting accesssudo -u http cat /tmp/file
worked fine- PHP functions like
file_exists()
andfile()
failed with "No such file or directory"
When running this test code:
touch("/tmp/boo");
var_dump(file_exists("/tmp/boo")); // Returns true
exec("ls /tmp", $output); // "boo" doesn't appear in the listing
We get the bizarre situation where PHP thinks the file exists, but it's not actually visible in the directory.
The core issue stems from how modern Linux systems handle mount namespaces for security. In many configurations:
- PHP-FPM or the web server runs in a separate mount namespace
- The tmpfs mount at
/tmp
isn't propagated to this namespace - PHP sees an empty
/tmp
directory instead of the actual tmpfs mount
For systemd-based systems using PHP-FPM:
# Edit the PHP-FPM service unit
sudo systemctl edit php-fpm.service
[Service]
MountFlags=shared
This makes the /tmp
mount visible to PHP-FPM's namespace.
Create a dedicated tmp directory for PHP:
# Create directory
sudo mkdir -p /var/tmp/php_tmp
sudo chown http:http /var/tmp/php_tmp
sudo chmod 1777 /var/tmp/php_tmp
# Configure PHP to use it
sudo sed -i 's|;upload_tmp_dir =|upload_tmp_dir = /var/tmp/php_tmp|' /etc/php/php.ini
Create a bind mount from the "real" /tmp:
sudo mkdir -p /srv/tmp
sudo mount --bind /tmp /srv/tmp
Then use /srv/tmp
in your PHP scripts.
After implementing any solution, verify with:
<?php
file_put_contents('/tmp/testfile', 'test');
echo file_exists('/tmp/testfile') ? "Working!" : "Still broken";
Remember to check both the PHP output and the actual filesystem.