Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Php Fpm Ubuntu
GUIDEInstall Guides

How to Install PHP 8.3 + PHP-FPM on Ubuntu 24.04 VPS

21 min read

How to Install PHP 8.3 + PHP-FPM on Ubuntu 24.04 VPS

PHP 8.3 ships with Ubuntu 24.04 "Noble" out of the box, but the version in the default repositories is frozen for the lifetime of the LTS release. If you want the latest point release, multiple PHP versions side by side, or fresh builds of common extensions like redis, imagick, and apcu, you need a third-party repository. The community standard is the Ondrej Surý PPA, maintained by a Debian PHP packager and trusted by millions of production servers. This guide walks you through installing PHP 8.3 + PHP-FPM on a clean Ubuntu 24.04 VPS, from PPA setup to a hardened, OpCache-tuned, Nginx-integrated deployment ready for WordPress, Laravel, Symfony, or any modern PHP application.

Prefer a managed stack? Our CloudCore Starter plan is pre-sized for typical PHP workloads with 4 vCPU, 8 GB RAM, and 100 GB NVMe. Deploy and SSH in under 60 seconds, then follow this guide to get a production-grade PHP-FPM pool running.

Table of Contents

  • What is PHP-FPM?
  • Why Use the Ondrej PPA?
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Add the Ondrej PHP PPA
  • Step 3: Install PHP 8.3 + PHP-FPM + Extensions
  • Step 4: Verify the Installation
  • Step 5: Configure the PHP-FPM Pool
  • Step 6: Tune php.ini for Web Apps
  • Step 7: Enable and Tune OpCache (+ JIT)
  • Step 8: Connect Nginx to PHP-FPM via FastCGI
  • Step 9: Run Multiple PHP Versions Side by Side
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is PHP-FPM?

    PHP-FPM (FastCGI Process Manager) is the modern, production-grade way to run PHP behind a web server. Instead of spawning a PHP interpreter per request (as with legacy mod_php on Apache), PHP-FPM keeps a pool of long-lived worker processes listening on a Unix socket or TCP port. When Nginx receives a request for a .php file, it forwards the request over the FastCGI protocol to one of these workers, which executes the script and returns the response.

    This architecture solves several problems at once. Workers are reused across requests, which eliminates the fork/exec overhead per hit and lets OpCache keep compiled bytecode in shared memory between requests -- the single biggest performance win for PHP applications. Workers run as a dedicated user (typically www-data or a per-site user), which is cleaner to sandbox than embedding PHP in the web server. And because PHP-FPM is decoupled from the HTTP server, you can run multiple pools with different PHP versions, user accounts, and tuning parameters on the same box -- ideal for multi-tenant hosting, staging + production side by side, or migrating legacy apps one at a time.

    PHP-FPM also exposes rich process-management primitives. A single pool can use pm=static (fixed worker count, lowest latency), pm=dynamic (elastic pool with min/max idle workers, best general-purpose choice), or pm=ondemand (workers spawn per request and die when idle, ideal for low-traffic sites). It ships with slow-request logging, per-pool status endpoints, emergency restart thresholds, and chroot support out of the box. Every serious PHP deployment runs it.

    Why Use the Ondrej PPA?

    The default Ubuntu 24.04 repositories include only PHP 8.3 and freeze it at the version that shipped with the LTS release. That means:

    • No point releases -- if PHP 8.3.7 lands upstream with a security fix, you wait for Ubuntu's security team to backport it.
    • No other versions -- you cannot install PHP 8.1, 8.2, or 8.4 alongside 8.3 without a third-party source. This is a hard blocker if you host legacy apps that haven't been certified on 8.3 yet, or if you want to try 8.4 in staging.
    • Fewer extensions -- handy PECL extensions like redis, imagick, apcu, msgpack, and swoole are either outdated or missing entirely.
    The Ondrej Surý PPA (source repo: github.com/oerdnj/deb.sury.org) solves all three. Ondrej Surý is the Debian PHP package maintainer, and the PPA is the same source that powers the official Debian PHP packages -- just rebuilt for Ubuntu. It ships every supported PHP version (7.4 through 8.4 at the time of writing), tracks upstream point releases within days, and packages every common PECL extension as a .deb. Installing redis becomes apt install php8.3-redis instead of a PECL compile.

    It is the de-facto standard for production PHP on Debian and Ubuntu.

    Prerequisites

    Before you begin, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access
    • SSH access to your server
    • At least 2 GB of RAM for a single-site PHP-FPM pool with OpCache (4 GB+ recommended for real-world traffic)
    • At least 10 GB of free disk space for PHP + extensions + application code
    • Nginx already installed (or plan to install it in Step 8)
    Recommended Plan: CloudCore Starter
    >
    For most PHP workloads -- WordPress sites, Laravel apps, Symfony APIs, shared-hosting-replacement boxes -- the CloudCore Starter plan is the sweet spot:
    >
    - 4 vCPU cores
    - 8 GB RAM
    - 100 GB NVMe SSD
    - Unmetered bandwidth
    >
    This gives you enough headroom for an Nginx + PHP-FPM + MariaDB stack with room for OpCache, Redis session storage, and 20-40 concurrent PHP workers before you need to scale up.

    Connect to your server via SSH:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    Always start with a clean package index and applied security updates. This avoids dependency conflicts later when the PPA pulls in newer library versions.

    bash
    sudo apt update && sudo apt upgrade -y

    Expected output (abbreviated):

    text
    Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
    Hit:2 http://archive.ubuntu.com/ubuntu noble-updates InRelease
    Reading package lists... Done
    Building dependency tree... Done
    Calculating upgrade... Done
    0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

    If the kernel was upgraded, reboot before continuing:

    bash
    sudo reboot

    Install the small set of utilities needed to add a PPA:

    bash
    sudo apt install -y software-properties-common ca-certificates lsb-release apt-transport-https gnupg

    Step 2: Add the Ondrej PHP PPA

    Add the PPA and refresh the package index:

    bash
    sudo add-apt-repository ppa:ondrej/php -y
    sudo apt update

    Expected output:

    text
    Repository: 'deb https://ppa.launchpadcontent.net/ondrej/php/ubuntu/ noble main'
    Description: Co-installable PHP versions: PHP 5.6, PHP 7.x and most requested extensions are included.
    ...
    Adding repository.
    Press [ENTER] to continue or Ctrl-c to cancel.
    Adding deb entry to /etc/apt/sources.list.d/ondrej-ubuntu-php-noble.list
    ...
    Hit:5 https://ppa.launchpadcontent.net/ondrej/php/ubuntu noble InRelease
    Reading package lists... Done

    Verify PHP 8.3 is now available from the PPA rather than the stock Ubuntu archive:

    bash
    apt-cache policy php8.3-fpm

    Expected output:

    text
    php8.3-fpm:
      Installed: (none)
      Candidate: 8.3.x-1+ubuntu24.04.1+deb.sury.org+1
      Version table:
         8.3.x-1+ubuntu24.04.1+deb.sury.org+1 500
            500 https://ppa.launchpadcontent.net/ondrej/php/ubuntu noble/main amd64 Packages
         8.3.x-0ubuntu0.24.04.1 500
            500 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 Packages

    The deb.sury.org version should be the candidate -- that confirms the PPA is taking precedence.

    Step 3: Install PHP 8.3 + PHP-FPM + Extensions

    Install PHP-FPM together with the extensions that 95% of real-world PHP apps need:

    bash
    sudo apt install -y \
      php8.3-fpm \
      php8.3-cli \
      php8.3-common \
      php8.3-curl \
      php8.3-gd \
      php8.3-intl \
      php8.3-mbstring \
      php8.3-mysql \
      php8.3-opcache \
      php8.3-readline \
      php8.3-soap \
      php8.3-xml \
      php8.3-zip \
      php8.3-bcmath \
      php8.3-imagick \
      php8.3-redis \
      php8.3-apcu

    What each package gives you:

    • php8.3-fpm -- The FastCGI Process Manager itself. Pulls in php8.3-common.
    • php8.3-cli -- The command-line interpreter. Needed for composer, artisan, wp-cli, and cron jobs.
    • php8.3-curl -- HTTP client (curl_* functions, Guzzle, wp-cron outbound calls).
    • php8.3-gd -- Image manipulation (thumbnails, WordPress uploads).
    • php8.3-intl -- Unicode + internationalization (IntlDateFormatter, Normalizer, required by Symfony/Laravel).
    • php8.3-mbstring -- Multi-byte string functions (UTF-8 handling).
    • php8.3-mysql -- MySQL/MariaDB driver (PDO + mysqli).
    • php8.3-opcache -- Bytecode cache. Huge performance win; always enable.
    • php8.3-readline -- REPL support for php -a.
    • php8.3-soap -- SOAP client/server (still needed for many B2B integrations).
    • php8.3-xml -- XML parsing (SimpleXML, DOMDocument, XMLReader).
    • php8.3-zip -- ZIP archive handling (Composer, WordPress plugin uploads).
    • php8.3-bcmath -- Arbitrary-precision math (payment processing, crypto).
    • php8.3-imagick -- ImageMagick bindings. Higher-quality image processing than GD.
    • php8.3-redis -- Redis client. Pair with session.save_handler=redis.
    • php8.3-apcu -- In-memory user cache (APCu). Useful for per-request caches and as a Doctrine cache backend.
    The installer automatically enables PHP-FPM and starts the systemd service.

    Step 4: Verify the Installation

    Confirm the PHP version:

    bash
    php -v

    Expected output:

    text
    PHP 8.3.x (cli) (built: ... ) (NTS)
    Copyright (c) The PHP Group
    Zend Engine v4.3.x, Copyright (c) Zend Technologies
        with Zend OPcache v8.3.x, Copyright (c), by Zend Technologies

    Check that PHP-FPM is running:

    bash
    sudo systemctl status php8.3-fpm

    Expected output:

    text
    ● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
         Loaded: loaded (/lib/systemd/system/php8.3-fpm.service; enabled; preset: enabled)
         Active: active (running) since Wed 2026-04-16 10:00:00 UTC; 1min ago
       Main PID: 1234 (php-fpm8.3)
         Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
          Tasks: 3 (limit: 9487)
         Memory: 14.3M
            CPU: 180ms
         CGroup: /system.slice/php8.3-fpm.service
                 ├─1234 "php-fpm: master process (/etc/php/8.3/fpm/php-fpm.conf)"
                 ├─1235 "php-fpm: pool www"
                 └─1236 "php-fpm: pool www"

    List the enabled modules:

    bash
    php -m

    You should see curl, gd, intl, mbstring, mysqli, OPcache, redis, imagick, apcu and friends in the output.

    Confirm the default Unix socket exists:

    bash
    ls -l /run/php/

    Expected output:

    text
    srw-rw---- 1 www-data www-data 0 Apr 16 10:00 php8.3-fpm.sock
    -rw-r--r-- 1 root     root     4 Apr 16 10:00 php8.3-fpm.pid

    The socket at /run/php/php8.3-fpm.sock is what Nginx will connect to in Step 8.

    Step 5: Configure the PHP-FPM Pool

    Each PHP-FPM pool is an independent process group with its own user, socket, and tuning parameters. The default pool is defined in /etc/php/8.3/fpm/pool.d/www.conf. Open it:

    bash
    sudo nano /etc/php/8.3/fpm/pool.d/www.conf

    Key directives to review and tune:

    ini
    [www]
    user = www-data
    group = www-data

    listen = /run/php/php8.3-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0660

    pm = dynamic pm.max_children = 20 pm.start_servers = 4 pm.min_spare_servers = 2 pm.max_spare_servers = 6 pm.max_requests = 500

    pm.status_path = /fpm-status ping.path = /fpm-ping

    request_terminate_timeout = 60s request_slowlog_timeout = 5s slowlog = /var/log/php8.3-fpm-slow.log

    catch_workers_output = yes decorate_workers_output = no

    Process Manager Modes

    • pm = dynamic (recommended default) -- PHP-FPM keeps between pm.min_spare_servers and pm.max_spare_servers idle workers ready, up to a hard ceiling of pm.max_children. Elastic + cheap in RAM.
    • pm = static -- A fixed pm.max_children workers are always running. Lowest latency, predictable RAM usage, best for dedicated single-site servers under constant load.
    • pm = ondemand -- Zero workers until a request arrives, then spawn on demand up to pm.max_children. Idle workers are killed after pm.process_idle_timeout. Best for low-traffic servers hosting many pools.

    Calculating pm.max_children

    This is the single most important tuning decision. The formula:

    text
    pm.max_children = (Total RAM - OS/other services) / avg PHP worker memory

    On an 8 GB VPS running Nginx + MariaDB + PHP-FPM:

    • Reserve ~2 GB for OS + Nginx + MariaDB + buffers.
    • Leave ~6 GB for PHP workers.
    • Measure average PHP worker RSS under load. Typical values: WordPress ~80 MB, Laravel ~120 MB, Magento ~200 MB.
    • For WordPress at 80 MB/worker: 6144 / 80 ≈ 76. Set pm.max_children = 60 to leave headroom.
    • For Laravel at 120 MB/worker: 6144 / 120 ≈ 51. Set pm.max_children = 40.
    To measure actual worker memory:

    bash
    ps -ylC php-fpm8.3 --sort:rss | awk '{sum+=$8; n++} END {print "avg RSS KB:", sum/n}'

    Why pm.max_requests = 500?

    PHP workers accumulate memory over time (leaks in extensions, long-lived autoloaders). Recycling each worker after 500 requests keeps memory bounded without visibly hurting performance. Set it lower (100-200) if you see RSS growth in production.

    Reload PHP-FPM after edits:

    bash
    sudo systemctl reload php8.3-fpm

    Step 6: Tune php.ini for Web Apps

    The PHP-FPM php.ini lives at /etc/php/8.3/fpm/php.ini. The CLI has its own at /etc/php/8.3/cli/php.ini -- remember to edit both if you want artisan or wp-cli to behave the same as the web stack.

    Open the FPM config:

    bash
    sudo nano /etc/php/8.3/fpm/php.ini

    Recommended changes for modern web apps:

    ini
    ; --- Resource limits ---
    memory_limit = 256M
    max_execution_time = 60
    max_input_time = 60
    max_input_vars = 3000

    ; --- File uploads --- upload_max_filesize = 64M post_max_size = 64M file_uploads = On max_file_uploads = 20

    ; --- Error handling (production) --- display_errors = Off display_startup_errors = Off log_errors = On error_log = /var/log/php8.3-fpm-error.log

    ; --- Dates --- date.timezone = UTC

    ; --- Sessions on Redis --- session.save_handler = redis session.save_path = "tcp://127.0.0.1:6379?database=0" session.gc_maxlifetime = 86400 session.cookie_httponly = 1 session.cookie_secure = 1 session.cookie_samesite = "Lax"

    ; --- Hide PHP signature --- expose_php = Off

    A few notes:

    • memory_limit -- 256M is a sane default. WordPress + WooCommerce + a heavy plugin load may need 512M. Laravel + Horizon jobs typically stay under 256M.
    • upload_max_filesize and post_max_size -- Must both be raised together. post_max_size should be at least as large as upload_max_filesize.
    • session.save_handler = redis -- Requires the php8.3-redis extension and a running Redis server (sudo apt install redis-server). Moving sessions to Redis eliminates disk I/O and, crucially, lets you scale horizontally across multiple PHP-FPM servers.
    • date.timezone -- PHP emits a warning on every request if this is unset.
    Validate the config before reloading:

    bash
    sudo php-fpm8.3 -t

    Expected output:

    text
    [16-Apr-2026 10:00:00] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful

    Reload:

    bash
    sudo systemctl reload php8.3-fpm

    Step 7: Enable and Tune OpCache (+ JIT)

    OpCache compiles PHP source to bytecode on first request and caches the bytecode in shared memory, so every subsequent request skips the parse/compile step. On a typical WordPress or Laravel request this is a 3-5x speedup. PHP 8.3 additionally supports JIT compilation (tracing mode), which can deliver another 10-20% on CPU-bound code.

    Open the OpCache config:

    bash
    sudo nano /etc/php/8.3/fpm/conf.d/10-opcache.ini

    Recommended production settings:

    ini
    opcache.enable=1
    opcache.enable_cli=0
    opcache.memory_consumption=256
    opcache.interned_strings_buffer=16
    opcache.max_accelerated_files=20000
    opcache.max_wasted_percentage=10
    opcache.validate_timestamps=0
    opcache.revalidate_freq=0
    opcache.save_comments=1
    opcache.fast_shutdown=1

    ; JIT (PHP 8.0+, tracing mode is best for web apps) opcache.jit_buffer_size=128M opcache.jit=tracing

    What each directive does:

    • opcache.enable=1 -- Turn OpCache on.
    • opcache.memory_consumption=256 -- Shared memory (MB) for compiled bytecode. 256 MB fits most apps; Magento or large ERP codebases may need 512 MB.
    • opcache.interned_strings_buffer=16 -- Memory for deduplicated strings. 16 MB is safe; raise to 32 if you hit the limit.
    • opcache.max_accelerated_files=20000 -- Maximum number of PHP files to cache. Count your app: find /var/www/html -type f -name '*.php' | wc -l. Round up to next prime.
    • opcache.validate_timestamps=0 -- Production setting. OpCache never checks if source files changed, giving maximum performance. After a deploy, you must manually flush OpCache (see Troubleshooting). On staging/dev, set this to 1 and opcache.revalidate_freq=2.
    • opcache.jit=tracing + opcache.jit_buffer_size=128M -- Enable tracing JIT with a 128 MB buffer. Tracing mode is optimized for long-running web apps.
    Reload and verify:

    bash
    sudo systemctl reload php8.3-fpm
    php -r 'print_r(opcache_get_status(false));' 2>&1 | head

    You can also drop a tiny status script at /var/www/html/opcache.php:

    php
    <?php phpinfo(); ?>

    and browse to http://your-server/opcache.php -- look for the "Zend OPcache" and "opcache.jit" sections. Delete this file immediately after checking -- leaving phpinfo() exposed on production is a real security risk.

    Step 8: Connect Nginx to PHP-FPM via FastCGI

    If Nginx is not installed yet:

    bash
    sudo apt install -y nginx
    sudo systemctl enable --now nginx

    For a deeper Nginx walkthrough (TLS, HTTP/2, gzip, rate limits), see our companion guide How to Install Nginx on Ubuntu 24.04. For a full stack including MariaDB, follow How to Set Up a LEMP Stack on Ubuntu 24.04.

    Create a site config at /etc/nginx/sites-available/php-app:

    bash
    sudo tee /etc/nginx/sites-available/php-app > /dev/null <<'EOF'
    server {
        listen 80;
        server_name example.com www.example.com;
        root /var/www/html;

    index index.php index.html;

    # Security: don't leak Nginx version server_tokens off;

    # Max upload size must match php.ini post_max_size client_max_body_size 64m;

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

    location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params;

    # Timeouts (align with PHP-FPM request_terminate_timeout) fastcgi_read_timeout 60s; fastcgi_send_timeout 60s; fastcgi_connect_timeout 5s;

    # Buffers (tune for large responses) fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; }

    # Deny access to hidden files and sensitive paths location ~ /\.(?!well-known) { deny all; } location ~* \.(env|ini|log|sh|sql)$ { deny all; } } EOF

    Enable the site and reload:

    bash
    sudo ln -s /etc/nginx/sites-available/php-app /etc/nginx/sites-enabled/
    sudo rm -f /etc/nginx/sites-enabled/default
    sudo nginx -t && sudo systemctl reload nginx

    The snippets/fastcgi-php.conf file is shipped by the Nginx package and contains safe defaults for fastcgi_split_path_info and security-critical fastcgi_param directives. Always include it rather than rolling your own.

    Test end-to-end:

    bash
    echo "<?php phpinfo();" | sudo tee /var/www/html/info.php
    curl -I http://localhost/info.php

    You should see X-Powered-By: PHP/8.3.x in the response headers (unless you set expose_php = Off, in which case the header is absent and you can confirm via the rendered phpinfo() page instead). Delete info.php after verifying.

    Step 9: Run Multiple PHP Versions Side by Side

    One of the killer features of the Ondrej PPA is co-installable PHP versions. You can run PHP 8.1 for a legacy app, PHP 8.3 for your main site, and PHP 8.4 for a staging project -- all on the same server, each with its own FPM pool and socket.

    Install an additional version, for example PHP 8.1:

    bash
    sudo apt install -y php8.1-fpm php8.1-cli php8.1-mysql php8.1-curl php8.1-mbstring php8.1-xml php8.1-zip php8.1-opcache

    This creates a fully independent php8.1-fpm systemd service, its own config tree at /etc/php/8.1/, and a distinct socket at /run/php/php8.1-fpm.sock.

    Switch the CLI Default with update-alternatives

    The php command on the CLI is managed via update-alternatives:

    bash
    sudo update-alternatives --config php

    Expected output:

    text
    There are 2 choices for the alternative php (providing /usr/bin/php).

    Selection Path Priority Status ------------------------------------------------------------

    • 0 /usr/bin/php8.3 83 auto mode
    1 /usr/bin/php8.1 81 manual mode 2 /usr/bin/php8.3 83 manual mode

    Press <enter> to keep the current choice[*], or type selection number:

    Pick the version you want as the default php binary. Note that this only affects the CLI -- each FPM pool continues to use its own version based on the socket Nginx points at.

    Route Each Site to a Specific Version

    In your Nginx site config, choose the socket:

    nginx
    # Legacy app on PHP 8.1
    server {
        server_name legacy.example.com;
        location ~ \.php$ {
            fastcgi_pass unix:/run/php/php8.1-fpm.sock;
            include snippets/fastcgi-php.conf;
        }
    }

    Main app on PHP 8.3

    server { server_name example.com; location ~ \.php$ { fastcgi_pass unix:/run/php/php8.3-fpm.sock; include snippets/fastcgi-php.conf; } }

    Per-Version Pool Tuning

    Each version has its own pool.d/www.conf at /etc/php/<version>/fpm/pool.d/www.conf. You can also create multiple pools within a single version for per-site isolation. Copy www.conf to site1.conf, change [www] to [site1], pick a unique socket path (listen = /run/php/php8.3-site1.sock), set a dedicated user and group, and reload:

    bash
    sudo systemctl reload php8.3-fpm

    This pattern is how multi-tenant PHP hosting is built -- one pool per customer, each running as its own Unix user, each with its own pm.max_children and php_admin_value[memory_limit] overrides.

    Troubleshooting

    ProblemCauseSolution
    Nginx returns 502 Bad GatewayPHP-FPM is not running, the socket path is wrong, or FPM crashed under loadCheck service: sudo systemctl status php8.3-fpm. Verify socket: ls -l /run/php/. Tail error log: sudo tail -f /var/log/nginx/error.log. Raise pm.max_children if the log shows "server reached pm.max_children setting".
    504 Gateway TimeoutScript ran longer than fastcgi_read_timeout or max_execution_timeRaise both. Investigate slow queries via slowlog at /var/log/php8.3-fpm-slow.log.
    Call to undefined function for a built-inExtension missing or not enabled for this SAPIphp -m \</td><td>grep <ext><code> to confirm. Install: </code>sudo apt install php8.3-<ext><code>. Reload FPM: </code>sudo systemctl reload php8.3-fpm.
    Code changes not visible after deployOpCache has validate_timestamps=0 and is serving stale bytecodeReload FPM: sudo systemctl reload php8.3-fpm, or call opcache_reset() from a privileged endpoint, or use cachetool: cachetool opcache:reset --fcgi=/run/php/php8.3-fpm.sock.
    WARNING: [pool www] server reached pm.max_children setting (20), consider raising it in FPM logToo much concurrent traffic for the configured worker poolRaise pm.max_children (see formula in Step 5). If RAM is already saturated, move to a bigger plan or add a second server behind a load balancer.
    Uncaught RedisException: Connection refusedRedis server not installed/runningsudo apt install -y redis-server && sudo systemctl enable --now redis-server. Test: redis-cli ping should return PONG.
    Allowed memory size of X bytes exhaustedmemory_limit too low for this scriptRaise memory_limit in php.ini, or override per-pool with php_admin_value[memory_limit] = 512M in the pool config.
    Too many open connections / connect() to unix:/run/php/php8.3-fpm.sock failed (11: Resource temporarily unavailable)Socket backlog exhausted under burst trafficAdd listen.backlog = 4096 to the pool config. Check kernel net.core.somaxconn and raise if needed: sudo sysctl -w net.core.somaxconn=4096.
    imagick or redis extension loads on CLI but not FPMExtension enabled for cli SAPI onlyCheck: ls /etc/php/8.3/fpm/conf.d/ \</td><td>grep <ext><code>. Enable: </code>sudo phpenmod -s fpm <ext> then reload FPM.

    Viewing Logs

    • FPM process errors: sudo journalctl -u php8.3-fpm -f
    • PHP errors: sudo tail -f /var/log/php8.3-fpm-error.log
    • Slow requests: sudo tail -f /var/log/php8.3-fpm-slow.log
    • Nginx access/error: sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log

    FAQ

    Should I use a Unix socket or a TCP port for PHP-FPM?

    For single-server deployments where Nginx and PHP-FPM are on the same machine, use a Unix socket (/run/php/php8.3-fpm.sock). It's slightly faster (no TCP/IP stack overhead) and is automatically protected by filesystem permissions. For multi-server setups where Nginx is on a separate load-balancer box, use TCP (listen = 127.0.0.1:9000 on the PHP server, fastcgi_pass 10.0.0.5:9000 on Nginx) and bind to a private network interface. Never expose FPM on a public IP.

    What's the difference between pm=dynamic and pm=ondemand?

    pm=dynamic keeps a pool of idle workers warm, ready to serve requests instantly -- best for any site with steady traffic. pm=ondemand spawns workers on demand and kills them when idle, saving RAM on low-traffic sites at the cost of a few-millisecond cold-start penalty on the first request after idle. Use ondemand when you host many low-traffic sites on one box (multi-tenant hosting, staging environments) and dynamic for anything with consistent load.

    How do I reload OpCache after a deploy without reloading PHP-FPM?

    Three options: (1) the simplest is sudo systemctl reload php8.3-fpm, which is a graceful no-downtime reload. (2) Call opcache_reset() from a protected admin endpoint in your app. (3) Install cachetool and run cachetool opcache:reset --fcgi=/run/php/php8.3-fpm.sock from your deploy script -- this talks directly to FPM via FastCGI without any app-level code.

    Can I use the Ondrej PPA alongside the stock Ubuntu PHP packages?

    Technically yes, but avoid it. Pin priorities get complicated, and a future apt upgrade can silently cross-replace packages. The cleanest approach is: once you add the PPA, let it own every PHP package on the box. If you need to remove the PPA later, use ppa-purge to cleanly downgrade back to Ubuntu packages.

    Is PHP-FPM enough for high-traffic WordPress / Laravel, or do I need more?

    PHP-FPM + OpCache + JIT gets you very far. For most sites up to ~1M requests/day on a single VPS, tuning the pool size and enabling a full-page cache (WP Super Cache, Nginx FastCGI cache, Varnish in front) matters more than the PHP stack itself. Add Redis for object/session caching, a CDN for static assets, and horizontal scaling behind a load balancer only when you actually hit CPU or concurrency limits on a single box.

    Next Steps

    Now that PHP 8.3 + PHP-FPM is running on your VPS, here are the natural next moves:

    • Install MariaDB or MySQL to complete a LEMP stack -- follow our How to Install MariaDB on Ubuntu 24.04 guide, then tie it all together with the LEMP Stack install guide.
    • Add free SSL with Let's Encrypt -- sudo apt install -y certbot python3-certbot-nginx && sudo certbot --nginx gets you HTTPS in under a minute. See How to Set Up Let's Encrypt on Nginx.
    • Install Composer for Laravel / Symfony / modern PHP apps -- curl -sS https://getcomposer.org/installer | php && sudo mv composer.phar /usr/local/bin/composer.
    • Tune Nginx FastCGI caching -- cache rendered PHP output at the edge for 10x throughput on cacheable pages.
    • Monitor PHP-FPM -- enable the pm.status_path endpoint (already set in Step 5) and scrape it with php-fpm_exporter into Prometheus + Grafana for per-pool metrics (active workers, slow requests, queue depth).
    • Read the upstream docs -- php.net/manual/en/install.fpm.php for the authoritative FPM reference, and the Ondrej PPA repo at github.com/oerdnj/deb.sury.org for packaging details and the latest version matrix.

    Run PHP 8.3 on CloudCore Starter -- EUR-priced, NVMe-fast
    >
    Our CloudCore Starter plan is the sweet spot for modern PHP workloads:
    >
    - 4 vCPU cores, 8 GB RAM, 100 GB NVMe SSD
    - Ubuntu 24.04 LTS pre-installed
    - Full root SSH access from minute one
    - Unmetered bandwidth
    - Deploy in under 60 seconds
    >
    Launch a CloudCore VPS and follow this guide end to end -- you'll have a production-grade PHP-FPM stack running before your coffee gets cold.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket