OneLinersCommand workbench
Guides
Services & Applications / DevOps & CI/CD

Run multiple PHP versions behind nginx with isolated FPM pools

Install versioned runtimes, create distinct sockets and services, route each server block explicitly, and upgrade one application at a time.

60 min8 stepsChanges system stateRevision 2
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 8 steps completed
Goal

Host applications requiring different PHP branches without ambiguous handlers, shared sockets, or accidental cross-upgrades.

Supported environments
  • PHP 8.3, 8.4, 8.5
Prerequisites
  • Supported host and recovery access Use a supported distribution, keep an independent root-capable session, and record the current package repositories before changing the PHP runtime.cat /etc/os-release && uname -m
  • Application compatibility Confirm the application, framework, extensions, and deployment tooling explicitly support the target PHP branch.
  • Configuration backup Archive the active PHP, web-server, and application configuration before packages, pools, or handlers change.
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • Two independently packaged and supervised PHP-FPM branches, each with its own application identity, pool, socket, INI tree, logs, limits, and lifecycle.
  • An nginx routing layer that maps each hostname to exactly one versioned socket without changing the system-wide CLI interpreter.
  • A tested upgrade and rollback path that moves one canary application between branches while preserving the previous pool.
Observable outcome
  • Each hostname proves its expected PHP branch through a protected health response and cannot accidentally cross into the other application's pool.
  • Restarting, upgrading, or rolling back one branch leaves the other branch healthy and observable.

Architecture

How the parts fit together

nginx terminates the client connection and chooses a server block by hostname. Its FastCGI target is a private Unix socket owned by one versioned PHP-FPM pool. Each pool uses a non-login application identity and a branch-specific INI tree, so packages, extensions, limits and restarts remain explicit. A canary hostname is moved first; only after dependency and application checks pass is production routing changed. The previous pool remains installed until rollback confidence expires.

Versioned PHP packagesSupply two supported runtimes without replacing one branch with the other.
Dedicated FPM poolsSeparate identities, sockets, workers, logs and configuration by application and version.
nginx server blocksRoute each hostname to one exact socket and prevent arbitrary script execution.
Canary and rollbackProve application compatibility before the old runtime is retired.
  1. Inventory package provenance and current request routing before installing a second branch.
  2. Create a dedicated pool/socket and validate its configuration independently.
  3. Point only a canary server block at the new socket and run real dependency, session, upload and database checks.
  4. Promote production deliberately while preserving the prior pool and configuration for rollback.

Assumptions

  • Both PHP branches are supported by an approved signed repository and all required native extensions exist for both ABIs.
  • Applications use separate hostnames or server blocks and have representative tests plus a maintenance/canary window.
  • The operator knows the exact application identities, sockets, service units, INI trees and rollback package versions.
  • No shared in-memory session or cache format silently depends on one PHP branch without a compatibility test.

Key concepts

SAPI
A PHP execution interface; CLI and each FPM daemon can load different binaries and configuration.
pool socket
The Unix endpoint through which a web server sends a request to one specific FPM pool.
ABI
The binary interface that requires native extensions to match the exact PHP/Zend branch and architecture.
canary
A limited hostname or traffic slice used to prove compatibility before broad promotion.

Before you copy

Values used in this guide

{{canaryDomain}}

Operator-reviewed value used by this tutorial for canaryDomain; derive it from the target environment and never from untrusted request data.

Example: example-canarydomain
{{legacyApp}}

Operator-reviewed value used by this tutorial for legacyApp; derive it from the target environment and never from untrusted request data.

Example: example-legacyapp
{{legacyDomain}}

Operator-reviewed value used by this tutorial for legacyDomain; derive it from the target environment and never from untrusted request data.

Example: example-legacydomain
{{newApp}}

Operator-reviewed value used by this tutorial for newApp; derive it from the target environment and never from untrusted request data.

Example: example-newapp
{{newDomain}}

Operator-reviewed value used by this tutorial for newDomain; derive it from the target environment and never from untrusted request data.

Example: example-newdomain
{{php83Packages}}

Operator-reviewed value used by this tutorial for php83Packages; derive it from the target environment and never from untrusted request data.

Example: example-php83packages
{{php85Packages}}

Operator-reviewed value used by this tutorial for php85Packages; derive it from the target environment and never from untrusted request data.

Example: example-php85packages

Security and production boundaries

  • Never solve cross-version access by making sockets world-writable; use explicit web-server group membership and restrictive modes.
  • Keep application identities and writable paths separate, and patch both branches until the retired branch is removed.
  • Expose only existing scripts below the document root and keep diagnostic version endpoints authenticated or internally restricted.

