Tutorial

How Panelica Isolates Every User: 5-Layer PHP Security Architecture Explained

April 01, 2026

Back to Blog

One compromised PHP script shouldn't take down your entire server. On most hosting panels, it can. Here's how Panelica makes that impossible — using five layers of kernel-level isolation that wrap every single user, on every single plan.

Why Isolation Matters More Than You Think

Shared hosting has a fundamental problem: multiple users, multiple applications, one machine. When isolation fails, one bad actor — or one vulnerable plugin — can compromise everyone on that server.

This isn't theoretical. In October 2024, CyberPanel CVE-2024-51567 was exploited in the wild. A single unauthenticated command injection vulnerability allowed attackers to compromise the entire host. The PSAUX ransomware campaign infected over 22,000 servers in days. On each of those servers, every customer's data was accessible because there was no meaningful isolation between users.

Most panels respond to this with basic Unix file permissions and hope. If you're running cPanel without CloudLinux, HestiaCP, ISPConfig, or CyberPanel — your users are sharing a PHP process pool, possibly a single PHP-FPM socket, and have visibility into each other's process trees.

Panelica was built differently. Five layers of kernel-enforced isolation, active on every server, for every user, at no extra cost.

In this post, we'll walk through each layer: what it does, why it exists, and exactly what it protects against.


The Big Picture: Request Flow Through All 5 Layers

Before diving into each layer, here's how a web request travels through Panelica's security stack — and where isolation is enforced at each step.

Internet → Browser Request (HTTPS :443)
Layer 0 — Network Perimeter
WAF + Firewall + Intrusion Prevention
nftables • ModSecurity + OWASP CRS • Fail2ban • IP Blocklist
↓ PASS
Layer 1 — Web Server
Nginx Request Router
Static files → Serve directly
PHP files → Per-domain socket
Other → Apache proxy :7080
Layer 2 — Socket Routing
Per-Domain Unix Socket
php-{version}-{user}_{domain}.sock • mode 0660 • owner www-data
Each domain gets its own isolated socket. Cross-domain access: impossible.
Layer 3 — PHP Process Isolation
PHP-FPM Per-User Per-Version Pool
Master process (root) → Worker processes (user UID/GID)
Separate open_basedir • separate disable_functions • per pool
Layer 4 — Kernel Resource Limits
Cgroups v2 Enforcement
panelica-user-{username}.slice
cpu.max • memory.max • io.max • pids.max — kernel-enforced, unbypassable
Layer 5 — Namespace + Permissions
Linux Namespaces + Unix Permissions
PID namespace: can't see other users' processes
Mount namespace: private filesystem view (CageFS-equivalent)
SSH chroot jail • home dir 700 • dedicated UID/GID

Layer 0: Network Perimeter — WAF, Firewall, and Intrusion Prevention

Before a request even reaches your application, it passes through Panelica's network perimeter layer. This is the outermost defense, and it's designed to stop known threats before they cost any server resources.

nftables Firewall

Panelica uses nftables — the modern replacement for iptables in Linux kernels 3.13+. Unlike iptables, nftables processes rules in a single pass through a kernel-native virtual machine, reducing overhead significantly on busy servers. The panel manages rules directly: open ports, blocked IPs, country-based blocks, and rate limiting are all configured through the UI and written atomically to nftables.

ModSecurity + OWASP Core Rule Set

ModSecurity runs as a dynamic module in Nginx (the panel proxy layer), inspecting every HTTP request against the OWASP Core Rule Set (CRS). The CRS protects against the full OWASP Top 10: SQL injection, XSS, remote file inclusion, path traversal, and command injection. This is the layer that would have stopped CyberPanel CVE-2024-51567 at the network level.

Panelica manages per-domain ModSecurity rules from the panel — you can whitelist false positives, adjust sensitivity levels, or disable specific rules for specific domains without touching config files.

Fail2ban Integration

Fail2ban monitors log files for repeated failure patterns and automatically adds IP bans via nftables. Panelica configures jails for SSH, Nginx authentication endpoints, WordPress login pages, and the panel's own login. Default ban threshold is 5 failures in 10 minutes; default ban duration is 1 hour — all configurable per-jail from the panel.

