• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Setting up an Isolated Multi-Tenant PHP-FPM and Nginx Environment on Ubuntu 24.04 LTS using Systemd Cgroups limits

Setting up an Isolated Multi-Tenant PHP-FPM and Nginx Environment on Ubuntu 24.04 LTS using Systemd Cgroups limits

Prerequisites and Initial Setup

This guide assumes a fresh Ubuntu 24.04 LTS installation with root or sudo privileges. We’ll be setting up a robust, isolated multi-tenant environment for PHP applications served by Nginx and managed by PHP-FPM. The core of our isolation strategy will leverage Systemd’s control group (cgroup) capabilities to enforce resource limits per tenant.

First, ensure your system is up-to-date:

sudo apt update && sudo apt upgrade -y

Install Nginx, PHP-FPM (we’ll use PHP 8.3 as an example), and necessary PHP extensions. For multi-tenancy, we’ll also install php8.3-fpm and potentially other modules required by your applications.

sudo apt install -y nginx php8.3-fpm php8.3-mysql php8.3-mbstring php8.3-xml php8.3-zip

Verify the installations:

systemctl status nginx
systemctl status php8.3-fpm

Tenant Isolation Strategy: Systemd Cgroups

Systemd, through its integration with cgroups, provides a powerful mechanism for resource control. We will create separate Systemd service units for each tenant’s PHP-FPM pool. This allows us to define CPU, memory, and I/O limits granularly for each tenant, preventing resource contention and ensuring fair usage.

Our approach involves:

  • Creating dedicated PHP-FPM pool configurations for each tenant.
  • Defining custom Systemd service units that manage these tenant-specific PHP-FPM pools.
  • Configuring Nginx to route requests to the appropriate tenant’s PHP-FPM pool based on domain or subdomain.

Tenant-Specific PHP-FPM Pool Configuration

For each tenant, we’ll create a distinct PHP-FPM pool. This ensures that processes for one tenant do not interfere with another. Let’s assume our first tenant is `tenant1.example.com`.

First, locate the PHP-FPM pool configuration directory. On Ubuntu 24.04, this is typically `/etc/php/8.3/fpm/pool.d/`.

Create a new pool configuration file for `tenant1`:

[tenant1]
; User and group for the pool
user = tenant1
group = tenant1

; Listen on a Unix socket for better performance and security
listen = /run/php/php8.3-fpm-tenant1.sock

; Set process manager settings
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.process_idle_timeout = 10s
pm.max_requests = 500

; Resource limits (example values, adjust as needed)
; These are hints for PHP-FPM, but Systemd cgroups will enforce them.
; env[HOSTNAME] = $HOSTNAME
; env[UNIQUE_ID] = $UNIQUE_ID

; Error logging
error_log = /var/log/php/php8.3-fpm-tenant1.log
; Log level
log_level = notice

; Security settings
; php_admin_value[disable_functions] = exec,passthru,shell_exec,system
; php_admin_flag[enable_dl] = off

Next, create the user and group for this tenant. This user will own the process and its associated files, enhancing security.

sudo groupadd tenant1
sudo useradd -g tenant1 -s /sbin/nologin tenant1

Create the log directory and set appropriate permissions:

sudo mkdir -p /var/log/php
sudo chown root:root /var/log/php
sudo chmod 755 /var/log/php

Restart PHP-FPM to load the new pool configuration. Note that we are not yet using a custom Systemd service, so this restarts the main service.

sudo systemctl restart php8.3-fpm

Custom Systemd Service Units with Cgroup Limits

To isolate tenants and enforce resource limits, we’ll create custom Systemd service files. This allows us to define cgroup parameters directly within the service definition.

Create a Systemd service file for `tenant1`’s PHP-FPM pool. The standard PHP-FPM service is typically named `php8.3-fpm.service`. We’ll create a drop-in configuration or a completely new service. For better management, a new service is often preferred.

Create the service file:

[Unit]
Description=PHP-FPM FPM application server for tenant1
After=network.target

[Service]
; User and group for the process
User=tenant1
Group=tenant1

; Specify the configuration file for this pool
ExecStart=/usr/sbin/php-fpm8.3 --fpm-config /etc/php/8.3/fpm/pool.d/tenant1.conf

; Cgroup v2 limits (Ubuntu 24.04 uses cgroup v2 by default)
; CPU shares: 1024 is default. 512 means 50% of a single core if no other limits are set.
CPUWeight=512
; Memory limit: 256MB. Use 'M' for megabytes, 'G' for gigabytes.
MemoryMax=256M
; IO weight (optional, for disk I/O)
; IOWeight=100

; Restart policy
Restart=on-failure
RestartSec=5s

; Standard output/error redirection
StandardOutput=journal
StandardError=journal

; Security hardening
ProtectSystem=full
PrivateTmp=true
NoNewPrivileges=true
CapabilityBoundingSet=CAP_SETGID CAP_SETUID CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

Save this file as `/etc/systemd/system/php-fpm-tenant1.service`. Now, enable and start this new service:

