Harden PHP-FPM with systemd service controls
Measure the current unit, add compatible filesystem and privilege restrictions, validate application paths, and roll out hardening without breaking package upgrades.
Reduce the impact of a compromised PHP worker while preserving required application writes and networking.
- systemd 252+
- 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
- A package-safe systemd drop-in that reduces PHP-FPM kernel, device, namespace, filesystem and privilege exposure.
- A measured writable-path and address-family allowlist derived from real application behavior rather than a copied hardening score.
- A staged validation, denial-observation and rollback workflow preserving application health and package upgrades.
- The FPM unit receives a materially improved exposure assessment without losing required sessions, uploads, caches, logs, DNS, database or upstream access.
- Every remaining privilege and every write/network exception has an application owner, evidence and review date.
Architecture
How the parts fit together
systemd starts the packaged PHP-FPM service and merges an administrator-owned drop-in without modifying the vendor unit. Process restrictions reduce privilege gain and kernel interfaces; filesystem protections make the host read-only except for enumerated state paths; address-family and device controls remove unused interfaces. A representative workload and journal review reveal compatibility gaps. The original vendor unit and prior drop-in remain recoverable through `systemctl revert`.
- Capture unit composition, process behavior and security score before changing policy.
- Map legitimate writes and network/device requirements with a representative workload.
- Add controls in compatible groups, verify unit syntax and restart only in a protected window.
- Exercise all capabilities, inspect denials and either narrow exceptions or revert the drop-in.
Assumptions
- The host uses a supported systemd release and the exact PHP-FPM service/pool/application identity is known.
- Console or out-of-band recovery and a maintenance/canary path are available before service restart.
- Application owners can exercise reads, writes, sessions, uploads, database/DNS/upstream access and background behavior.
- Writable paths are outside immutable releases and have explicit ownership and capacity controls.
Key concepts
- drop-in
- An administrator-owned unit fragment merged with the vendor unit, preserving package upgrade visibility and rollback.
- ProtectSystem
- A systemd filesystem control that can make operating-system paths read-only with explicit write exceptions.
- NoNewPrivileges
- A process rule preventing children from gaining privileges through setuid, file capabilities or similar mechanisms.
- address-family restriction
- An allowlist of socket families a service may create, which must retain every required local/network protocol.
Before you copy
Values used in this guide
{{app}}Short application identifier used in service, pool, directory, and configuration names.
Example: billing{{appUser}}Non-login operating-system identity used by the application workers.
Example: billing{{domain}}Fully qualified public or internal hostname served by this configuration.
Example: billing.example.com{{phpVersion}}Exact PHP major and minor branch used by the target runtime.
Example: 8.3{{safeFixture}}Small non-sensitive test file used to exercise an upload or parser path.
Example: /srv/billing/fixtures/upload.txtSecurity and production boundaries
- A lower `systemd-analyze security` score is guidance, not proof; workload checks and narrow exceptions decide whether the policy is safe.
- Never add broad `/`, `/etc`, `/usr` or entire application roots to ReadWritePaths just to suppress a denial.
- Preserve denial logs and review extension behavior because native modules execute inside the worker security boundary.
Stop before continuing if
- Stop if the baseline cannot identify required paths/network behavior, recovery access is absent, or the unit verification reports unknown directives.
- Immediately revert when health, sessions, uploads, logs, name resolution, database or required upstream calls fail after restart.
instruction
Capture the packaged unit and security score
Record vendor unit contents, drop-ins, dynamic properties, filesystem use, and systemd-analyze security output before adding restrictions.
Why this step matters
Capture the packaged unit and security score is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Record vendor unit contents, drop-ins, dynamic properties, filesystem use, and systemd-analyze security output before adding restrictions. 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 “Capture the packaged unit and security score”. The scope must remain limited to the selected application and supported host.
Syntax explained
systemctl- Invokes the principal tool or configuration operation for “Capture the packaged unit and security score”; 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.
systemctl cat php{{phpVersion}}-fpm; systemctl show php{{phpVersion}}-fpm > /tmp/php-fpm-unit-before.txt; systemd-analyze security php{{phpVersion}}-fpm.serviceUnit baseline and exposure score are recorded.
Checkpoint: Checkpoint: Capture the packaged unit and security score
Continue whenUnit baseline and exposure score are recorded. 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 systemctl 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 “Capture the packaged unit and security score” 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 systemctl 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
Map required writable paths
Use application documentation and a representative test to list sockets, sessions, cache, uploads, logs, and release paths that FPM must modify.
Why this step matters
Map required writable paths is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Use application documentation and a representative test to list sockets, sessions, cache, uploads, logs, and release paths that FPM must modify. 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 “Map required writable paths”. The scope must remain limited to the selected application and supported host.
Syntax explained
-u- Invokes the principal tool or configuration operation for “Map required writable paths”; 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.
|- Passes standard output to the next read-only inspection stage; review each stage separately if the combined result is surprising.
Values stay on this page and are never sent or saved.
sudo -u {{appUser}} find /srv/{{app}} -xdev -type d -writable -printf '%m %u:%g %p\n'; sudo lsof -p "$(pgrep -o php-fpm)" 2>/dev/null | head -n 50A minimal writable-path allowlist is documented.
Checkpoint: Checkpoint: Map required writable paths
Continue whenA minimal writable-path allowlist is documented. 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 -u 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 “Map required writable paths” 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 -u 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 a package-safe systemd drop-in
Use systemctl edit instead of modifying the vendor unit so package upgrades remain visible and reversible.
Why this step matters
Create a package-safe systemd drop-in is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Use systemctl edit instead of modifying the vendor unit so package upgrades remain visible and reversible. 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 a package-safe systemd drop-in”. The scope must remain limited to the selected application and supported host.
Syntax explained
systemctl- Invokes the principal tool or configuration operation for “Create a package-safe systemd drop-in”; 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.
/etc/systemd/system/php{{phpVersion}}-fpm.service.d/override.conf- Is the persistent configuration boundary changed by this step. Keep an attributable backup and validate the consumer before reload.
/etc/systemd/system/php{{phpVersion}}-fpm.service.d/override.confValues stay on this page and are never sent or saved.
sudo systemctl edit php{{phpVersion}}-fpm.serviceA drop-in exists below /etc/systemd/system.
Checkpoint: Checkpoint: Create a package-safe systemd drop-in
Continue whenA drop-in exists below /etc/systemd/system. 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 systemctl 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 a package-safe systemd drop-in” 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 systemctl 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
Block unnecessary privilege gains and kernel access
Add NoNewPrivileges, PrivateTmp, ProtectKernelTunables, ProtectKernelModules, ProtectControlGroups, and RestrictSUIDSGID after checking runtime compatibility.
Why this step matters
Block unnecessary privilege gains and kernel access is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Add NoNewPrivileges, PrivateTmp, ProtectKernelTunables, ProtectKernelModules, ProtectControlGroups, and RestrictSUIDSGID after checking runtime compatibility. 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 “Block unnecessary privilege gains and kernel access”. The scope must remain limited to the selected application and supported host.
Syntax explained
systemctl- Invokes the principal tool or configuration operation for “Block unnecessary privilege gains and kernel access”; 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.
|- 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.
Values stay on this page and are never sent or saved.
systemctl cat php{{phpVersion}}-fpm | grep -E 'NoNewPrivileges|PrivateTmp|ProtectKernel|ProtectControlGroups|RestrictSUIDSGID'Privilege and kernel protections are explicit.
Checkpoint: Checkpoint: Block unnecessary privilege gains and kernel access
Continue whenPrivilege and kernel protections are explicit. 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 systemctl 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 “Block unnecessary privilege gains and kernel access” 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 systemctl 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
Make the system read-only with narrow write exceptions
Use ProtectSystem=strict, ProtectHome, ReadWritePaths for required application state, and RuntimeDirectory for sockets where compatible.
Why this step matters
Make the system read-only with narrow write exceptions is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Use ProtectSystem=strict, ProtectHome, ReadWritePaths for required application state, and RuntimeDirectory for sockets where compatible. 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 “Make the system read-only with narrow write exceptions”. The scope must remain limited to the selected application and supported host.
Syntax explained
systemctl- Invokes the principal tool or configuration operation for “Make the system read-only with narrow write exceptions”; 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.
|- 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.
Values stay on this page and are never sent or saved.
systemctl cat php{{phpVersion}}-fpm | grep -E 'ProtectSystem|ProtectHome|ReadWritePaths|RuntimeDirectory'Only documented state paths remain writable.
Checkpoint: Checkpoint: Make the system read-only with narrow write exceptions
Continue whenOnly documented state paths remain writable. 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 systemctl 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 “Make the system read-only with narrow write exceptions” 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 systemctl 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
Constrain devices, address families, and namespaces
Use PrivateDevices, RestrictAddressFamilies for Unix and required network families, and RestrictNamespaces only after extension and application tests.
Why this step matters
Constrain devices, address families, and namespaces is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Use PrivateDevices, RestrictAddressFamilies for Unix and required network families, and RestrictNamespaces only after extension and application tests. 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 “Constrain devices, address families, and namespaces”. The scope must remain limited to the selected application and supported host.
Syntax explained
systemctl- Invokes the principal tool or configuration operation for “Constrain devices, address families, and namespaces”; 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.
|- 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.
Values stay on this page and are never sent or saved.
systemctl cat php{{phpVersion}}-fpm | grep -E 'PrivateDevices|RestrictAddressFamilies|RestrictNamespaces'The unit exposes only required kernel interfaces.
Checkpoint: Checkpoint: Constrain devices, address families, and namespaces
Continue whenThe unit exposes only required kernel interfaces. 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 systemctl 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 “Constrain devices, address families, and namespaces” 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 systemctl 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
Verify unit syntax and start in a maintenance window
Run systemd-analyze verify, daemon-reload, restart FPM, and inspect denial messages before returning traffic.
Why this step matters
Verify unit syntax and start in a maintenance window is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Run systemd-analyze verify, daemon-reload, restart FPM, and inspect denial messages before returning traffic. 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 “Verify unit syntax and start in a maintenance window”. The scope must remain limited to the selected application and supported host.
Syntax explained
systemd-analyze- Invokes the principal tool or configuration operation for “Verify unit syntax and start in a maintenance window”; 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 systemd-analyze verify php{{phpVersion}}-fpm.service; sudo systemctl daemon-reload; sudo systemctl restart php{{phpVersion}}-fpm; sudo journalctl -u php{{phpVersion}}-fpm --since '-5 min' --no-pagerThe unit starts and logs contain no unexpected permission denial.
Checkpoint: Checkpoint: Verify unit syntax and start in a maintenance window
Continue whenThe unit starts and logs contain no unexpected permission denial. 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 systemd-analyze 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 “Verify unit syntax and start in a maintenance window” 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 systemd-analyze 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
Exercise every required capability
Test reads, configured writes, sessions, uploads, database connections, DNS, outbound calls if required, background jobs, and log delivery.
Why this step matters
Exercise every required capability is a separate checkpoint because it establishes one auditable part of reduce the impact of a compromised php worker while preserving required application writes and networking. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.
What to understand
Test reads, configured writes, sessions, uploads, database connections, DNS, outbound calls if required, background jobs, and log delivery. 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 “Exercise every required capability”. The scope must remain limited to the selected application and supported host.
Syntax explained
curl- Invokes the principal tool or configuration operation for “Exercise every required capability”; 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.
curl --fail --silent https://{{domain}}/internal/dependency-health; curl --fail --silent -F file=@{{safeFixture}} https://{{domain}}/internal/upload-testAll allowlisted application capabilities succeed.
Checkpoint: Checkpoint: Exercise every required capability
Continue whenAll allowlisted application capabilities succeed. 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 curl 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 “Exercise every required capability” 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 curl 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
systemd-analyze security php{{phpVersion}}-fpm.serviceThe exposure score improves and every remaining item has a documented compatibility reason.sudo systemctl restart php{{phpVersion}}-fpm && curl --fail --silent https://{{domain}}/healthFPM restarts and the application stays 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.
Recovery
Rollback
Remove or revert the systemd drop-in, daemon-reload, and restart FPM.
- Use systemctl revert php{{phpVersion}}-fpm.service or restore the prior drop-in archive.
- Run daemon-reload, restart FPM, and repeat health and write-path tests.
- Preserve journal denial evidence to design a narrower compatible policy.
Evidence