What This Means For You:

Malicious traffic is blocked before it reaches your application code. SQL injection attempts, brute-force login attempts, and known attack patterns are stopped at the server edge — not after they've already consumed PHP worker time and database connections.


Layer 1: Nginx Request Router — Smart Traffic Distribution

Every domain hosted on Panelica gets its own Nginx server block, generated automatically when the domain is provisioned. This isn't a wildcard catch-all — each domain has explicitly defined routing rules.

The Decision Tree

When a request arrives for a PHP file, Nginx evaluates the domain's configuration and routes to the correct PHP-FPM socket based on the domain's assigned PHP version and user. Static files (images, CSS, JS) are served directly from disk without touching PHP at all. This alone eliminates a significant attack surface — static files don't execute code.

Nginx-Only vs Nginx+Apache Mode

Panelica supports two web server configurations per domain:

  • Nginx-only mode: Pure Nginx for maximum performance. All URL rewriting handled in Nginx config. Best for Laravel, WordPress (with Nginx rules), and modern PHP apps.
  • Nginx+Apache mode: Nginx handles SSL termination and static files; dynamic requests are reverse-proxied to Apache running on port 7080. This enables full .htaccess support — critical for legacy applications, CodeIgniter, or any app relying on Apache-specific directives.

In both modes, Nginx acts as the front router. Apache never directly faces the internet.

What This Means For You:

Each domain's traffic is independently routed. A misconfiguration in one domain's server block cannot affect another domain. Static assets bypass PHP entirely, reducing attack surface. The routing layer is fully managed — you never edit nginx config manually.


Layer 2: Per-Domain Unix Sockets — The Isolation Gateway

This is where cross-domain isolation begins at the transport level. Every domain gets its own Unix domain socket for communicating with PHP-FPM.

Socket Naming Convention

# Socket path pattern:
/opt/panelica/var/run/php-{version}-{username}_{domain}.sock

# Examples:
/opt/panelica/var/run/php-8.3-alice_example.com.sock
/opt/panelica/var/run/php-8.1-alice_legacy-app.com.sock
/opt/panelica/var/run/php-8.4-bob_newsite.org.sock

Socket permissions are set to 0660 with ownership www-data:www-data. The Nginx worker (running as www-data) can communicate with the socket; no other user can. Even if User B's PHP code attempts to send a request to User A's socket — the kernel rejects it at the file permission level before any data is transmitted.

Per-Domain PHP Version Selection

Because each domain gets its own socket pointing to its own PHP-FPM pool, different domains can run different PHP versions simultaneously. User A can run example.com on PHP 8.4 and legacy-app.com on PHP 8.1, with no interference between them. Changing PHP version for one domain restarts only that domain's FPM pool — zero impact on other domains.

Domain Restart Isolation

Per-domain sockets mean per-domain restartability. When a PHP-FPM pool is restarted (after a config change, after an OOM event, after a crash), only that specific domain's socket is affected. Other users' sites continue serving requests through their own sockets, completely unaware anything happened.

What This Means For You:

Your domains are completely independent at the transport layer. A crashing PHP pool on another user's domain doesn't affect your socket. You can run different PHP versions per domain. Domain restarts are surgical — other customers don't feel a thing.


Layer 3: PHP-FPM Per-User Per-Version Pools — Process-Level Isolation

This is the layer where most competing panels cut corners. Panelica runs a dedicated PHP-FPM pool per user per PHP version — not a shared global pool, not a per-site pool that reuses a global config. A fully independent FPM service, isolated by kernel boundaries.

Systemd Service Per User Per Version

Each user + PHP version combination gets a dedicated systemd service unit:

# Service unit naming:
panelica-php-fpm-{version}-user@{username}.service

# Examples:
[email protected]
[email protected]   # Alice's legacy PHP
[email protected]

# Pool config location:
/opt/panelica/etc/php-fpm-users/{username}/{version}/pool.d/pool.conf