sudo systemctl daemon-reload
sudo systemctl enable php-fpm-tenant1.service
sudo systemctl start php-fpm-tenant1.service

Verify the service status and check its cgroup limits:

systemctl status php-fpm-tenant1.service
systemctl show php-fpm-tenant1.service | grep -E 'CPUWeight|MemoryMax'

You should see the configured `CPUWeight` and `MemoryMax` values. Repeat this process for each tenant, creating a unique `.conf` file in `/etc/php/8.3/fpm/pool.d/` and a corresponding `.service` file in `/etc/systemd/system/`.

Nginx Configuration for Multi-Tenant Routing

Nginx needs to be configured to route incoming requests to the correct PHP-FPM pool based on the requested domain or subdomain. We’ll use the `fastcgi_pass` directive to specify the Unix socket for each tenant’s pool.

Create an Nginx server block configuration for `tenant1.example.com`:

server {
    listen 80;
    server_name tenant1.example.com;
    root /var/www/tenant1/public_html; # Adjust to your tenant's web root
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # Pass to the specific tenant's PHP-FPM socket
        fastcgi_pass unix:/run/php/php8.3-fpm-tenant1.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    location ~ /\.ht {
        deny all;
    }

    access_log /var/log/nginx/tenant1.access.log;
    error_log /var/log/nginx/tenant1.error.log;
}

Ensure the web root directory exists and has the correct ownership:

sudo mkdir -p /var/www/tenant1/public_html
sudo chown -R tenant1:tenant1 /var/www/tenant1
sudo chmod -R 755 /var/www/tenant1/public_html

Enable the Nginx site and test the configuration:

sudo ln -s /etc/nginx/sites-available/tenant1.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

For each additional tenant, create a new server block in Nginx, pointing to their respective PHP-FPM socket (e.g., `php8.3-fpm-tenant2.sock`) and web root.

Advanced Considerations and Best Practices

Resource Monitoring: Regularly monitor resource usage per tenant using tools like systemd-cgtop or by examining cgroup filesystem entries under `/sys/fs/cgroup/`. This helps in tuning the `CPUWeight` and `MemoryMax` limits.

sudo systemd-cgtop

PHP-FPM Process Manager: While `dynamic` is common, consider `ondemand` for very low-traffic tenants to save resources, or `static` for high-traffic, predictable workloads. Adjust `pm.max_children` carefully based on server memory and expected load.

Security: Ensure that the `user` and `group` specified in the PHP-FPM pools and Systemd services are unique per tenant and have minimal privileges. Avoid running PHP-FPM pools as `root`. The `NoNewPrivileges=true` and `CapabilityBoundingSet` directives in Systemd are crucial for security.

PHP Configuration Isolation: For more granular control over PHP settings (e.g., `memory_limit`, `upload_max_filesize`) per tenant, you can create tenant-specific `php.ini` files and point to them via `php_admin_value` in the pool configuration, or by using `PHP_INI_SCAN_DIR` environment variables in the Systemd service. However, for strict isolation, managing these via separate PHP-FPM pools is cleaner.

Systemd Cgroup v1 vs v2: Ubuntu 24.04 defaults to cgroup v2. The directives used (`CPUWeight`, `MemoryMax`) are compatible with v2. If you were on an older system using v1, you might use `CPUQuota`, `CPUShares`, `MemoryLimit` respectively.

Scalability: For a large number of tenants, consider automating the creation of PHP-FPM pools, Systemd services, and Nginx configurations using scripting (Bash, Python) or configuration management tools like Ansible or Puppet.

Error Handling: Centralize Nginx and PHP-FPM logs. Use dedicated log files per tenant as shown, and consider a log aggregation system (e.g., ELK stack, Graylog) for easier analysis.

Conclusion

By combining dedicated PHP-FPM pools, custom Systemd service units with cgroup resource controls, and precise Nginx routing, you can establish a highly isolated and resource-managed multi-tenant environment on Ubuntu 24.04 LTS. This architecture provides the necessary separation and control for hosting multiple PHP applications securely and efficiently.

Reader Interactions

Leave a Reply Cancel reply

You must be logged in to post a comment.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store
  • How to refactor legacy event ticket registers queries using modern WP_Query and custom Transient caching
  • Step-by-Step Guide: Offloading high-frequency member profile directories metadata writes to a Redis KV store

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (662)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (873)
  • PHP (5)
  • PHP Development (49)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (647)
  • SEO & Growth (492)
  • Server (118)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (726)
  • WordPress Theme Development (357)

Recent Posts

  • Debugging Guide: Diagnosing PHP-FPM child process pool exhaustion in multi-site network environments with modern tools
  • Debugging and Resolving complex namespace class loading collisions issues during heavy concurrent database traffic
  • Step-by-Step Guide: Offloading high-frequency customer support tickets metadata writes to a Redis KV store

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (873)
  • WordPress Plugin Development (726)
  • Debugging & Troubleshooting (662)
  • Security & Compliance (647)
  • SEO & Growth (492)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala