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.
Host applications requiring different PHP branches without ambiguous handlers, shared sockets, or accidental cross-upgrades.
- PHP 8.3, 8.4, 8.5
- 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.
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
- 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.
- 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.
- Inventory package provenance and current request routing before installing a second branch.
- Create a dedicated pool/socket and validate its configuration independently.
- Point only a canary server block at the new socket and run real dependency, session, upload and database checks.
- 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-php85packagesSecurity 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.
instruction
Build an application compatibility matrix
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.
printf '%-24s %-8s %-32s\n' application php socket; grep -R --line-number 'fastcgi_pass' /etc/nginx/sites-enabledEvery 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.
command -v printf 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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.
command
Install versioned runtimes and matching extensions
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.
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}}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.
command -v apt 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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.
config
Create one pool per application and version
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.
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}}.confNo 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.
command -v sudoedit 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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.
verification
Validate both FPM trees
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.
sudo php-fpm8.3 -tt; sudo php-fpm8.5 -tt; sudo systemctl restart php8.3-fpm php8.5-fpm; sudo ss -lxnp | grep phpBoth 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.
command -v php-fpm8.3 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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.
config
Bind each nginx server block to one explicit socket
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.
sudo grep -R --line-number -E 'server_name|fastcgi_pass' /etc/nginx/sites-enabledEach 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.
command -v grep 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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.
config
Prevent arbitrary PHP execution
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.
sudo nginx -T | grep -nE 'location .*\.php|try_files|fastcgi_pass|uploads'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.
command -v nginx 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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.
verification
Canary one application on the new branch
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.
Values stay on this page and are never sent or saved.
sudo nginx -t && sudo systemctl reload nginx; curl --fail --silent https://{{canaryDomain}}/internal/runtimeThe 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.
command -v nginx 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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.
instruction
Compare runtime health before migration
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.
sudo journalctl -u php8.3-fpm -u php8.5-fpm --since '-30 min' --no-pager; systemctl status php8.3-fpm php8.5-fpm --no-pagerNo 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.
command -v journalctl 2>/dev/null || truesystemctl --failed --no-pager 2>/dev/null || truejournalctl -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
curl --fail --silent https://{{legacyDomain}}/internal/runtime; curl --fail --silent https://{{newDomain}}/internal/runtimeEach site reports its assigned PHP branch.sudo 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.
systemctl status 'php*-fpm' --no-pagerfind /run -maxdepth 3 -type s -name '*php*fpm*' -lsjournalctl -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.
php --inisystemctl list-units --type=service '*php*fpm*' --no-pagergrep -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.
systemctl --failed --no-pagerjournalctl -p warning --since '-10 min' --no-pagercurl --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.
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.
- Restore the archived nginx server block, validate, reload, and test the old application release.
- Keep the newer FPM service isolated but running until logs and queued work are inspected.
- Remove a runtime only after no enabled configuration, timer, or job references it.
Evidence