The master PHP-FPM process starts as root (required to bind sockets with correct permissions), then immediately drops privileges: worker processes run as the user's actual UID and GID. This means every PHP process executing Alice's code runs as Alice — not as www-data, not as nobody, not as a shared service account.

open_basedir Enforcement

Each pool has its own open_basedir configuration, pointing exclusively to that user's home directory. When Alice's PHP code attempts to open a file outside /home/alice/, the kernel rejects the open() syscall — not because of a PHP check, not because of Nginx rules, but because the filesystem ACL says no.

# In Alice's pool.conf:
php_admin_value[open_basedir] = /home/alice/:/tmp/alice/:/opt/panelica/services/php/8.3/

# Bob cannot access /home/alice/ — his open_basedir points to /home/bob/
php_admin_value[open_basedir] = /home/bob/:/tmp/bob/:/opt/panelica/services/php/8.3/

disable_functions Per Pool

Dangerous PHP functions (shell execution, process spawning, system calls) can be disabled per pool. The panel provides a security profile per user — relaxed for developers who need exec(), strict for untrusted users, locked-down for shared hosting customers.

ondemand Process Manager

Pools use PHP-FPM's ondemand process manager: worker processes spawn when a request arrives and are killed after a configurable idle timeout. This means idle users consume zero CPU and minimal memory — the pool exists in configuration but costs nothing until a request is served.

Why This Matters vs. Other Panels:

On CyberPanel, HestiaCP (default config), and ISPConfig — all users' PHP code runs through a shared global PHP-FPM pool. There's no per-user process isolation. A shell script uploaded by User B, if executed via PHP, can traverse to User A's directory if file permissions are misconfigured anywhere. Panelica's per-user per-version pools make this class of attack impossible by design.

What This Means For You:

Your PHP code runs in its own process, as your own Linux user, with its own filesystem boundaries. Other users' PHP code cannot reach your files. You choose your PHP version independently. If your application exhausts PHP workers, only your pool is affected — other users continue serving requests normally.


Layer 4: Cgroups v2 — Kernel-Enforced Resource Limits

Cgroups (Control Groups) version 2 is a Linux kernel feature that enforces resource quotas at the process level. There is no way for a process to exceed a cgroup limit from userspace — the kernel enforces it unconditionally.

Per-User Systemd Slice

Every Panelica user gets a dedicated systemd slice:

# Slice path in cgroup hierarchy:
/sys/fs/cgroup/panelica.slice/panelica-user.slice/panelica-user-{username}.slice/

# Controllers active:
cpu     → cpu.max (e.g., "100000 100000" = 1 core max)
memory  → memory.max (e.g., "536870912" = 512MB hard limit)
io      → io.max (disk read/write bytes per second)
pids    → pids.max (maximum process count — fork bomb protection)

All of Alice's processes — PHP-FPM workers, SSH sessions, FTP connections, Docker containers — are placed under Alice's slice. The kernel accounts for the total across all of them. If Alice's crypto miner spawns 100 forked processes, they all count toward her PID limit and CPU quota.

What "Kernel-Enforced" Actually Means

When a process in Alice's slice attempts to use more CPU than allowed, the kernel's CFS scheduler throttles it. There's no user-mode agent, no polling check, no grace period. The throttling happens at the scheduler level, microsecond by microsecond. Alice can run a while(true) {} loop and it will never steal CPU from Bob because the kernel distributes CPU time according to cgroup weights before any scheduler tick is assigned.

Memory limits work via OOM (Out Of Memory) killer integration. When a process in Alice's cgroup exceeds memory.max, the kernel sends SIGKILL to the largest process in that cgroup — Alice's runaway PHP script dies, not Bob's database server.

Watcher Daemons

Panelica runs three dedicated watcher daemons that ensure new processes are always placed in the correct cgroup:

  • ssh_cgroup_watcher: Monitors new SSH connections and assigns them to the connecting user's slice
  • phpfpm_cgroup_watcher: Assigns newly spawned PHP-FPM worker processes to their user's slice
  • ftp_cgroup_watcher: Handles ProFTPD session processes
What This Means For You:

