OneLinersCommand workbench
Guides
Services & Applications / Databases & Data

Install a production LAMP stack on Ubuntu

Build Apache, MariaDB, PHP-FPM, TLS, application identity, database credentials, health checks, and backups as one verified stack.

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

Deploy a maintainable Linux, Apache, MariaDB, and PHP application baseline without exposing database or sample endpoints.

Supported environments
  • Ubuntu Server 24.04 LTS
  • PHP 8.3
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
  • A patched Ubuntu host running Apache, MariaDB and a dedicated PHP-FPM pool for one application.
  • A least-privilege database identity, isolated release layout, HTTPS virtual host, bounded firewall and concrete health/backup checks.
Observable outcome
  • The named HTTPS site reaches the intended FPM pool and local database while default sites, public database exposure and temporary diagnostics are absent.
  • Every service validates, restarts and produces attributable logs; a logical backup stream and rollback release are verified.

Architecture

How the parts fit together

Apache terminates TLS and serves only the application's public document root. PHP requests are forwarded through proxy_fcgi to a dedicated Unix-socket FPM pool running under a non-login application identity. MariaDB listens locally and grants one account access to one database. Immutable release directories separate code from writable shared state. UFW exposes only management SSH and HTTP/HTTPS, while health checks, service journals and a tested database dump provide operational evidence.

Apache virtual hostOwns hostname, TLS, document-root policy, request logs and FastCGI routing.
PHP-FPM poolRuns application code with bounded workers and a private Unix socket.
MariaDBStores application data behind a localhost-only least-privilege account.
Release and backup layoutSeparates deployable code, writable state and recoverable data.
  1. Patch and inventory the host before packages or firewall rules change.
  2. Install signed packages, create explicit identities/directories and provision the local database account.
  3. Validate the FPM pool and Apache virtual host before enabling TLS and reloading services.
  4. Exercise HTTPS, PHP, database and backup paths, then retain the previous release and configuration for rollback.

Assumptions

  • The target is a supported Ubuntu 24.04 LTS host with console/recovery access and correct time/DNS.
  • The hostname resolves to the host before certificate issuance and inbound ports 80/443 are permitted by upstream controls.
  • Application secrets are generated and delivered outside shell history; the release has a safe health endpoint and migration plan.

Key concepts

document root
The only directory Apache may expose; application secrets and writable private data remain outside it.
FastCGI
The protocol Apache uses to pass PHP requests to a separately supervised FPM worker pool.
least privilege
Giving the application identity and database account only the paths and schema rights they require.
release layout
Versioned immutable code plus separately managed shared state, enabling an atomic application rollback.

Before you copy

Values used in this guide

{{app}}

Short application identifier used in service, pool, directory, and configuration names.

Example: billing
{{appGroup}}

Operating-system group that owns application-readable or shared runtime paths.

Example: billing
{{appUser}}

Non-login operating-system identity used by the application workers.

Example: billing
{{database}}

Application database name; use an identifier approved by the database naming policy.

Example: billing
{{dbPassword}}

Generated database credential supplied through a protected secret channel, never committed to source control.

Example: use-a-secret-manager-generated-value
{{dbUser}}

Least-privilege database account used by the application.

Example: billing_app
{{domain}}

Fully qualified public or internal hostname served by this configuration.

Example: billing.example.com
{{managementCidr}}

Trusted administration network in CIDR notation.

Example: 192.0.2.0/24
{{phpVersion}}

Exact PHP major and minor branch used by the target runtime.

Example: 8.3

Security and production boundaries

  • Do not expose MariaDB publicly or grant global privileges to the application account.
  • Remove phpinfo and installation test files, restrict the public root, and keep certificate/database secrets outside repository and shell history.
  • Keep a second administration session open before enabling the firewall and verify upstream access controls independently.

Stop before continuing if

  • Stop if DNS points elsewhere, package provenance is unexpected, Apache exposes the release root, or the firewall would remove recovery access.
  • Do not declare success until HTTPS, FPM, database connectivity, logs and a non-empty recoverable backup are all proven.
01

instruction

Patch and inventory the host

caution

Apply supported security updates, confirm time synchronization, and record listening services before adding the stack.

Why this step matters

Patch and inventory the host is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Apply supported security updates, confirm time synchronization, and record listening services before adding the stack. 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 “Patch and inventory the host”. The scope must remain limited to the selected application and supported host.

Syntax explained

apt
Invokes the principal tool or configuration operation for “Patch and inventory the host”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
&&
Runs the following operation only when the previous command succeeds, preventing a reload or state transition after a failed validation.
Command
sudo apt update && sudo apt upgrade --yes; timedatectl status; sudo ss -lntup
Example output / evidence
The host is current, synchronized, and its listener baseline is recorded.

Checkpoint: Checkpoint: Patch and inventory the host

