CloudLinux CageFS: Fix “Command Not Found” Errors in cPanel

· 6 min read · 2 views · Getwebup

You SSH into your cPanel account, type the command for a tool you know is installed on the server, and get -bash: wkhtmltopdf: command not found. You check as root and it's right there in /usr/bin. Nothing was uninstalled, nothing broke overnight. What you're running into is CageFS — CloudLinux's per-account filesystem jail — and it's doing exactly what it was built to do: hide everything your account hasn't been explicitly given access to.

What CageFS Actually Does

On CloudLinux-based servers (most shared and reseller cPanel hosting, and plenty of managed VPS setups), every cPanel account runs inside its own virtualized filesystem called CageFS. Log in as user1 and you see a /usr, /etc, and /bin that look like the real server root — but they're a curated view. CageFS only exposes the binaries, libraries, and config files that CloudLinux has whitelisted for that account, plus whatever's in the user's own home directory.

This is a security feature, not a bug. It stops one compromised or malicious account from reading another user's files, seeing server-wide process lists, or reaching binaries that could be used to escalate privileges. The tradeoff is that anything installed after CageFS was configured, or anything living outside its whitelist, is invisible to every jailed user — even though root can see it fine.

Symptom: What You'll See

  • command not found in SSH or cPanel Terminal for a binary that's confirmed present in /usr/bin or /usr/local/bin as root
  • A cron job that runs perfectly when triggered manually as root, but silently fails (or emails a bounce) when run as the cPanel user
  • PHP's exec(), shell_exec(), or proc_open() returning empty output for a tool that works fine outside the jail
  • A custom-compiled PHP extension that shows up in php -m as root but not when the site's PHP-FPM pool (running as the cPanel user) loads it
  • Tools like wkhtmltopdf, ffmpeg, imagemagick/convert, git, composer, node, or a Python virtualenv binary working in one account and not another on the same server

Cause: Why the Jail Doesn't See It

Two things usually cause this, and it helps to know which one you're dealing with before you start changing anything:

  1. CageFS's internal cache is stale. CageFS doesn't watch the filesystem live — it builds a snapshot of allowed paths and refreshes it on a schedule or when told to. If a binary was installed after the last refresh, jailed accounts won't see it until CageFS is told to rebuild.
  2. The binary or library was never added to CageFS's whitelist at all. Standard package-manager installs (yum/dnf, EasyApache, WHM's own installers) usually register automatically. A binary compiled from source and dropped into /usr/local/bin, a language runtime installed manually (nvm, pyenv, custom Node builds), or a third-party .rpm installed outside CloudLinux's tooling often isn't picked up on its own.

The Fix

All of this needs root access — either your own VPS/dedicated server or via a support ticket if you're on shared/reseller hosting where you don't have root.

1. Force CageFS to refresh its view of the filesystem

cagefsctl --force-update

This rebuilds the CageFS snapshot for every account on the box. It's safe to run any time and is the first thing to try — it fixes the majority of "it works as root but not as the user" cases where the software genuinely is in a standard, already-whitelisted location.

2. Confirm the account is actually jailed the way you think it is

cagefsctl --user-info username

This tells you whether CageFS is enabled for that account and which rule set it's using. Resellers sometimes have accounts CageFS-disabled on purpose; if that's the case here, the "missing" binary problem has a different cause entirely.

3. Add a custom path CageFS doesn't know about

If the binary lives somewhere CageFS never whitelists by default — a custom install directory, a Node version manager's path, a self-compiled tool — you add it as a custom mount rule:

# Create or edit a rules file, e.g.:
vi /etc/cagefs/cagefs.mp.d/custom.mp

# Add the path you need exposed inside every jail, one per line:
/usr/local/mytool

# Then apply it:
cagefsctl --remount-all

Only add paths you trust. Every directory you expose here becomes readable by every CageFS-jailed account on the server, so don't casually whitelist anything containing credentials, other users' data, or write-access to sensitive locations.

4. Rebuild the jail for one specific account

If it's a single account misbehaving rather than the whole server, you don't need to touch everyone else:

cagefsctl --remountuser username

5. Verify from inside the jail, not as root

su - username
which wkhtmltopdf
wkhtmltopdf --version

Testing as root will always "work" because root isn't jailed. The only real test is running the command as the actual cPanel user, ideally the same way your application invokes it — SSH session, cron, and PHP-FPM can each resolve paths slightly differently.

PHP extensions specifically

If a compiled PHP extension (like a custom .so module) isn't loading for a site but shows up under php -m as root, check two things: that the extension file itself sits in a CageFS-visible path, and that the account's PHP version (set via MultiPHP Manager) actually has that extension enabled in its php.ini. CageFS and PHP version selection are separate systems and both have to line up.

Prevention

  • After installing any new system-wide binary, runtime, or library on a CloudLinux server, run cagefsctl --force-update as a standard last step — don't wait for a ticket to remind you.
  • Keep custom software in standard, well-known paths (/usr/local/bin, /opt) rather than scattering installs across the filesystem, so whitelisting stays predictable.
  • If you manage several CloudLinux servers, keep your custom .mp rule files in version control so a fresh server gets the same whitelist without you having to remember every one-off fix from the last two years.
  • When onboarding a new customer app that shells out to a specific tool (PDF generation, video processing, image conversion), test it under the actual cPanel user before go-live, not just as root during setup.

Quick Reference: Useful cagefsctl Commands

CommandWhat it does
cagefsctl --force-updateRebuilds the CageFS filesystem snapshot for all accounts
cagefsctl --remount-allApplies rule/config changes across every jailed account
cagefsctl --remountuser usernameRebuilds the jail for one specific account only
cagefsctl --user-info usernameShows whether CageFS is enabled for an account and its rule set
cagefsctl --list-brokenLists accounts with a broken or inconsistent CageFS mount
cagefsctl --disable-cagefs usernameDisables CageFS for a single account (use sparingly, weakens isolation)

Most CageFS "missing command" tickets resolve with nothing more than cagefsctl --force-update. The custom rule-file route is only needed when the tool genuinely lives outside anywhere CloudLinux already expects software to be — and once it's added, it stays added through future updates.

Questions people actually ask