Bali, Indonesia
Insights

Architecting a CloudLinux Alternative: Low-Level Multi-Tenant Isolation with systemd, cgroups v2, and Podman

October 12, 2026
For over a decade, CloudLinux has been the default choice for shared web hosting providers needing strict multi-tenant resource containment via LVE (Lightweight Virtual Environment) and CageFS. However, licensing costs, proprietary kernel modules, and delayed support for upstream Linux kernel releases force modern infrastructure engineers to seek open-source alternatives. By utilizing modern Linux kernel primitives—specifically cgroups v2, systemd slice units, and Podman—you can architect a high-performance, completely native multi-tenant isolation layer that matches or exceeds proprietary solutions.
In a standard shared hosting environment running PHP-FPM or Node.js via Apache or Nginx, a single runaway script, an unoptimized MySQL query, or a DDoS attack can exhaust system memory or saturate the disk I/O queue. This leads to the 'noisy neighbor' effect, crashing PHP pools or degrading system-wide responsiveness. Traditional Linux resource management relied on cgroups v1, which suffered from hierarchical fragmentation—where CPU, memory, and block I/O controllers operated in separate, uncoordinated trees. CloudLinux solved this by patching the kernel with LVE to enforce limits per user UID. Today, cgroups v2 unifies these hierarchies into a single, cohesive tree, allowing systemd to manage resource allocation cleanly across system slices, user sessions, and containerized runtimes.
To replicate a commercial hosting environment, your alternative architecture must enforce four strict boundaries:
  1. CPU Throttling: Preventing a single tenant from monopolizing CPU cycles while allowing burst capacity.
  2. Memory Hard Limits: Enforcing strict RSS and swap boundaries with immediate OOM termination or throttling rather than cascading kernel panics.
  3. Block I/O Weight and Throttling: Restricting read/write throughput (IOPS and bandwidth) on shared NVMe arrays.
  4. Filesystem Jails: Isolating user directories to prevent horizontal privilege escalation and unauthorized file reads.
Systemd natively interfaces with cgroups v2. We can create custom service slice templates that automatically scope resources for each hosting account based on their system user account.
Ensure your host is running a modern kernel (Linux 5.14+) with cgroups v2 unified hierarchy enabled. Verify your boot parameters in '/etc/default/grub':
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash cgroup_no_v1=all"
Update your bootloader and reboot:
sudo update-grub
sudo reboot
Verify that cgroups v2 is active by inspecting the filesystem type:
stat -fc %T /sys/fs/cgroup/
The output must return 'cgroup2fs'.
Create a systemd template file at '/etc/systemd/system/[email protected]'. This unit file manages the resource constraints for any given system user (e.g., 'client1', 'client2').
[Unit]
Description=Hosting Tenant Isolation Slice for %I
After=network.target

[Service]
Type=simple
User=%i
WorkingDirectory=/home/%i
ExecStart=/bin/sleep infinity
MemoryMax=2G
MemoryHigh=1.5G
CPUQuota=200%
IOWeight=100
IOReadBandwidthMax=/home/%i 50M
IOWriteBandwidthMax=/home/%i 20M
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/%i/public_html /home/%i/logs /tmp /var/tmp

[Install]
WantedBy=multi-user.target
Reload systemd and start an isolated slice for a specific tenant:
sudo systemctl daemon-reload
sudo systemctl enable --now [email protected]
Inspect the live cgroups metrics for the tenant to verify resource quotas are applied at the kernel level:
systemctl status [email protected]
cat /sys/fs/cgroup/system.slice/hosting-user\@client1.service/memory.max
While systemd slices restrict system resources, PHP applications and background daemons still share the primary OS user space unless containerized. Podman provides rootless container execution that integrates seamlessly with systemd, acting as a lightweight alternative to CageFS.
Create a minimal Dockerfile or Containerfile for your hosting tenants, packaging only the required runtime environment (e.g., PHP 8.3 FPM and Composer):
FROM debian:12-slim
RUN apt-get update && apt-get install -y \
    php8.3-fpm \
    php8.3-mysql \
    php8.3-curl \
    php8.3-mbstring \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /var/www/html
EXPOSE 9000
CMD ["php-fpm8.3", "--nodaemonize"]
Build the image as a non-privileged user:
podman build -t hosting-php:8.3 .
Generate a systemd service file for the tenant container using Podman's native generation tool:
podman generate systemd --name client1-php --files --restart-policy=always
Move the generated unit file to your systemd directory, enforcing resource limits directly in the container runtime definition:
[Unit]
Description=Podman container client1-php.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman run \
    --cidfile=%t/%n.ctr-id \
    --cgroups=no-conmon \
    --sdnotify=conmon \
    --replace \
    --name=client1-php \
    --memory=2G \
    --cpus=2.0 \
    -v /home/client1/public_html:/var/www/html:z \
    hosting-php:8.3
ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -t 10
ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all

[Install]
WantedBy=multi-user.target
To validate that your open-source stack handles resource contention as effectively as commercial alternatives, execute load tests using 'wrk' or 'ab' while monitoring kernel metrics.
FeatureCloudLinux (LVE)Native cgroups v2 + Podman
LicensingCommercial (Paid per server)Open Source (Free, GPLv2)
Kernel RequirementCustom CloudLinux KernelUpstream Vanilla Linux Kernel (5.14+)
CPU ControlLVE CPU Limits / Faultscgroups v2 CPUQuota / Weight
Filesystem IsolationCageFS (Virtual Filesystem)Bind mounts, Namespaces, AppArmor
OrchestrationcPanel / DirectAdmin integrationAnsible, Terraform, Custom APIs
Monitor real-time CPU and memory usage across all active tenant slices using systemd's built-in accounting tool:
systemd-cgtop
Inspect specific block I/O throttling and latency metrics for a tenant slice:
cat /sys/fs/cgroup/system.slice/hosting-user\@client1.service/io.stat
Monitor out-of-memory (OOM) killer invocations in the kernel ring buffer to identify tenants hitting their memory caps:
dmesg -T | grep -i oom
  1. Swap Annihilation: If 'MemoryMax' is set without configuring 'MemoryHigh', processes will abruptly hit hard limits and trigger the kernel OOM killer without warning. Always set 'MemoryHigh' to 80 percent of 'MemoryMax' to allow application runtimes to catch memory pressure exceptions gracefully.
  2. File Descriptor Exhaustion: Tenants running background workers can exhaust system-wide file descriptors. Ensure your systemd slice configuration includes explicit limits:
    LimitNOFILE=65535
    LimitNPROC=4096
  3. SELinux/AppArmor Contexts: When using rootless containers with shared directory mounts ('-v'), ensure you append ':z' or ':Z' to the volume declaration to prevent permission denied errors caused by strict Mandatory Access Control policies.