Worker Privilege
Worker Privilege
Section titled “Worker Privilege”After reading this page you can clear the Worker Privilege warning on the rConfig V8 Pro dashboard: confirm that Horizon, the scheduler and the SNMP trap receiver run as the web server user and not as root, fix whatever the check reports, and recheck straight away. It explains every line the check can print and the fix for each one.
When to use this page
Section titled “When to use this page”Use this page when:
- the dashboard Health Status panel shows a Worker Privilege alert
- a row in the Security & privileges card on Settings > System Management > System Optimization shows FAIL or WARN
- the SNMP Traps page shows “The receiver is not running as the web user”
sudo php artisan rconfig:check-worker-userreports a FAIL or WARN- the deploy script stops at its final worker check
It applies to bare metal installs only. In Docker the container runs Horizon and the scheduler itself, so the checks are skipped and the dashboard shows “Not applicable in Docker.”
Why must the workers not run as root?
Section titled “Why must the workers not run as root?”Horizon runs every queued job, including device connections, config downloads and script connections. The scheduler starts all scheduled work through schedule:run. If either runs as root, a flaw in any job runs with full control of the server, and anyone who can queue a job can run code as root. Root-run workers also leave root-owned files in storage and bootstrap/cache, which the web server then cannot write, causing permission errors and blank pages.
From V8.4.1, everything runs as the web server user: apache on RHEL, Rocky, Alma, CentOS, Oracle and Amazon Linux, and www-data on Ubuntu and Debian.
| Part | Where it runs | Set up by |
|---|---|---|
| Horizon | Supervisor program horizon, with user= set to the web user. The config is a root-owned 0644 file: /etc/supervisord.d/horizon_supervisor.ini on RHEL family systems, /etc/supervisor/conf.d/horizon_supervisor.conf on Ubuntu. | The deploy script, on every deploy |
| Scheduler | One line in the web user’s own crontab (crontab -u apache -l) | The deploy script, which also removes any old line from root’s crontab |
| SNMP trap receiver | The systemd service rconfig-snmptrap, with User= set to the web user | sudo php artisan rconfig:snmp-trap-service install, and the deploy script for receivers that already exist. See SNMP trap receiver service. |
Installs deployed before 20 October 2026 usually ran Horizon and the scheduler as root, and every install before V8.4.1 ran the SNMP trap receiver as root under Supervisor. Deploying V8.4.1 moves them over.
What does the check report?
Section titled “What does the check report?”rconfig:check-worker-user runs five checks. The dashboard alert, the System Optimization rows and the command all read the same report, so they always agree.
| Check | What it looks at |
|---|---|
| Horizon | The running artisan horizon processes for this rConfig folder, and which user owns them. |
| Supervisor config | That exactly one Supervisor config runs this install’s Horizon, and that it is a regular root:root 0644 file with user= set to the web user. |
| Scheduler (web user crontab) | That the web user’s crontab has a line that runs this install’s schedule:run. |
| Root crontab | That root’s crontab has no schedule:run line for this install, and that the scheduler has not run as root in the last 15 minutes. Root’s crontab is only read when the check runs with sudo. |
| SNMP trap receiver | If the receiver is installed, that it runs from the rconfig-snmptrap systemd service as the web user, and not from an old Supervisor program. |
Only things that belong to this rConfig folder count. Another rConfig install on the same server is never mistaken for this one.
Each line starts with one of four results:
| Result | Meaning |
|---|---|
OK | The check passed. |
WARN | Something needs a look, but nothing was found running as root. For example, Horizon is not running. |
SKIP | The check does not apply or could not run. For example, the SNMP receiver is not installed, or root’s crontab was not read without sudo. |
FAIL | Part of rConfig runs as root or is set up unsafely. The command exits with code 1. |
The Overall line sums up the server, and also says how the SNMP receiver runs (systemd, Supervisor, another install, or not installed):
| Overall | Meaning |
|---|---|
All good | Nothing runs as root. |
Still running as root | At least one part runs as root and nothing yet runs as the web user. Typical of a server last deployed before V8.4.1. |
Mixed: some parts run as the web user and some as root | The move is half done, for example a new Supervisor config while an old root crontab line still runs the scheduler. |
Not set up | Horizon is not running, and neither the Supervisor config nor the scheduler line exists. |
Misconfigured | Something fails without pointing at root, for example a config file with the wrong owner or mode. |
On the dashboard, the alert shows the overall line, every problem found, and the fix to run. On System Optimization, each check has its own row with a link to its section below.
Prerequisites
Section titled “Prerequisites”- Shell access to the rConfig server with
sudo. - rConfig V8 Pro V8.4.1 or later. See How to update rConfig V8 Pro.
- The examples use the RHEL family web user
apacheand the default folder/var/www/html/rconfig8/current. On Ubuntu usewww-data.
How do I fix it?
Section titled “How do I fix it?”-
Run the check as root, so it can read root’s crontab:
Terminal window cd /var/www/html/rconfig8/currentsudo php artisan rconfig:check-worker-userA server last deployed before V8.4.1 typically shows something like:
Web user: apache (checked as root)FAIL Horizon: Horizon runs as root (PID 3346). It must run as apache only.FAIL Supervisor config: /etc/supervisord.d/horizon_supervisor.ini is a symlink. It must be a regular file owned by root, so the web user cannot change what root loads. /etc/supervisord.d/horizon_supervisor.ini sets user=root, not apache.FAIL Scheduler (web user crontab): apache's crontab has no line that runs this install's scheduler.FAIL Root crontab: Root's crontab still runs this install's scheduler as root: * * * * * php /var/www/html/rconfig8/current/artisan schedule:run >> /dev/null 2>&1FAIL SNMP trap receiver: Old Supervisor config /etc/supervisord.d/snmptrap_supervisor.ini still runs the SNMP trap receiver for this install. It has not been migrated to systemd.Overall: Still running as root (SNMP trap receiver: Supervisor)Fix:- ...Find any line you do not recognise under its check below. Every FAIL line has a fix under
Fix:. -
Re-run the deploy. Open Settings > System Management > Update > Instructions and run the commands shown there, as described in How to update rConfig V8 Pro. The V8.4.1 deploy script fixes Horizon, the Supervisor config and both crontabs, moves an SNMP receiver that was set up under Supervisor over to systemd, and runs
rconfig:check-worker-useras its last step. -
If the SNMP trap receiver still fails, install the service from the rConfig folder:
Terminal window cd /var/www/html/rconfig8/currentsudo php artisan rconfig:snmp-trap-service install -
Confirm the fix:
Terminal window sudo php artisan rconfig:check-worker-userecho $?Every line should read OK or SKIP, the overall line should be
All good, and the exit code0. Only a FAIL exits non-zero, so read any WARN line too. -
Recheck the dashboard now rather than waiting for the next run. Run the health checks as the web server user, never as root:
Terminal window # RHEL, Rocky, Almasudo -u apache php artisan health:check# Ubuntusudo -u www-data php artisan health:checkRefresh the dashboard. If the only thing left is “The scheduler ran as root in the last 15 minutes”, wait 15 minutes and check again (see Root crontab).
What does each check mean, and how do I fix it?
Section titled “What does each check mean, and how do I fix it?”Re-running the deploy fixes every item in one go, and is the supported route. Use the tables to read a single message, or to fix one item by hand when you cannot deploy right now. Messages print the real paths and user for your server.
Horizon
Section titled “Horizon”| Message | Fix |
|---|---|
Horizon runs as apache (PID …). (OK) | Nothing to do. |
Horizon runs as root (PID …). It must run as apache only. | Re-run the deploy. To fix it by hand, make sure the Horizon Supervisor config has user=apache (see Supervisor config), then run sudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl restart horizon. |
Horizon runs as apache (PID …) and root (PID …). It must run as apache only. | Two Horizons are running, usually from two Supervisor configs or a Horizon started by hand. The Supervisor config check lists every config that runs this install. Remove the extra one and stop any Horizon you started yourself. |
Horizon is not running, so its user could not be checked. (WARN) | Start it with sudo supervisorctl start horizon. If supervisorctl status already shows it as RUNNING, /proc is probably mounted with hidepid, which hides other users’ processes from the check. Run the check with sudo, or read the user= line in the Supervisor config instead. |
To see who owns each Horizon process:
ps -eo user=,pid=,args= | grep '[a]rtisan horizon$'Horizon running as root leaves files in storage that belong to root. The deploy hands them back. To do it by hand, change only files that are not already the web user’s:
cd /var/www/html/rconfig8/currentsudo find storage bootstrap/cache \( ! -user apache -o ! -group apache \) -exec chown -h apache:apache {} +Supervisor config
Section titled “Supervisor config”The deploy writes the Horizon config from the horizon_supervisor.ini template on every deploy, as a regular file owned by root:root with mode 0644 and user= set to the web user. It replaces anything you change by hand, so do not edit it.
| Message | Fix |
|---|---|
/etc/supervisord.d/horizon_supervisor.ini is root:root 0644 and runs Horizon as apache. (OK) | Nothing to do. |
No Supervisor config in /etc/supervisord.d or /etc/supervisor/conf.d runs this install's Horizon (…), so Supervisor does not run it. | Re-run the deploy. |
… is a symlink. It must be a regular file owned by root, so the web user cannot change what root loads. | Older installs linked this file from the rConfig folder, which the web user can write. Supervisor runs as root, so that let the web user choose what root runs. Re-run the deploy, which replaces the link with a root-owned file. |
… is owned by … , not root:root. or … has mode …, not 0644. | Re-run the deploy, or fix it by hand: sudo chown root:root <file> and sudo chmod 0644 <file>. |
… has no user= line, so Supervisor runs Horizon as root. or … sets user=root, not apache. | Re-run the deploy. Never set user=root. |
More than one Supervisor config runs this install's Horizon: …. Supervisor may start a second Horizon. | Keep the one the deploy writes (horizon_supervisor.ini on RHEL, horizon_supervisor.conf on Ubuntu). Move the others out of the folder, then run sudo supervisorctl reread && sudo supervisorctl update. Before you move a file, check that its command= line runs this install’s artisan. |
To fix it by hand, keep one regular file (not a symlink) with user=apache, owned by root with mode 0644:
sudo ls -l /etc/supervisord.d/sudo cat /etc/supervisord.d/horizon_supervisor.inisudo chown root:root /etc/supervisord.d/horizon_supervisor.inisudo chmod 0644 /etc/supervisord.d/horizon_supervisor.inisudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl restart horizonOn Ubuntu the folder is /etc/supervisor/conf.d/ and Supervisor only loads files ending in .conf.
Scheduler crontab
Section titled “Scheduler crontab”The scheduler runs from the web user’s own crontab, never from root’s.
| Message | Fix |
|---|---|
apache's crontab runs this install's scheduler. (OK) | Nothing to do. |
apache's crontab has no line that runs this install's scheduler. | Re-run the deploy, or add the line the check prints with sudo crontab -u apache -e. |
apache's crontab could not be read: … (WARN) | Usually crontab is not allowed for the web user. Check /etc/cron.allow and /etc/cron.deny. |
Not running as root or apache, so apache's crontab was not read. (SKIP) | Run the check with sudo. |
The line looks like this, with www-data and the path the check prints on your server:
sudo crontab -u apache -l* * * * * cd /var/www/html/rconfig8/current && php artisan schedule:run >> /dev/null 2>&1Root crontab
Section titled “Root crontab”| Message | Fix |
|---|---|
Root's crontab does not run this install's scheduler. (OK) | Nothing to do. |
Root's crontab still runs this install's scheduler as root: … | Re-run the deploy, which removes the line. To do it by hand, add the web user’s line first (see Scheduler crontab), then delete only this install’s schedule:run line with sudo crontab -u root -e. |
The scheduler ran as root in the last 15 minutes, but root's crontab has no line for it. (WARN) | Something else ran schedule:run or health:check as root. Look with sudo grep -rn "schedule:run" /etc/crontab /etc/cron.d/ /var/spool/cron/. If you ran either command by hand as root, that is the cause: the warning clears 15 minutes after the last root run. |
The scheduler ran as root in the last 15 minutes. Root's crontab was not read, … (WARN) | The same marker, seen without sudo. Run the check with sudo. If the scheduler has just moved off root, it clears on its own. |
Not running as root, so root's crontab was not read. (SKIP) | Run the check with sudo to include it. The dashboard runs as the web user, so it shows SKIP here unless the scheduler itself runs as root. |
A line in both crontabs runs the scheduler twice, once as root, so remove the root one even when the web user’s line is in place.
SNMP trap receiver
Section titled “SNMP trap receiver”From V8.4.1 the receiver runs as the systemd service rconfig-snmptrap as the web user. See SNMP trap receiver service for the full set-up.
| Message | Fix |
|---|---|
/etc/systemd/system/rconfig-snmptrap.service is root:root 0644 and runs the receiver as apache (PID …). (OK) | Nothing to do. |
… is root:root 0644 with User=apache. The receiver is installed, not running. (OK) | Start it with sudo php artisan rconfig:snmp-trap-service restart. |
The SNMP trap receiver is not installed. (SKIP) | Nothing to do unless you use SNMP traps. |
Old Supervisor config … still runs the SNMP trap receiver for this install. It has not been migrated to systemd. | Run sudo php artisan rconfig:snmp-trap-service install from the rConfig folder. It moves the settings over, removes the Supervisor program and starts the service. |
The SNMP trap receiver is not installed as a service, but this install's receiver runs as root (PID …). It must run as apache. | Someone started the receiver by hand. Stop that process (sudo kill <PID>), then run sudo php artisan rconfig:snmp-trap-service install. |
Any problem with the unit file’s owner, mode or User= line, or The receiver runs as root (PID …). | Run sudo php artisan rconfig:snmp-trap-service install. It rewrites the unit as a root-owned 0644 file with User=apache. |
… belongs to another rConfig install on this server, so it was not checked. (SKIP) | Only one rConfig on a server can run the receiver. Nothing to do here. |
How do I check from scripts or monitoring?
Section titled “How do I check from scripts or monitoring?”sudo php artisan rconfig:check-worker-user --jsonThe JSON holds state (all_good, still_root, mixed, not_set_up, misconfigured or docker), summary, status (ok, warn, fail or skipped), web_user, checked_as_root, snmp (systemd, supervisor, other_install or not_installed), one entry per check with its key, status, message and fix, and fix, the list of fixes for every FAIL. The exit code is 1 only when a check fails. WARN, SKIP and Docker exit 0.
What’s next
Section titled “What’s next”- Redis Authentication, the other security check on the dashboard health panel
- Horizon Queue Manager for managing the queue once it runs as the web user