SEO Metadata
SEO Title Options
- How to Configure a LAMP Stack on Ubuntu: Apache, MySQL
- How to Configure a LAMP Stack on Ubuntu: Practical 2026
- Web Server Playbook: How to Configure a LAMP Stack on
Meta Description Options
- Learn How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning with a practical Web Server framework, expert mistakes, implementation steps.
- Full LAMP installation and optimization: MPM worker tuning, MySQL InnoDB settings, PHP-FPM pools, and .htaccess security hardening.
URL Slug
how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning
Focus Keyword
How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning
Additional LSI Keywords
- Web Server
- LAMP
- Ubuntu
- Apache
- MySQL
- PHP-FPM
- How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning
- production checklist
- implementation guide
- best practices
- architecture decisions
- testing strategy
Table of Contents
- Article overview
- What How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning means
- Why it matters now
- Implementation framework
- Practical comparison
- Expert workflow
- Common mistakes
- Media and link plan
- Original technical deep dive
- FAQ
- Structured data
- Conclusion
Article overview
How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning is the kind of topic that looks simple until it reaches production. Teams usually discover the real cost late: unclear boundaries, weak defaults, hidden maintenance work, and decisions that seemed harmless when the codebase was small.
The problem gets worse when the article, tutorial, or implementation guide only explains the happy path. This guide closes that gap with a practical framework, a comparison table, common mistakes, and a deep technical section you can use while planning real work.
Keep reading for the non-obvious part: the safest implementation is rarely the most impressive-looking one. It is the one your team can debug, test, document, and evolve without turning every future change into archaeology.
Key Takeaways
- How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning should be evaluated as a production decision, not only as a syntax or tooling choice.
- The best implementation keeps responsibilities visible, with clear ownership, tests, documentation, and rollback paths.
- Search visibility improves when practical depth, structured answers, and expert examples live on the same page.
[IMAGE: A mobile-first technical article layout showing the main concept, decision table, implementation checklist, and FAQ blocks. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning expert guide for Web Server]
What How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning means
How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning means applying web server knowledge to a concrete engineering decision, then turning that decision into reliable code, documentation, and operational behavior. In practice, it combines the topic's core concepts with trade-off analysis, implementation boundaries, testing strategy, and maintenance discipline.
This is the definition worth optimizing for featured snippets because it avoids hype. It tells the reader what the topic does and what a professional implementation must include.
Why it matters now
The technical web is more crowded than it was a few years ago. Thin tutorials can still get indexed, but they rarely earn trust from senior developers, buyers, AI answer systems, or teams that need production guidance.
For web server topics, the strongest content now has three layers:
- a clear answer for fast scanning
- a practical framework for implementation
- expert context that explains what breaks later
That same structure helps search engines understand the page. It also helps readers decide whether the advice fits their project.
Implementation framework
Use this framework before adopting the approach described in this article.
- Define the user problem and the production risk.
- Identify the smallest reliable implementation boundary.
- Keep configuration, secrets, and environment-specific behavior outside the article's core logic.
- Add tests for the behavior that would hurt if it regressed.
- Document the trade-off, not only the final code.
- Measure the result with logs, metrics, or user-facing outcomes.
- Revisit the decision after real usage exposes edge cases.
The sequence is deliberately conservative. It keeps the work grounded in outcomes instead of novelty.
[IMAGE: A seven-step implementation framework with discovery, boundary design, configuration, tests, documentation, measurement, and iteration. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning implementation framework]
Practical comparison
| Decision area | Strong approach | Weak approach | Why it matters |
|---|---|---|---|
| Scope | Solve one clear problem | Mix unrelated concerns | Focus improves testing and search intent |
| Architecture | Put logic in explicit classes or documented boundaries | Hide behavior in templates or incidental callbacks | Future changes stay easier to review |
| Data flow | Pass prepared data into the view or endpoint | Query or compute in presentation code | Reduces regressions and performance surprises |
| Testing | Cover the risky behavior directly | Test only the happy path | Catches production failures earlier |
| Documentation | Explain trade-offs and limits | Repeat generic definitions | Builds E-E-A-T and reader trust |
| Operations | Track logs, metrics, and rollback steps | Ship without measurement | Makes the decision reversible |
This table is intentionally practical. It gives a reviewer something to check before the implementation becomes expensive to change.
Expert workflow
Expert tip: "Treat How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning as a system boundary. If the next developer cannot find where the decision lives, how it is tested, and when it should be avoided, the implementation is not finished."
A useful workflow is simple:
- Start with the smallest working example.
- Add the constraints that exist in your real project.
- Remove anything that only demonstrates cleverness.
- Write down the failure modes.
- Add links to related decisions so future readers can navigate the topic cluster.
That last point matters for both humans and search systems. A single article can answer a question; a cluster proves authority.
Common mistakes
Mistake 1: Copying a pattern without its context
A pattern that works in a small demo can fail in a real application. The missing context is usually data volume, team experience, deployment process, security requirements, or observability.
Before copying the pattern, ask what assumption made it safe in the original example.
Mistake 2: Putting business logic in the wrong layer
This is the fastest way to make future debugging expensive. In Laravel, PHP, and server-rendered websites, presentation should receive prepared data, not discover rules on its own.
Keep decision logic in models, actions, services, policies, requests, jobs, or documented helpers where it can be tested directly.
Mistake 3: Optimizing for novelty instead of maintainability
Newer tools and language features can be valuable. They can also hide simple behavior behind unfamiliar syntax.
Use the option that makes the next production incident easier to understand.
Mistake 4: Publishing without a measurement plan
If the article describes a performance, SEO, security, or architecture improvement, define how success will be checked. Logs, tests, crawl diagnostics, analytics, and user behavior are all stronger than assumptions.
[IMAGE: A common-mistakes board with context loss, wrong layer, novelty bias, and missing measurement highlighted. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning common mistakes]
Media and link plan
Image placeholders
- [IMAGE: A concept diagram for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning with input, decision boundary, implementation, tests, and production feedback. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning concept diagram]
- [IMAGE: A mobile screenshot-style checklist for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning mobile checklist]
- [IMAGE: A comparison table visualization for strong versus weak implementation choices. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning comparison table]
Video placeholder
[VIDEO: Insert a 5-8 minute YouTube walkthrough that demonstrates the main decision, the implementation boundary, the test strategy, and the production caveats for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning.]
Trustworthy outbound links
- PHP manual - use this as the trust reference for language-level reference.
- Google Search quality guidance - use this as the trust reference for people-first content and E-E-A-T alignment.
Internal linking opportunities
- Internal guide: Configuring Nginx on Ubuntu 20.04: Virtual - use this when readers need a related Web Server follow-up.
- Internal guide: SQL vs NoSQL in PHP Apps: When to Use MySQL - use this when readers need a related Database follow-up.
Original Technical Deep Dive
The short version
A usable LAMP stack is easy:
sudo apt update
sudo apt install apache2 mysql-server php php-fpm php-mysql
A production-ready LAMP stack needs more care:
- Apache should serve static files and pass PHP to PHP-FPM.
- Use
mpm_eventormpm_workerwith PHP-FPM, notmod_phpwithmpm_prefork. - Tune Apache concurrency against available memory.
- Tune PHP-FPM workers against real process memory.
- Keep MySQL bound to localhost unless it is intentionally remote.
- Size InnoDB memory after the OS, Apache, and PHP-FPM have budget.
- Put most Apache rules in virtual host config, not
.htaccess, when you control the server. - Enable HTTPS, security headers, backups, logs, and monitoring before calling the stack finished.
This guide uses Ubuntu-style paths and commands. It was originally framed for Ubuntu 20.04-era servers, where PHP 7.4 was common. The current examples use generic package names and PHP-FPM patterns that also fit modern Ubuntu LTS releases. Check the exact PHP version installed by your Ubuntu release before copying versioned paths.
Install the base packages
Start with an updated server:
sudo apt update
sudo apt upgrade -y
Install Apache, MySQL, PHP-FPM, and common PHP extensions:
sudo apt install -y \
apache2 \
mysql-server \
php \
php-fpm \
php-cli \
php-mysql \
php-curl \
php-mbstring \
php-xml \
php-zip \
php-intl \
php-gd \
php-opcache
Check services:
systemctl status apache2 --no-pager
systemctl status mysql --no-pager
systemctl status 'php*-fpm' --no-pager
Check versions:
apache2 -v
mysql --version
php -v
php-fpm* -v 2>/dev/null || true
On Ubuntu, MySQL usually starts automatically after installation. Apache usually starts automatically too.
Open the firewall
If UFW is enabled, allow HTTP and HTTPS:
sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'
sudo ufw status verbose
If you prefer explicit ports:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Do not open MySQL to the internet:
sudo ufw deny 3306/tcp
If a separate application server must reach MySQL, allow only that private source IP:
sudo ufw allow from 10.0.0.20 to any port 3306 proto tcp
Prefer Apache plus PHP-FPM
Old LAMP tutorials often install libapache2-mod-php, which runs PHP inside Apache worker processes and usually forces the prefork MPM. That is simple, but it couples Apache concurrency to PHP memory and removes the main benefit of threaded Apache MPMs.
For a modern server, use Apache as the HTTP server and PHP-FPM as the PHP process manager.
Enable the Apache modules PHP-FPM needs:
sudo a2enmod proxy proxy_fcgi setenvif rewrite headers
Disable mod_php if it is installed and enabled. The exact module name depends on PHP version:
apache2ctl -M | grep php
If you see a PHP module, disable it:
sudo a2dismod php8.3 2>/dev/null || true
sudo a2dismod php8.2 2>/dev/null || true
sudo a2dismod php8.1 2>/dev/null || true
sudo a2dismod php7.4 2>/dev/null || true
Enable the PHP-FPM Apache config if Ubuntu created one:
ls /etc/apache2/conf-available/*fpm*.conf
Example:
sudo a2enconf php8.3-fpm
Then reload:
sudo apache2ctl configtest
sudo systemctl reload apache2
Use the event MPM when possible
Check the active MPM:
apache2ctl -M | grep mpm
For PHP-FPM setups, mpm_event is usually the best Apache default on modern Ubuntu servers:
sudo a2dismod mpm_prefork
sudo a2enmod mpm_event
sudo apache2ctl configtest
sudo systemctl restart apache2
If your package state prevents enabling mpm_event, check whether mod_php is still enabled. mod_php and threaded MPMs are not the combination you want for this stack.
[IMAGE: Supporting visual 1 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 1]
[IMAGE: Supporting visual 1 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 1]
mpm_worker can also be valid. The tuning concepts are similar: Apache has processes, threads, and a maximum number of request workers. PHP execution still happens in PHP-FPM.
Create a virtual host
Use a web root that points to public, not the project root:
sudo mkdir -p /var/www/example.com/public
sudo chown -R deploy:www-data /var/www/example.com
sudo chmod -R 2750 /var/www/example.com
Create a test file:
printf '%s\n' '<?php echo "ok\n";' | sudo tee /var/www/example.com/public/index.php
Create the vhost:
sudo nano /etc/apache2/sites-available/example.com.conf
Config:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
<Directory /var/www/example.com/public>
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<FilesMatch "\.php$">
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost/"
</FilesMatch>
<FilesMatch "^\.">
Require all denied
</FilesMatch>
</VirtualHost>
Adjust the socket path:
ls /run/php/
You may see:
php8.3-fpm.sock
php8.3-fpm.pid
Enable the site:
sudo a2ensite example.com
sudo a2dissite 000-default
sudo apache2ctl configtest
sudo systemctl reload apache2
Test locally from the server:
curl -i http://127.0.0.1/ -H 'Host: example.com'
Safer rewrite rules without .htaccess
If you control the server, put rewrites in the vhost instead of .htaccess.
For a front-controller PHP app:
<Directory /var/www/example.com/public>
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
</Directory>
This avoids per-request .htaccess filesystem checks and keeps the effective configuration in one place.
If the application requires .htaccess, allow only the override class it needs:
<Directory /var/www/example.com/public>
Options -Indexes +FollowSymLinks
AllowOverride FileInfo
Require all granted
</Directory>
Avoid this unless you have a clear reason:
AllowOverride All
AllowOverride All lets .htaccess files control much more than rewrites. It is convenient, but it expands what writable application directories can do to your server behavior.
Harden public file access
Keep sensitive directories outside public.
Good:
/var/www/example.com/
app/
storage/
vendor/
.env
public/
index.php
assets/
Bad:
/var/www/example.com/public/
app/
vendor/
storage/
.env
index.php
Add defense-in-depth denies for common sensitive files:
<FilesMatch "(^\.|composer\.(json|lock)|package(-lock)?\.json|phpunit\.xml|\.env)">
Require all denied
</FilesMatch>
<DirectoryMatch "^/var/www/example.com/(app|vendor|storage|tests|config)">
Require all denied
</DirectoryMatch>
Do not rely on these rules to make a bad document root safe. Set the document root correctly first.
Enable HTTPS
Install Certbot:
sudo apt install -y certbot python3-certbot-apache
Issue and install a certificate:
sudo certbot --apache -d example.com -d www.example.com
Test renewal:
sudo certbot renew --dry-run
After TLS works, force HTTP to HTTPS. Certbot can do this interactively. If you write it manually:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>
Then configure the HTTPS vhost as the real app vhost.
Add baseline security headers
Add headers in the HTTPS vhost:
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
Only add HSTS after HTTPS is stable:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Do not blindly add preload. HSTS preload is a long-term browser-level commitment. It is wrong for many staging, subdomain-heavy, or inherited-domain setups.
A Content Security Policy is valuable, but it must match the app:
Header always set Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-ancestors 'self'; object-src 'none'"
That baseline may break inline scripts, third-party widgets, analytics, map embeds, and payment scripts. Test it in staging first, or start with Content-Security-Policy-Report-Only.
Apache MPM tuning
[IMAGE: Supporting visual 2 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 2]
Find the active MPM config:
ls /etc/apache2/mods-enabled/mpm_*.conf
For mpm_event, edit:
sudo nano /etc/apache2/mods-available/mpm_event.conf
Example for a small 2 GB server:
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 25
MaxSpareThreads 75
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 100
MaxConnectionsPerChild 5000
</IfModule>
The important number is MaxRequestWorkers. It caps simultaneous Apache request workers. For worker and event MPMs, it is the total thread count available to serve requests.
A simple memory budget:
available memory for Apache =
total RAM
- OS reserve
- MySQL budget
- PHP-FPM max children * average PHP child memory
- other services
Apache event workers are usually much smaller than PHP-FPM workers, but they are not free. Do not set MaxRequestWorkers to a huge number because a benchmark tool made it look good for static files.
[IMAGE: Supporting visual 2 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 2]
Check for saturation:
sudo tail -f /var/log/apache2/error.log
If Apache logs that it reached MaxRequestWorkers, you have three choices:
- raise the limit if memory allows it;
- reduce request time so workers free faster;
- add another server.
Raising concurrency without memory budget can push the server into swap and make every request slower.
PHP-FPM pool tuning
Find the active pool config:
ls /etc/php/*/fpm/pool.d/www.conf
Edit it:
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Small server baseline:
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 12
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
request_terminate_timeout = 60s
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm-slow.log
clear_env = yes
security.limit_extensions = .php
Calculate pm.max_children from real memory, not guesses.
Get average PHP-FPM process size under traffic:
ps -ylC php-fpm8.3 --sort:rss
Or:
ps -o rss= -C php-fpm8.3 | awk '{sum+=$1; count++} END {if (count) print int(sum/count/1024) " MB"}'
Formula:
pm.max_children =
memory available for PHP-FPM / average PHP-FPM child memory
Example:
Memory available for PHP-FPM: 900 MB
Average PHP child memory: 75 MB
pm.max_children: 12
Reload after changes:
sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
sudo systemctl reload apache2
Check for saturation:
sudo journalctl -u php8.3-fpm -f
If you see server reached pm.max_children, either:
- PHP workers need less time per request;
pm.max_childrenis too low for available memory;- traffic exceeds the server's capacity;
- slow external services are holding PHP workers open.
Do not raise pm.max_children until the memory math says the server can survive it.
Enable OPcache for web requests
Find the FPM php.ini:
php --ini
ls /etc/php/*/fpm/php.ini
Edit FPM config, not only CLI config:
sudo nano /etc/php/8.3/fpm/conf.d/10-opcache.ini
Baseline:
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.jit=off
For controlled deployments, you can disable timestamp validation and reload PHP-FPM on deploy:
opcache.validate_timestamps=0
Then your deployment must reload FPM:
sudo systemctl reload php8.3-fpm
If you cannot guarantee a reload on every deploy, leave timestamp validation on.
Production PHP settings
Create an app-specific FPM INI file:
sudo nano /etc/php/8.3/fpm/conf.d/99-production.ini
Baseline:
expose_php=0
display_errors=0
display_startup_errors=0
log_errors=1
error_log=/var/log/php_errors.log
memory_limit=256M
max_execution_time=30
max_input_time=30
post_max_size=32M
upload_max_filesize=32M
session.cookie_secure=1
session.cookie_httponly=1
session.cookie_samesite=Lax
Reload:
sudo systemctl reload php8.3-fpm
Verify through the web SAPI, not only CLI. CLI and FPM can load different php.ini files.
Temporary diagnostic file:
printf '%s\n' '<?php phpinfo();' | sudo tee /var/www/example.com/public/_phpinfo.php
Open it through the browser, confirm the FPM settings, then remove it:
sudo rm /var/www/example.com/public/_phpinfo.php
[IMAGE: Supporting visual 3 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 3]
Never leave phpinfo() public on production.
MySQL first secure setup
Ubuntu's MySQL package creates a local root account that commonly uses socket authentication. Connect with:
sudo mysql
Create an application database and user:
CREATE DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
CREATE USER 'app'@'localhost'
IDENTIFIED WITH caching_sha2_password
BY 'replace-with-a-long-random-password';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP
ON app.*
TO 'app'@'localhost';
FLUSH PRIVILEGES;
Use a narrower grant set if migrations run from a separate deployment account. The application runtime often does not need CREATE, ALTER, INDEX, or DROP.
Check MySQL network binding:
sudo ss -tap | grep mysql
A local-only database should show 127.0.0.1:mysql or a Unix socket, not 0.0.0.0:3306.
If needed, edit:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
Keep local binding:
bind-address = 127.0.0.1
Restart:
sudo systemctl restart mysql
MySQL InnoDB memory tuning
Before changing MySQL memory, decide the server role:
| Server role | InnoDB buffer pool starting point |
|---|---|
| Small all-in-one LAMP server | 25 to 40 percent of RAM |
| Dedicated MySQL server | 50 to 75 percent of RAM |
| Tiny 1 GB VPS | Be conservative; avoid starving PHP-FPM and the OS |
[IMAGE: Supporting visual 3 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 3]
For an all-in-one 4 GB VPS:
OS and filesystem cache: 700 MB
Apache: 200 MB
PHP-FPM: 1200 MB
MySQL buffer pool: 1200 MB
Other services and burst room: 700 MB
Create a local MySQL tuning file:
sudo nano /etc/mysql/mysql.conf.d/99-lamp.cnf
Example:
[mysqld]
innodb_buffer_pool_size = 1G
innodb_log_buffer_size = 64M
innodb_redo_log_capacity = 512M
max_connections = 80
table_open_cache = 2000
tmp_table_size = 64M
max_heap_table_size = 64M
slow_query_log = ON
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 0.5
log_queries_not_using_indexes = OFF
Notes:
innodb_buffer_pool_sizeis usually the most important InnoDB memory setting.innodb_log_buffer_sizehelps large transactions avoid extra redo log flushes.innodb_redo_log_capacityreplaced oldinnodb_log_file_sizestyle tuning in newer MySQL.max_connectionsmust fit memory. Each connection can allocate memory.- Slow query logging is for diagnosis. Watch disk usage.
Validate and restart:
sudo mysqld --validate-config
sudo systemctl restart mysql
If mysqld --validate-config is unavailable or unsuitable for your package, use:
sudo journalctl -u mysql -n 100 --no-pager
After restart:
sudo mysql -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size','innodb_redo_log_capacity','max_connections');"
Use mysqltuner carefully
Install:
sudo apt install -y mysqltuner
Run only after the server has handled representative traffic for at least a day:
sudo mysqltuner
Treat output as suggestions, not commands. mysqltuner does not know your deploy schedule, queue load, traffic spikes, backup window, or memory budget for PHP-FPM.
The best MySQL tuning is still query tuning:
- Add indexes that match real queries.
- Fix N+1 query patterns.
- Avoid loading giant result sets.
- Paginate by keyset for high-volume lists.
- Measure with
EXPLAINand the slow query log.
[IMAGE: Supporting visual 4 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 4]
Align Apache, PHP-FPM, and MySQL limits
Bad all-in-one setup:
Apache MaxRequestWorkers = 400
PHP-FPM pm.max_children = 100
MySQL max_connections = 500
RAM = 2 GB
That stack can promise far more concurrency than memory can pay for.
Better small setup:
Apache MaxRequestWorkers = 100
PHP-FPM pm.max_children = 12
MySQL max_connections = 80
RAM = 2 GB
The Apache number can be higher than PHP-FPM because Apache also serves static files and handles keep-alive connections. PHP-FPM is the expensive layer for dynamic PHP requests.
Watch the bottleneck:
| Symptom | Likely pressure |
|---|---|
Apache reaches MaxRequestWorkers | HTTP request concurrency, slow upstreams, too low Apache limit |
PHP-FPM reaches pm.max_children | Dynamic PHP requests exceed worker capacity |
MySQL hits max_connections | Too many app connections, no pooling, slow queries |
| System swaps | Memory limits are too high for the server |
| CPU pinned, low I/O wait | PHP CPU work, compression, TLS, MySQL CPU queries |
| High I/O wait | Disk, MySQL, logs, swap, backups |
Tune one layer at a time. If you change Apache, PHP-FPM, MySQL, OPcache, and application code together, you will not know which change mattered.
Add log rotation awareness
Ubuntu packages usually install logrotate rules for Apache, PHP-FPM, and MySQL. Verify them:
ls /etc/logrotate.d/
cat /etc/logrotate.d/apache2
cat /etc/logrotate.d/mysql-server 2>/dev/null || true
Check disk:
df -h
sudo du -sh /var/log/*
Slow query logs, access logs, and app logs can fill a small VPS quickly. A full disk can take down MySQL and make deployments fail.
[IMAGE: Supporting visual 4 for How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning, showing How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning decisions, examples, and LAMP, Ubuntu, Apache. Alt: How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning how-configure-lamp-stack-ubuntu-apache-mysql-php-tuning visual 4]
Add basic health checks
Apache config:
<VirtualHost *:80>
ServerName example.com
Alias /healthz /var/www/example.com/public/healthz.txt
</VirtualHost>
File:
echo ok | sudo tee /var/www/example.com/public/healthz.txt
Check:
curl -fsS http://127.0.0.1/healthz -H 'Host: example.com'
For PHP-FPM status, configure a private endpoint only:
pm.status_path = /fpm-status
ping.path = /fpm-ping
ping.response = pong
Then restrict access in Apache:
<LocationMatch "^/(fpm-status|fpm-ping)$">
Require ip 127.0.0.1
</LocationMatch>
Do not expose FPM status pages publicly.
Deployment checklist
Before putting traffic on the server:
[ ] Apache serves only the public document root.
[ ] PHP runs through PHP-FPM, not mod_php.
[ ] Active MPM is event or worker.
[ ] Apache config passes apache2ctl configtest.
[ ] PHP-FPM config passes php-fpm -t.
[ ] MySQL config validates or starts cleanly.
[ ] UFW allows only SSH, HTTP, and HTTPS publicly.
[ ] MySQL is bound to localhost or a private interface.
[ ] HTTPS works and renewal dry run passes.
[ ] Security headers are tested.
[ ] .htaccess is disabled or narrowly scoped.
[ ] OPcache is enabled for FPM.
[ ] PHP display_errors is off.
[ ] PHP memory_limit matches app needs.
[ ] PHP-FPM pm.max_children fits memory.
[ ] Apache MaxRequestWorkers fits memory.
[ ] MySQL InnoDB buffer pool fits memory.
[ ] Slow query log is enabled with disk monitoring.
[ ] Backups are configured and restore-tested.
[ ] Logs rotate.
[ ] Health checks work.
[ ] Monitoring alerts on CPU, memory, disk, 5xx, PHP-FPM saturation, MySQL errors.
FAQ
What is How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning?
How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning is a practical web server topic that should be evaluated through implementation scope, production risk, testing, documentation, and long-term maintainability.
When should a team use How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning?
Use How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning when it solves a real project constraint, improves clarity, or reduces operational risk. Avoid it when it only adds novelty or hides behavior from future maintainers.
What is the biggest risk with How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning?
The biggest risk is copying a pattern without its context. Production systems need clear boundaries, rollback options, tests, and observability before a technique becomes dependable.
How do you test How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning?
Test the smallest unit that owns the behavior, then add integration coverage for the path users or systems actually rely on. Include failure cases, configuration differences, and regression checks.
How does How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning affect SEO and AI search visibility?
It improves visibility when the article gives a direct answer, expert context, structured headings, internal links, trustworthy references, and FAQ content that matches the visible page.
Conclusion
How to Configure a LAMP Stack on Ubuntu: Apache, MySQL & PHP Tuning is worth doing when the implementation improves clarity, reliability, or delivery speed. It is not worth doing when it hides ownership, increases operational risk, or makes the system harder to explain.
Use the framework above as a review checklist. Then connect this topic to the rest of the project documentation so readers can move from concept to implementation without losing context.