Guaranteed resources. If a neighbor's application is hammering the CPU, your PHP processes still get their allocated share — the kernel guarantees it. A runaway script, a crypto miner, a fork bomb — none of these can starve other users because the kernel enforces limits before any process gets to run. This is something money can't buy on cPanel without a CloudLinux license.


Layer 5: Linux Namespaces + Unix Permissions — Complete Invisibility

The final layer separates what users can see — not just what they can access. Even with all previous layers in place, a user could potentially run ps aux and see other users' process names and arguments. Namespaces eliminate even that.

PID Namespace Isolation

Each Panelica user operates in their own PID namespace. When Alice runs ps aux in her shell session, she sees only her own processes — PHP-FPM workers, her SSH session, any cron jobs she's running. Bob's processes are literally invisible in her namespace. This is not access control — it's namespace-level invisibility enforced by the kernel's clone(CLONE_NEWPID) call.

This matters because process listing reveals information: running application names, port bindings in arguments, database credentials passed via command line. In a shared environment without PID namespaces, any user can enumerate the entire server's process tree.

Mount Namespace Isolation

Mount namespaces give each user a private view of the filesystem. Panelica implements a CageFS-equivalent approach: each user's namespace includes their home directory, required system libraries, and PHP binaries — but not other users' home directories, not /proc entries for other users' processes, not sensitive system paths.

This is implemented natively via Linux namespaces (clone(CLONE_NEWNS)) — no kernel patches, no third-party modules. Pure upstream Linux.

SSH Chroot Jail

Two SSH access modes, both chroot-based:

  • sshjailed mode: SFTP access only, no shell. The user's view of the filesystem is limited to their home directory. Standard sftp commands work; shell execution is impossible.
  • sshfull mode: Full bash shell, but inside a chroot environment. Users get a working shell but cannot navigate above their chroot root. System directories, other users' homes, sensitive configuration — all invisible.

Unix Permissions Foundation

Every user gets a dedicated UID and GID on the Linux system. Home directories are created with permission 700 (owner only). All files written by the panel on behalf of a user — web files, config files, database dumps — are written using the user's UID via sudo with a whitelist-based sudoers configuration. The panel never writes files as root into user home directories.

What This Means For You:

Other users are invisible to you, and you are invisible to them. You cannot see their processes, their files, or their running applications. Your home directory is private at the kernel level — not just by convention. SSH access is scoped to exactly what you need, nothing more.


Real-World Scenario: What Happens When User A Gets Hacked?

🔍 Incident: WordPress Plugin Exploit → PHP Shell Upload

Setup: Server has 20 users. Alice runs a WordPress site with a vulnerable contact form plugin. Bob, Carol, and 17 others run unrelated PHP applications.

The attack: An attacker exploits a remote file upload vulnerability in Alice's plugin and uploads a PHP webshell to Alice's WordPress uploads directory. They access it via the browser and have a PHP command execution environment.


On a panel without isolation (e.g., CyberPanel without hardening):

  • PHP shell runs as www-data (shared by all users)
  • Attacker navigates to /home/bob/ — readable
  • Steals Bob's database credentials from wp-config.php
  • Reads all users' email account passwords from config files
  • Launches crypto miner — consumes 100% CPU, all 20 users' sites slow to a crawl
  • Reads SSH private keys from other users' .ssh/ directories
  • Pivots to root via kernel exploit or misconfigured sudo
  • Entire server compromised. All 20 customers affected.

On Panelica:

  • PHP shell runs as Alice's UID (e.g., uid=1001) — not www-data
  • Attacker tries file_get_contents('/home/bob/public_html/wp-config.php')Permission denied (open_basedir: Layer 3)
  • Attacker tries exec('ls /home/') → sees only /home/alice/, other directories empty in mount namespace (Layer 5)
  • Attacker spawns a crypto miner → CPU throttled to Alice's cgroup quota (Layer 4), Bob's site unaffected
  • Attacker tries to fork-bomb the server → hits pids.max, process creation fails (Layer 4)
  • Attacker runs ps aux → sees only Alice's processes, PID namespace is isolated (Layer 5)
  • Attacker tries to read /etc/passwd via shell → chroot jail prevents filesystem traversal (Layer 5)
  • ModSecurity logged the initial upload attempt as suspicious (Layer 0)
  • Fail2ban is watching for repeated exploit attempts (Layer 0)
  • Damage is contained entirely within Alice's account. Bob, Carol, and all 17 others: unaffected.