Stop before continuing if

  • Stop if the second repository would replace the supported system runtime, disable signature checks, or provide incompatible extension ABIs.
  • Do not switch production when the canary differs in extensions, INI, session behavior, database behavior, queue workers, or health output.
01

instruction

Build an application compatibility matrix

read-only

For each site record current PHP branch, required extensions, framework support, test suite, socket, and rollback release.

Why this step matters

Build an application compatibility matrix is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

For each site record current PHP branch, required extensions, framework support, test suite, socket, and rollback release. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Build an application compatibility matrix”. The scope must remain limited to the selected application and supported host.

Syntax explained

printf
Invokes the principal tool or configuration operation for “Build an application compatibility matrix”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
Command
printf '%-24s %-8s %-32s\n' application php socket; grep -R --line-number 'fastcgi_pass' /etc/nginx/sites-enabled
Example output / evidence
Every active site maps to a known PHP version and socket.

Checkpoint: Checkpoint: Build an application compatibility matrix

Continue whenEvery active site maps to a known PHP version and socket. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when printf reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Build an application compatibility matrix” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v printf 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.
02

command

Install versioned runtimes and matching extensions

caution

Use one trusted repository family and explicitly version every FPM and extension package.

Why this step matters

Install versioned runtimes and matching extensions is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Use one trusted repository family and explicitly version every FPM and extension package. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Install versioned runtimes and matching extensions”. The scope must remain limited to the selected application and supported host.

Syntax explained

apt
Invokes the principal tool or configuration operation for “Install versioned runtimes and matching extensions”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
{{…}}
Marks an operator-supplied value. Replace every placeholder deliberately; do not paste untrusted text or leave braces in the production command.
Command
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

sudo apt install --yes php8.3-fpm php8.3-cli {{php83Packages}} php8.5-fpm php8.5-cli {{php85Packages}}
Example output / evidence
Both versioned services and approved extension sets install.

Checkpoint: Checkpoint: Install versioned runtimes and matching extensions

Continue whenBoth versioned services and approved extension sets install. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when apt reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Install versioned runtimes and matching extensions” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v apt 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.
03

config

Create one pool per application and version

caution

Give each pool a unique identity, socket, log path, and capacity limit; disable unused default pools.

Why this step matters

Create one pool per application and version is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Give each pool a unique identity, socket, log path, and capacity limit; disable unused default pools. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Create one pool per application and version”. The scope must remain limited to the selected application and supported host.

Syntax explained

sudoedit
Invokes the principal tool or configuration operation for “Create one pool per application and version”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
{{…}}
Marks an operator-supplied value. Replace every placeholder deliberately; do not paste untrusted text or leave braces in the production command.
configuration file
Is the persistent configuration boundary changed by this step. Keep an attributable backup and validate the consumer before reload.
Configuration
Fill variables0/2 ready

Values stay on this page and are never sent or saved.

sudoedit /etc/php/8.3/fpm/pool.d/{{legacyApp}}.conf; sudoedit /etc/php/8.5/fpm/pool.d/{{newApp}}.conf
Example output / evidence
No two pools share a name or socket.

Checkpoint: Checkpoint: Create one pool per application and version

Continue whenNo two pools share a name or socket. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when sudoedit reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Create one pool per application and version” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v sudoedit 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.
04

verification

Validate both FPM trees

caution

Run each versioned binary's configuration test and list socket paths before touching nginx.

Why this step matters

Validate both FPM trees is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Run each versioned binary's configuration test and list socket paths before touching nginx. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Validate both FPM trees”. The scope must remain limited to the selected application and supported host.

Syntax explained

php-fpm8.3
Invokes the principal tool or configuration operation for “Validate both FPM trees”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
|
Passes standard output to the next read-only inspection stage; review each stage separately if the combined result is surprising.
Command
sudo php-fpm8.3 -tt; sudo php-fpm8.5 -tt; sudo systemctl restart php8.3-fpm php8.5-fpm; sudo ss -lxnp | grep php
Example output / evidence
Both versions validate, run, and expose distinct sockets.

Checkpoint: Checkpoint: Validate both FPM trees

Continue whenBoth versions validate, run, and expose distinct sockets. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when php-fpm8.3 reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Validate both FPM trees” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v php-fpm8.3 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.
05

config

Bind each nginx server block to one explicit socket

caution

Use a literal unix socket in the site's PHP location and include the distribution fastcgi parameters; never point at an unversioned alias.

Why this step matters

Bind each nginx server block to one explicit socket is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Use a literal unix socket in the site's PHP location and include the distribution fastcgi parameters; never point at an unversioned alias. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Bind each nginx server block to one explicit socket”. The scope must remain limited to the selected application and supported host.

