Handing a shared-hosting client a normal shell lets them cd /home, list every other account, and read anything world-readable across the server. Jailed shell — jailshell — chroots each user into their own home directory so ls / shows a skeleton filesystem, not your other customers' sites.
This covers enabling jailshell per-account and by default, how the VirtFS backing store works and why /home/virtfs quietly eats mount points, how to clean up stale mounts, and the one interaction that catches every host running CloudLinux: installing CageFS silently reverts jailshell users to plain bash.
Enable jailed shell for one account
Go to WHM → Account Functions → Manage Shell Access. Each account gets three options:
- Normal Shell — full
/bin/bash, can traverse the whole filesystem. Only give this to accounts you operate yourself. - Jailed Shell — chrooted
jailshell. SFTP and SSH both work, but the user is confined to their home. - Disabled Shell — no interactive login at all. SFTP still works via the internal SFTP subsystem, so pick this if the client only moves files.
Selecting Jailed Shell sets the account's login shell to /usr/local/cpanel/bin/jailshell in /etc/passwd. Confirm from the command line:
grep bob /etc/passwd
# bob:x:1024:1030::/home/bob:/usr/local/cpanel/bin/jailshell
You can flip it non-interactively with the API, which is handy inside provisioning hooks:
whmapi1 modifyacct user=bob shell=/usr/local/cpanel/bin/jailshell
Make jailshell the default for new accounts
Setting shells one account at a time drifts the moment someone creates a package outside your runbook. Set the server-wide default in WHM → Server Configuration → Tweak Settings → search for jail → enable Use cPanel® jailshell by default. Every new or modified account with shell access then lands in jailshell instead of /bin/bash.
This is one of the Tweak Settings worth changing on a shared box. It does not retroactively re-jail existing normal-shell accounts — audit those separately:
grep -vE 'jailshell|/nologin|/false' /etc/passwd | grep '/home/'
Anything that prints is an account with an unrestricted shell.
How VirtFS works and why /home/virtfs grows
jailshell doesn't use a static chroot. cPanel builds each jail on demand with VirtFS: the first time a jailed user opens an SSH or SFTP session, cPanel creates /home/virtfs/<user>/ and BIND-mounts the system binaries, libraries, and config files the user needs — /bin, /lib64, /etc, and so on — into that per-user tree. The user sees a working Linux environment; you keep isolation.
The cost is mount-table bloat. Each active jailed user can add dozens of bind mounts, and a busy server accumulates thousands:
grep virtfs /proc/mounts | wc -l
On servers with hundreds of shell users this inflates mount output, slows df, and occasionally leaves stale mounts behind after an account is removed or a session dies uncleanly.
Clean up stale VirtFS mounts
To tear down one user's jail safely, use cPanel's script — it unmounts every bind mount for that user before touching the directory:
/usr/local/cpanel/scripts/clean_user_virtfs bob
If mounts are wedged (a process still has a handle open), find and unmount them manually, then re-run the script:
grep '/home/virtfs/bob' /proc/mounts | awk '{print $2}' | sort -r | xargs -r umount
Sorting in reverse unmounts the deepest paths first. Do this during a maintenance window — the user's session dies with the mounts.
The CloudLinux CageFS exception
This is the part hosts trip over. If you run CloudLinux with CageFS, do not use jailshell. When you install or update the CageFS package, it rewrites every jailshell user in /etc/passwd back to /bin/bash:
grep bob /etc/passwd
# bob:x:1024:1030::/home/bob:/bin/bash
That looks like a security regression, but it isn't. CageFS already virtualises the entire filesystem per user at a lower level than VirtFS — a CageFS user in a normal bash shell still can't see other accounts, /etc/passwd is filtered, and only whitelisted binaries are reachable. Running jailshell inside CageFS would double-mount the same isolation and conflict with the CageFS skeleton, so CloudLinux deliberately disables it.
The practical rule:
- CloudLinux box → enable CageFS, leave shells as bash, add any missing tools to the CageFS skeleton. CageFS is the isolation layer.
- Stock cPanel, no CloudLinux → use jailshell + VirtFS as above.
Don't stack both. If you're weighing the upgrade, CageFS also brings per-user PHP versions and symlink-attack protection that jailshell doesn't — see SecureLinks symlink protection. Pricing and tiers are on the CloudLinux license page.
Jail Apache is a separate setting
The Security Advisor's "Jail Apache Virtual Hosts using mod_ruid2" recommendation is unrelated to shell access — it confines the web server's PHP processes, not SSH logins. It's worth enabling on most shared boxes, but on servers with more than ~500 accounts the extra per-vhost mounts can bite performance the same way VirtFS does. Test before rolling it fleet-wide, and pair either approach with account-level suspension hygiene so compromised accounts lose shell access fast.