Continue whenThe host is current, synchronized, and its listener baseline is 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 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 “Patch and inventory the host” 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.
02

command

Install Apache, MariaDB, PHP-FPM, and required modules

caution

Use Ubuntu packages and PHP-FPM rather than embedding mod_php in Apache.

Why this step matters

Install Apache, MariaDB, PHP-FPM, and required modules is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Use Ubuntu packages and PHP-FPM rather than embedding mod_php in Apache. 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 Apache, MariaDB, PHP-FPM, and required modules”. The scope must remain limited to the selected application and supported host.

Syntax explained

apt
Invokes the principal tool or configuration operation for “Install Apache, MariaDB, PHP-FPM, and required modules”; confirm that it resolves to the expected packaged executable or file before using elevated privileges.
Command
sudo apt install --yes apache2 mariadb-server php-fpm php-cli php-mysql php-opcache php-curl php-mbstring php-xml php-zip
Example output / evidence
Apache, MariaDB, and the Ubuntu PHP branch install.

Checkpoint: Checkpoint: Install Apache, MariaDB, PHP-FPM, and required modules

Continue whenApache, MariaDB, and the Ubuntu PHP branch 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 Apache, MariaDB, PHP-FPM, and required modules” 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

command

Create the application identity and release layout

caution

Separate immutable releases, shared writable data, and public document root under an application account.

Why this step matters

Create the application identity and release layout is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Separate immutable releases, shared writable data, and public document root under an application account. 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 the application identity and release layout”. The scope must remain limited to the selected application and supported host.

Syntax explained

useradd
Invokes the principal tool or configuration operation for “Create the application identity and release layout”; 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/3 ready

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

sudo useradd --system --home-dir /srv/{{app}} --shell /usr/sbin/nologin {{appUser}}; sudo install -d -o {{appUser}} -g {{appGroup}} -m 0750 /srv/{{app}}/{releases,shared}; sudo install -d -o {{appUser}} -g {{appGroup}} -m 0755 /srv/{{app}}/current/public
Example output / evidence
Application directories have explicit ownership and no shell login.

Checkpoint: Checkpoint: Create the application identity and release layout

Continue whenApplication directories have explicit ownership and no shell login. 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 useradd 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 the application identity and release layout” 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 useradd 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

command

Create a least-privilege database principal

caution

Create the database, local-only account, and application privileges with a generated secret supplied interactively or by a protected client option file.

Why this step matters

Create a least-privilege database principal is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Create the database, local-only account, and application privileges with a generated secret supplied interactively or by a protected client option file. 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 least-privilege database principal”. The scope must remain limited to the selected application and supported host.

Syntax explained

mariadb
Invokes the principal tool or configuration operation for “Create a least-privilege database principal”; 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/3 ready

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

sudo mariadb --protocol=socket -e "CREATE DATABASE \`{{database}}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER '{{dbUser}}'@'localhost' IDENTIFIED BY '{{dbPassword}}'; GRANT ALL PRIVILEGES ON \`{{database}}\`.* TO '{{dbUser}}'@'localhost';"
Example output / evidence
The application account has privileges only on its database.

Checkpoint: Checkpoint: Create a least-privilege database principal

Continue whenThe application account has privileges only on its database. 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 mariadb 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 least-privilege database principal” 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 mariadb 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

Create a dedicated PHP-FPM pool

caution

Run application code under the application identity and expose a private Unix socket to Apache.

Why this step matters

Create a dedicated PHP-FPM pool is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Run application code under the application identity and expose a private Unix socket to Apache. 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 dedicated PHP-FPM pool”. The scope must remain limited to the selected application and supported host.

Syntax explained

sudoedit
Invokes the principal tool or configuration operation for “Create a dedicated PHP-FPM pool”; 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/php/{{phpVersion}}/fpm/pool.d/{{app}}.conf
Is the persistent configuration boundary changed by this step. Keep an attributable backup and validate the consumer before reload.
File /etc/php/{{phpVersion}}/fpm/pool.d/{{app}}.conf
Configuration
Fill variables0/2 ready

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

sudoedit /etc/php/{{phpVersion}}/fpm/pool.d/{{app}}.conf; sudo php-fpm{{phpVersion}} -tt
Example output / evidence
The pool validates with a unique socket and bounded children.

Checkpoint: Checkpoint: Create a dedicated PHP-FPM pool

Continue whenThe pool validates with a unique socket and bounded children. 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 a dedicated PHP-FPM pool” 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.
06

config

Configure the Apache virtual host

caution

Set ServerName, document root, least-privilege Directory rules, logs, and a FilesMatch proxy to the application FPM socket.

Why this step matters

Configure the Apache virtual host is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Set ServerName, document root, least-privilege Directory rules, logs, and a FilesMatch proxy to the application FPM socket. 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 “Configure the Apache virtual host”. The scope must remain limited to the selected application and supported host.