Syntax explained

grep
Invokes the principal tool or configuration operation for “Bind each nginx server block to one explicit socket”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
|
Passes standard output to the next read-only inspection stage; review each stage separately if the combined result is surprising.
configuration file
Is the persistent configuration boundary changed by this step. Keep an attributable backup and validate the consumer before reload.
Configuration
sudo grep -R --line-number -E 'server_name|fastcgi_pass' /etc/nginx/sites-enabled
Example output / evidence
Each server_name is paired with exactly one versioned application socket.

Checkpoint: Checkpoint: Bind each nginx server block to one explicit socket

Continue whenEach server_name is paired with exactly one versioned application socket. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when grep reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Bind each nginx server block to one explicit socket” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v grep 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.
06

config

Prevent arbitrary PHP execution

caution

Use try_files to require an existing script, split no untrusted path info, and deny uploads or writable directories from executing PHP.

Why this step matters

Prevent arbitrary PHP execution is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Use try_files to require an existing script, split no untrusted path info, and deny uploads or writable directories from executing PHP. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Prevent arbitrary PHP execution”. The scope must remain limited to the selected application and supported host.

Syntax explained

nginx
Invokes the principal tool or configuration operation for “Prevent arbitrary PHP execution”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
|
Passes standard output to the next read-only inspection stage; review each stage separately if the combined result is surprising.
configuration file
Is the persistent configuration boundary changed by this step. Keep an attributable backup and validate the consumer before reload.
Configuration
sudo nginx -T | grep -nE 'location .*\.php|try_files|fastcgi_pass|uploads'
Example output / evidence
PHP execution is limited to intended locations and existing scripts.

Checkpoint: Checkpoint: Prevent arbitrary PHP execution

Continue whenPHP execution is limited to intended locations and existing scripts. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when nginx reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Prevent arbitrary PHP execution” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v nginx 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.
07

verification

Canary one application on the new branch

caution

Route a test hostname or controlled upstream to PHP 8.5, then exercise health, jobs, uploads, and representative database paths.

Why this step matters

Canary one application on the new branch is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Route a test hostname or controlled upstream to PHP 8.5, then exercise health, jobs, uploads, and representative database paths. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Canary one application on the new branch”. The scope must remain limited to the selected application and supported host.

Syntax explained

nginx
Invokes the principal tool or configuration operation for “Canary one application on the new branch”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
{{…}}
Marks an operator-supplied value. Replace every placeholder deliberately; do not paste untrusted text or leave braces in the production command.
&&
Runs the following operation only when the previous command succeeds, preventing a reload or state transition after a failed validation.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo nginx -t && sudo systemctl reload nginx; curl --fail --silent https://{{canaryDomain}}/internal/runtime
Example output / evidence
The canary reports PHP 8.5 and application checks pass.

Checkpoint: Checkpoint: Canary one application on the new branch

Continue whenThe canary reports PHP 8.5 and application checks pass. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when nginx reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Canary one application on the new branch” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v nginx 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.
08

instruction

Compare runtime health before migration

read-only

Compare FPM status, error logs, latency, memory, and application errors for both branches before moving another site.

Why this step matters

Compare runtime health before migration is a separate checkpoint because it establishes one auditable part of host applications requiring different php branches without ambiguous handlers, shared sockets, or accidental cross-upgrades. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Compare FPM status, error logs, latency, memory, and application errors for both branches before moving another site. Read the complete command or configuration before execution, replace the documented placeholders, and compare the target host with the supported environments listed at the beginning of this tutorial.

Capture the before-state and the exact output in the change record. If this step modifies a file or service, validate the candidate with its native checker before any reload; a successful process exit alone does not prove that the application uses the intended value.

Use a representative staging or canary host first. Repeat the stated checkpoint immediately, because proceeding with ambiguous evidence makes the later end-to-end verification and rollback materially harder.

System changes

  • This step may affect the files, packages, identities, services, or runtime policy named in “Compare runtime health before migration”. The scope must remain limited to the selected application and supported host.

Syntax explained

journalctl
Invokes the principal tool or configuration operation for “Compare runtime health before migration”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
Command
sudo journalctl -u php8.3-fpm -u php8.5-fpm --since '-30 min' --no-pager; systemctl status php8.3-fpm php8.5-fpm --no-pager
Example output / evidence
No new startup, extension, or application error blocks promotion.

Checkpoint: Checkpoint: Compare runtime health before migration

Continue whenNo new startup, extension, or application error blocks promotion. Treat this concrete evidence as the minimum success signal and also confirm that unrelated services and the previous healthy path remain unchanged.

