On a shared server every account wants a different PHP. One customer runs a legacy shop that breaks on anything newer than 7.4, the next wants 8.4 for a fresh Laravel project, and a third needs imagick that nobody else does. PHP Selector is CloudLinux's answer: a row of PHP builds installed side by side, and a page in the control panel where each user picks their own version, extensions and php.ini values without touching anyone else's site. This post covers how it fits together, what it needs before it works, and how to drive it from the shell.
alt-php: the versions sit next to each other
CloudLinux ships its own PHP builds as alt-php packages, one per version from 5.6 up to 8.5. They install under /opt/alt/php8X/ (the 8.3 binary is /opt/alt/php83/usr/bin/php) and never replace the panel's own PHP, which the selector calls native. Install the whole set with:
yum groupinstall alt-php
yum install alt-php83-imagick alt-php84-imagickThe group brings every version with a standard set of extensions. Extensions are separate packages named alt-phpNN-<extension>, so when a user cannot see imagick or redis in the panel, the usual reason is that the package is not installed for that version, not that the selector is broken.
Native stays the default until you change it. Users who never open the selector page keep running whatever the panel provides, which is exactly what you want on a server you are converting with live sites on it.
What it needs before it works
Two things, and both catch people out.
CageFS must be enabled for the user. PHP Selector works by placing the chosen interpreter and its php.ini inside the user's cage, so a user without CageFS keeps native PHP whatever they select. If the panel shows the page but nothing changes, run cagefsctl --list-enabled and see whether the user is in the list. Enabling it is covered in CageFS: what it does and how to enable it.
The PHP handler must be one the selector can hook. That means lsphp (mod_lsapi), suPHP or fcgid. The classic selector does not work with PHP-FPM on DirectAdmin, and CustomBuild's default is PHP-FPM, so a DirectAdmin server needs:
cd /usr/local/directadmin/custombuild
./build set cloudlinux yes
./build set php1_mode lsphp
./build php n
./build rewrite_confs
cagefsctl --force-updatemod_lsapi is the right handler for a shared server anyway; the reasons are in mod_lsapi: the fastest PHP handler for Apache. The final cagefsctl --force-update copies the rebuilt binaries into the CageFS skeleton, and you should repeat it whenever you add or update an alt-php package.
The admin side: defaults and locked options
In DirectAdmin open Admin Level → Extra Features → CloudLinux Manager → Selector → PHP. cPanel and Plesk have the same screen inside their LVE Manager plugin. Here you set the default version every account starts on, native or a specific alt-php; enable or disable individual versions so 5.6 is only offered when you decide it is; set the default extension list per version; and set php.ini options, marking the ones users may not change. Locking disable_functions and an upper memory_limit is the usual reason to use that last part.
The same defaults from the command line:
cloudlinux-selector get --json --interpreter php
cloudlinux-selector set --interpreter php --default-version 8.3Keep the default on a version that is still receiving upstream security fixes. The old versions exist for the customers who need them, not as the starting point for new accounts.
The user side: Select PHP Version
A DirectAdmin user opens Advanced Features → Select PHP Version (in cPanel it is under Software). The version dropdown is at the top; choose one and click Set as current. Below it, the Extensions tab is a grid of checkboxes for that version, and the Options tab holds the php.ini values a user is allowed to change: memory_limit, upload_max_filesize, post_max_size, max_execution_time, display_errors, date.timezone and so on.
Changes apply within seconds and only to that account. The settings live in a per-user php.ini inside the cage, so a site that breaks after a change is fixed by switching back on the same page. There is nothing to restart.
One detail worth telling customers: inside a caged shell or cron job, the php command is the selected version. A cron line such as php /home/bob/domains/example.com/public_html/cron.php therefore matches the website, which is not true of a plain DirectAdmin server where you have to spell out /usr/local/php83/bin/php. The full path /opt/alt/php83/usr/bin/php still works if a script must pin a version regardless of what the user selects.
Doing it from the shell
Support work goes faster with the CLI, and it is scriptable:
cloudlinux-selector set --interpreter php --current-version 8.3 --user bob
cloudlinux-selector enable-extensions --interpreter php --version 8.3 --user bob --extensions imagick,redis
cloudlinux-selector set --interpreter php --version 8.3 --user bob --options 'memory_limit=512M,upload_max_filesize=64M'
cloudlinux-selector get --json --interpreter php --user bobThe last command prints the user's selected version, enabled extensions and options as JSON. Loop it over your users when you need to know who is still on 7.4 before an end-of-life date.
Native or alt: where to put people
Leave native as the panel's current build and keep it as the default for accounts that never ask. Enable alt-php 8.2 to 8.5 so anyone can move forward on their own. Keep 7.4, 8.0 and 8.1 enabled only because CloudLinux's HardenedPHP backports security fixes to them, which makes them a safer bridge than the upstream builds; how far that protection goes is in HardenedPHP: running old PHP versions without the security debt. Disable anything older unless a named customer depends on it.
If a user reports a missing extension after switching, it is nearly always the extension checkbox for the new version, since the list is per version. If the site ignores the selection entirely, check CageFS and the handler in that order. On a VPSPioneer managed VPS running CloudLinux we set the handler, CageFS and the default version up as part of the service, so the selector page simply works when your customers open it.