Syntax explained

a2enmod
Invokes the principal tool or configuration operation for “Configure the Apache virtual host”; 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/apache2/sites-available/{{domain}}.conf
Is the persistent configuration boundary changed by this step. Keep an attributable backup and validate the consumer before reload.
File /etc/apache2/sites-available/{{domain}}.conf
Configuration
Fill variables0/1 ready

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

sudo a2enmod proxy_fcgi setenvif rewrite headers ssl; sudoedit /etc/apache2/sites-available/{{domain}}.conf; sudo a2ensite {{domain}}; sudo a2dissite 000-default
Example output / evidence
Only the named site is enabled and PHP requests target its pool.

Checkpoint: Checkpoint: Configure the Apache virtual host

Continue whenOnly the named site is enabled and PHP requests target its pool. 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 a2enmod 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 “Configure the Apache virtual host” 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 a2enmod 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

command

Restrict network access and issue TLS

caution

Expose SSH from the management network plus HTTP/HTTPS, then issue a certificate only after DNS is correct.

Why this step matters

Restrict network access and issue TLS is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Expose SSH from the management network plus HTTP/HTTPS, then issue a certificate only after DNS is correct. 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 “Restrict network access and issue TLS”. The scope must remain limited to the selected application and supported host.

Syntax explained

ufw
Invokes the principal tool or configuration operation for “Restrict network access and issue TLS”; 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 ufw allow from {{managementCidr}} to any port 22 proto tcp; sudo ufw allow 'Apache Full'; sudo ufw --force enable; sudo certbot --apache -d {{domain}} --redirect
Example output / evidence
Firewall rules are explicit and the site has a valid HTTPS certificate.

Checkpoint: Checkpoint: Restrict network access and issue TLS

Continue whenFirewall rules are explicit and the site has a valid HTTPS certificate. 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 ufw 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 “Restrict network access and issue TLS” 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 ufw 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

verification

Validate and restart the complete stack

caution

Test FPM, Apache, and database connectivity before a controlled restart; remove any temporary phpinfo file.

Why this step matters

Validate and restart the complete stack is a separate checkpoint because it establishes one auditable part of deploy a maintainable linux, apache, mariadb, and php application baseline without exposing database or sample endpoints. Performing it independently keeps a failed prerequisite, syntax error, or compatibility mismatch from being hidden by later actions.

What to understand

Test FPM, Apache, and database connectivity before a controlled restart; remove any temporary phpinfo file. 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 and restart the complete stack”. The scope must remain limited to the selected application and supported host.

Syntax explained

php-fpm
Invokes the principal tool or configuration operation for “Validate and restart the complete stack”; 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 php-fpm{{phpVersion}} -tt; sudo apachectl configtest; sudo mariadb-admin ping; sudo systemctl restart mariadb php{{phpVersion}}-fpm apache2; rm -f /srv/{{app}}/current/public/phpinfo.php
Example output / evidence
All services restart and no phpinfo endpoint remains.

Checkpoint: Checkpoint: Validate and restart the complete stack

Continue whenAll services restart and no phpinfo endpoint remains. 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-fpm 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 and restart the complete stack” 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-fpm 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

Application and TLScurl --fail --silent --show-error https://{{domain}}/health && openssl s_client -connect {{domain}}:443 -servername {{domain}} </dev/null 2>/dev/null | openssl x509 -noout -datesThe application health endpoint succeeds and certificate dates are valid.
Database and backupMYSQL_PWD='{{dbPassword}}' mariadb -h localhost -u {{dbUser}} {{database}} -e 'SELECT CURRENT_USER(), DATABASE();'; sudo mariadb-dump --single-transaction {{database}} | gzip -c | test -s /dev/stdinThe application connects locally and a logical backup stream is non-empty.

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 nginx when its event model and existing operations standards are a better fit; do not run both on the same public ports accidentally.
  • Use a managed database only when private connectivity, TLS identity, backup restoration and responsibility boundaries are documented.

Operate it safely

  • Automate signed releases, database backup restoration tests, certificate renewal checks and resource alerts.
  • Add application-specific headers, upload limits, slow-request tracing and dependency health without exposing sensitive diagnostics.

Recovery

Rollback

Restore the prior Apache site, FPM pool, database grant, and application release independently.

  1. Switch the current application release symlink back and reload Apache/FPM after syntax tests.
  2. Restore the database from the pre-change backup only if schema rollback requires it.
  3. Re-enable the prior site and firewall rules from the saved baseline; do not expose MariaDB publicly.

Evidence

Sources and review

Verified 2026-07-25Review due 2026-10-23
Ubuntu Server: install PHPofficialApache HTTP Server 2.4 documentationofficialMariaDB Server installation guideofficialPHP-FPM installation and configurationofficial