Stop whenStop before the next step when journalctl reports an error, the output differs materially from the example, an unexpected package/service/path is affected, or the required before-state and rollback artifact are missing.

If this step fails

The command for “Compare runtime health before migration” fails, produces no useful evidence, or the expected service/configuration state is absent.

Likely causeA placeholder targets the wrong environment, the installed version uses another path or unit name, permissions are insufficient, or an earlier prerequisite was not satisfied.

Safe checks
  • command -v journalctl 2>/dev/null || true
  • systemctl --failed --no-pager 2>/dev/null || true
  • journalctl -p warning -n 50 --no-pager 2>/dev/null || true

ResolutionDo not improvise a privileged workaround. Re-read the environment and variable table, inspect the official source for the installed version, restore the candidate file if necessary, and repeat only this checkpoint.

Security notes

  • Keep secrets out of shell history, process arguments, screenshots, logs, and tutorial placeholders. Elevate only the narrow command that requires it and preserve package signature and TLS verification.

Alternatives

  • When the host layout or software version differs, use the vendor-supported equivalent in a disposable staging environment and document the mapping before touching production.

Stop conditions

  • Stop if the action broadens network exposure, permissions, package sources, writable paths, or service privileges beyond the tutorial's declared architecture.

Finish line

Verification checklist

Per-site versioncurl --fail --silent https://{{legacyDomain}}/internal/runtime; curl --fail --silent https://{{newDomain}}/internal/runtimeEach site reports its assigned PHP branch.
Restart survivalsudo systemctl restart php8.3-fpm php8.5-fpm nginx && systemctl is-active php8.3-fpm php8.5-fpm nginxAll services restart and both sites remain healthy.

Recovery guidance

Common problems and safe checks

The web server returns 502/503 or reports that the FastCGI upstream is unavailable.

Likely causeThe configured socket and the active PHP-FPM pool disagree, the pool failed validation, or Unix socket ownership does not permit the web-server worker to connect.

Safe checks
  • systemctl status 'php*-fpm' --no-pager
  • find /run -maxdepth 3 -type s -name '*php*fpm*' -ls
  • journalctl -u nginx -u apache2 -u php8.3-fpm -p warning -n 80 --no-pager

ResolutionMatch the virtual host to one validated pool socket and explicit owner/group/mode, then reload only after both native configuration tests pass.

CLI reports the intended PHP version but the HTTP application uses another version or INI value.

Likely causeCLI and FPM are separate SAPIs, the virtual host targets another pool, or a later scan-directory file overrides the reviewed setting.

Safe checks
  • php --ini
  • systemctl list-units --type=service '*php*fpm*' --no-pager
  • grep -R -n 'fastcgi_pass\|SetHandler' /etc/nginx /etc/apache2 2>/dev/null

ResolutionTrace the request from hostname through the selected handler and pool to its loaded INI tree; correct that specific boundary rather than changing global CLI alternatives.

Validation passes but a representative request fails after the reload.

Likely causeThe syntax is legal but a path, permission, resource limit, extension, database grant, network dependency, or systemd restriction is incompatible with the real workload.

Safe checks
  • systemctl --failed --no-pager
  • journalctl -p warning --since '-10 min' --no-pager
  • curl --fail --silent --show-error https://example.com/health

ResolutionRemove the host from traffic, compare the before-state, revert the smallest changed layer, and use the preserved logs plus a safe fixture to refine the candidate in staging.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Use immutable containers with one PHP branch per image when the platform already has a secure image lifecycle.
  • Upgrade applications serially on separate hosts or pools instead of maintaining parallel branches indefinitely.

Operate it safely

  • Record end-of-support dates for both branches and assign removal of the old pool, packages, socket and monitoring.
  • Add per-pool latency, saturation, slow-request and restart alerts so a version-specific regression is visible.

Reference

Frequently asked questions

Should `update-alternatives` switch PHP globally?

No. Web routing should select an explicit FPM socket; changing the global CLI can break deployment jobs and does not configure nginx.

Can pools share one socket?

No. A socket must identify one pool and runtime. Sharing removes deterministic routing and safe rollback.

Recovery

Rollback

Point the affected server block back to its previous versioned socket and reload nginx.

  1. Restore the archived nginx server block, validate, reload, and test the old application release.
  2. Keep the newer FPM service isolated but running until logs and queued work are inspected.
  3. Remove a runtime only after no enabled configuration, timer, or job references it.

Evidence

Sources and review

Verified 2026-07-25Review due 2026-10-23
PHP-FPM installation and configurationofficialnginx FastCGI moduleofficialPHP core configuration directivesofficial