PHP Script Unable to Access /tmp Directory Despite Proper Permissions: tmpfs Mount Investigation


1 views

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:

  1. A chroot jail
  2. Namespace isolation (particularly common with containerized setups)
  3. 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 access
  • sudo -u http cat /tmp/file worked fine
  • PHP functions like file_exists() and file() 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.