3 min read

Solving Supervisor Log Conflicts in Laravel

How setting up Slack production monitoring helped us finally identify and resolve a persistent Laravel/Supervisor permission race condition that had been sidelined for two years.

I’ve been managing an internal Laravel app for quite some time now. We recently started an Integration between Enterprice systems like SAP and Salesforce. While we stabilized the complex data mappings between systems, one nagging infrastructure issue remained: a recurring Permission denied error in our daily logs.

This issue wasn’t a mystery-it had actually been present for the last two years. Because it was intermittent and didn’t crash the core business logic, it remained a low-priority “noise” item on my task list while I focused on higher-impact features. TBH I was a bit of worried as well to act against the infamous principle If it works, Don’t touch it

The Reality of Production Monitoring

Monitoring wasn’t the missing piece; I had actually set up a Slack notification channel for production errors nearly two years ago. For a long time, I simply “lived with” the occasional burst of log-permission alerts. However, as our SAP integration grew more complex and our data volume increased, the frequency of these bulk error notifications became impossible to ignore.

The Problem: A Daily Race Condition

The error occurred when the Laravel daily log file rotated at midnight. Even though our logger configuration was already correctly set to 0664 in config/logging.php, we still faced a classic race condition:

  • Scenario A: If the Nginx web server (www-data) triggered the log creation first, the worker (sa) couldn’t append to it.
  • Scenario B: If the Supervisor worker (sa) created the file first, the web server was locked out.

Despite the Laravel application-level config being correct, the OS-level defaults for the worker process were overriding our intent during the file creation.

The Solution

The fix required a more disciplined approach to our Supervisor and server configuration:

  1. Group Ownership: I ensured the storage/logs directory was group-owned by www-data, allowing both the worker process and the web server to share the same files seamlessly.
  2. User Groups: I have added the sa user to the www-data group, since this user is the one writing logs from the worker. This is the command used sudo usermod -aG www-data sa
  3. Supervisor umask=002: I updated the Supervisor configuration to ensure the worker process explicitly creates files with group-write permissions. This ensures that even when the worker “wins” the race at midnight, the web server still has access.
  4. The “Missing” Step: I realized that simply restarting Supervisor isn’t enough. Applying a umask change requires running supervisorctl reread and supervisorctl update to re-read the configuration into the system’s memory.

This time I have more confidence that it won’t happen again!!


BUT… it happened again!


Long story short, after two days I started to see the error message loops agains in the Slack..

Upon checking again, I found I missed a command which will enforce the future files also fall into the same www-data group.

sudo chmod -R 2775 storage/logs

So they call it “Sticky” permissions, and recommend to run on the folder bootstrap/cache, which I didn’t. Anyway this command will make sure the next log will be under the group www-data regardless of which user created it.