This is the practical difference between "security features" and "security architecture." Panelica's layers work together — each one limits a different attack vector, and together they make lateral movement between users effectively impossible.


How Other Panels Compare

Most panels have some isolation — the question is how much, and whether you need to pay extra for it.

Isolation Layer cPanel/WHM HestiaCP CyberPanel ISPConfig Panelica
Cgroups v2 CloudLinux only (paid) No No No Yes — all plans
PID/Mount Namespaces CloudLinux only (paid) No No No Yes — all plans
SSH Chroot Jail CageFS (paid add-on) Basic No Manual config Yes — all plans
Per-User PHP-FPM Yes (EasyApache, paid) Basic No Basic Per-user + per-version + per-domain
Per-Domain Socket No No No No Yes — per domain
WAF (ModSecurity) Paid add-on No Limited Manual Panel-managed — all plans
Kernel Resource Limits CloudLinux only (paid) No No No Kernel-enforced — all plans

The important caveat on cPanel: the features marked "CloudLinux only" work very well — when you're running CloudLinux. But CloudLinux is a separate operating system license, billed per-server per-month on top of your cPanel license. On a typical shared hosting server, you're looking at $40-60/month in licensing before you've paid for the hardware. Panelica includes equivalent isolation (and in some cases deeper) as part of the standard license.


Technical Specifications

Supported PHP Versions

PHP 8.1, 8.2, 8.3, 8.4, 8.5 — all versions can run simultaneously on the same server. Each user can have different PHP versions per domain. All versions have dedicated per-user per-version PHP-FPM service units.

Cgroup v2 Controllers Active
  • cpu — CPU time allocation via cpu.max (hard throttle)
  • memory — Memory usage via memory.max (OOM on breach)
  • io — Disk I/O bandwidth via io.max (read/write bytes per second)
  • pids — Process count via pids.max (fork bomb protection)
Linux Namespace Types
  • PID namespace (CLONE_NEWPID) — Process isolation. Users see only their own processes.
  • Mount namespace (CLONE_NEWNS) — Filesystem isolation. CageFS-equivalent without kernel patches.
PHP-FPM Configuration Details
  • Process manager: ondemand (spawn on request, idle cleanup)
  • Socket naming: php-{version}-{username}_{domain}.sock
  • Socket permissions: 0660, owner www-data:www-data
  • Worker UID/GID: user's actual system UID and GID
  • Config path: /opt/panelica/etc/php-fpm-users/{user}/{version}/pool.d/
  • Systemd service: panelica-php-fpm-{version}-user@{username}.service
Watcher Daemons
  • ssh_cgroup_watcher — Assigns SSH sessions to user cgroup slices on connect
  • phpfpm_cgroup_watcher — Assigns PHP-FPM workers to cgroup slices on spawn
  • ftp_cgroup_watcher — Assigns FTP sessions to user cgroup slices on connect

Five Layers. Zero Compromises. Every Plan.

Most panels treat security isolation as a premium feature — something you unlock by paying more or by switching to a different operating system. CloudLinux exists precisely because cPanel's base installation lacks the kernel-level isolation that production shared hosting requires.

Panelica's position is different: kernel-level isolation is not a feature you should have to pay extra for. It's the correct architecture for any server with multiple users. We built the isolation layer first, then built the panel around it.

The five layers work together and they work independently. Each layer stops a different class of attack. Together, they contain a compromise to the user it started with — leaving every other user on your server unaffected.

One compromised PHP script shouldn't take down your entire server. With Panelica, it won't.

See It Running on Your Server

Install Panelica in under 3 minutes. All five isolation layers active from the first user created. No CloudLinux license required. No premium add-ons.

Share: