How to Run Cron Jobs as Apache User for File Permission Synchronization in PHP


8 views

When PHP scripts create files through different execution contexts (web vs CLI), we often encounter permission mismatches. Here's a typical scenario:

// test_permissions.php
<?php 
file_put_contents('web_generated.txt', 'Created via web');
echo 'Running as: '.exec('whoami');

Browser output shows apache user while cron output shows your personal username. This creates permission issues when files need modification from both contexts.

1. Proper User Context Execution

The most elegant solution is running the cron job under the Apache user:

# In crontab -e
* * * * * /usr/bin/sudo -u apache /usr/bin/php /path/to/script.php

Requires sudoers configuration (add to /etc/sudoers):

yourusername ALL=(apache) NOPASSWD: /usr/bin/php /path/to/script.php

2. Group-Based Permission Management

Create a dedicated group for web content:

sudo groupadd webcontent
sudo usermod -a -G webcontent apache
sudo usermod -a -G webcontent yourusername
sudo chgrp -R webcontent /path/to/webdir
sudo chmod -R 2775 /path/to/webdir

The 2775 sets SGID (2) for group inheritance and gives read/write to group (7/5).

3. PHP-Level Permission Control

Modify your PHP script to enforce permissions:

<?php
$file = 'generated_image.jpg';
file_put_contents($file, $image_data);

// Set group ownership to Apache's group
chown($file, fileowner($file));
chgrp($file, 'apache');
chmod($file, 0664); // rw-rw-r--

For more granular control on modern systems:

sudo setfacl -Rm u:apache:rwx,d:u:apache:rwx /path/to/dir
sudo setfacl -Rm u:yourusername:rwx,d:u:yourusername:rwx /path/to/dir

Here's how we implement this in our deployment scripts:

#!/bin/bash
WEB_USER="apache"
WEB_GROUP="apache"
CONTENT_DIR="/var/www/generated_content"

# Ensure proper ownership
sudo chown -R ${WEB_USER}:${WEB_GROUP} ${CONTENT_DIR}
sudo chmod -R 2775 ${CONTENT_DIR}

# Set ACL for development team
DEV_USERS=("user1" "user2")
for user in "${DEV_USERS[@]}"; do
    sudo setfacl -Rm u:${user}:rwx ${CONTENT_DIR}
done

This maintains security while allowing both web and CLI access to generated files.


When your PHP script executes through different contexts (web vs CLI), you encounter ownership discrepancies. Through Apache, files are created as apache:apache, while cron jobs default to your user account (youruser:yourgroup). This creates a permissions nightmare for file management.

1. Running Cron as Apache Directly

The most elegant solution is to execute the cron job under the Apache user identity. Here's how to implement it:

# In crontab (edit using 'sudo crontab -e')
* * * * * /usr/bin/sudo -u apache /usr/bin/php /path/to/your/script.php

Prerequisites:

# Allow your user to run PHP as Apache without password
youruser ALL=(apache) NOPASSWD: /usr/bin/php

2. Group-based Permission Management

Create a shared group for both Apache and your user:

# Create new group
sudo groupadd webdev

# Add users to group
sudo usermod -a -G webdev apache
sudo usermod -a -G webdev youruser

# Set directory permissions
sudo chown -R :webdev /path/to/web/directory
sudo chmod -R 2775 /path/to/web/directory

This implements the SETGID bit (2) to ensure new files inherit the group.

3. PHP-based Permission Adjustment

Modify your PHP script to explicitly set permissions:

<?php
// After file creation
chmod('/path/to/newfile.jpg', 0664);
chown('/path/to/newfile.jpg', 'apache'); // Requires root privileges
?>

For this to work, you'll need to either:

  • Run the script via sudo in cron (as shown in solution 1)
  • Configure sudoers to allow specific chown/chmod commands

While universally writable permissions solve the immediate problem, they introduce security vulnerabilities. If you must use this approach:

// In your PHP script
umask(0); // Dangerous!
$fp = fopen('/path/to/file', 'w');
fwrite($fp, $data);
fclose($fp);

Always prefer more granular solutions when possible.

For production systems, I recommend combining solutions 1 and 2:

  1. Create a webdev group with proper directory permissions
  2. Run cron jobs as Apache user via sudo
  3. Implement proper umask settings in PHP
# Example complete solution
sudo groupadd webdev
sudo usermod -a -G webdev apache
sudo usermod -a -G webdev youruser
sudo chown -R :webdev /var/www/html/uploads
sudo chmod -R 2775 /var/www/html/uploads
sudo chmod g+s /var/www/html/uploads

This ensures all newly created files will be writable by both Apache and your user while maintaining security.