โก Quick Start - Complete LAMP Stack
# Install everything for a complete LAMP stack
sudo dnf install -y httpd mod_ssl php php-mysqlnd php-fpm php-gd php-mbstring php-xml php-json php-opcache python3-mod_wsgi mod_perl mod_perl-devel && sudo systemctl enable --now httpd php-fpm && sudo setsebool -P httpd_can_network_connect_db on && sudo firewall-cmd --permanent --add-service={http,https} && sudo firewall-cmd --reload
๐ System Information
Target Systems
- Oracle Enterprise Linux 8 (OEL8)
- Red Hat Enterprise Linux 8 (RHEL8)
- CentOS 8 / Rocky Linux 8 / AlmaLinux 8
- Apache httpd 2.4.x (default in RHEL8)
- PHP 7.x / 8.x (from AppStream)
๐ Database Compatibility Note
Good News: The PHP configuration works identically for both MariaDB and MySQL!
- Socket path is the same:
/var/lib/mysql/mysql.sock - php-mysqlnd driver works with both databases
- PDO and MySQLi connections are identical
- No special configuration needed to switch between them
๐ฆ Required Packages
Apache Core
httpd
Apache HTTP Server 2.4
httpd-tools
Tools: htpasswd, ab (benchmarking), etc.
mod_ssl
SSL/TLS module for HTTPS
httpd-manual
Apache documentation (optional)
PHP Integration (FastCGI/PHP-FPM)
php
PHP scripting language
php-fpm
FastCGI Process Manager (preferred over mod_php)
php-cli
Command-line PHP interpreter
php-common
Common files for PHP
php-mysqlnd
MySQL Native Driver (works with MySQL & MariaDB!)
php-pdo
PHP Data Objects abstraction layer
php-gd
GD graphics library
php-mbstring
Multi-byte string support
php-xml
XML support
php-json
JSON support
php-opcache
OPcache accelerator for performance
php-intl
Internationalization extension
php-bcmath
BC Math (arbitrary precision)
php-zip
ZIP archive support
php-curl
cURL support for HTTP requests
Python Integration (mod_wsgi)
python3-mod_wsgi
Apache module for Python 3 WSGI apps
python3
Python 3 interpreter
python3-pip
Python package installer
Perl Integration (mod_perl)
mod_perl
Embedded Perl interpreter for Apache
mod_perl-devel
Development files for mod_perl
perl
Perl interpreter (pre-installed)
๐ง Installation Steps
Complete Installation
One Command Install - Full LAMP Stack
sudo dnf install -y \
httpd \
httpd-tools \
mod_ssl \
php \
php-fpm \
php-cli \
php-common \
php-mysqlnd \
php-pdo \
php-gd \
php-mbstring \
php-xml \
php-json \
php-opcache \
php-intl \
php-bcmath \
php-zip \
php-curl \
python3-mod_wsgi \
python3-pip \
mod_perl \
mod_perl-devel
Enable and Start Services
Service Management
# Enable Apache and PHP-FPM to start on boot
sudo systemctl enable httpd
sudo systemctl enable php-fpm
# Start both services
sudo systemctl start httpd
sudo systemctl start php-fpm
# Check status
sudo systemctl status httpd
sudo systemctl status php-fpm
# Verify Apache is listening
sudo ss -tlnp | grep httpd
sudo ss -tlnp | grep php-fpm
Configure Firewall
Firewall Configuration
# Allow HTTP and HTTPS
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --list-all
Configure SELinux (CRITICAL!)
SELinux Configuration
# Allow httpd to connect to databases (MySQL/MariaDB)
sudo setsebool -P httpd_can_network_connect on
sudo setsebool -P httpd_can_network_connect_db on
# Allow httpd to send email (if needed)
sudo setsebool -P httpd_can_sendmail on
# For custom document root
# sudo semanage fcontext -a -t httpd_sys_content_t "/custom/path(/.*)?"
# sudo restorecon -Rv /custom/path
# For writable directories (uploads, cache)
# sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/uploads(/.*)?"
# sudo restorecon -Rv /var/www/html/uploads
# Check status
getenforce
getsebool -a | grep httpd
โ ๏ธ #1 LAMP Stack Problem: SELinux
If PHP can't connect to your database, it's almost always because httpd_can_network_connect_db is not enabled. This is THE most common issue at 2 AM. Enable it!
โ๏ธ Apache httpd Configuration
Main Apache Configuration (Key Settings)
/etc/httpd/conf/httpd.conf
# Apache httpd.conf - Essential Settings
# Full file is longer - these are the key edits
ServerRoot "/etc/httpd"
Listen 80
### Performance Tuning ###
Timeout 60
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
### MPM Event (default - best performance) ###
<IfModule mpm_event_module>
ServerLimit 16
StartServers 3
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 1000
</IfModule>
### Security ###
ServerTokens Prod
ServerSignature Off
TraceEnable Off
### User/Group ###
User apache
Group apache
### Server Info ###
ServerAdmin root@localhost
ServerName localhost:80
# Change to your domain: ServerName www.example.com:80
### Document Root ###
DocumentRoot "/var/www/html"
### Directory Permissions ###
<Directory />
AllowOverride none
Require all denied
</Directory>
<Directory "/var/www/html">
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
### Directory Index ###
<IfModule dir_module>
DirectoryIndex index.html index.php index.py index.pl
</IfModule>
### Logging ###
ErrorLog "logs/error_log"
LogLevel warn
CustomLog "logs/access_log" combined
### Include Additional Config Files ###
IncludeOptional conf.d/*.conf
๐ PHP-FPM Configuration
PHP-FPM Pool Configuration
/etc/php-fpm.d/www.conf
; PHP-FPM www Pool - Key Settings
[www]
user = apache
group = apache
; Unix socket (best performance)
listen = /run/php-fpm/www.sock
listen.owner = apache
listen.group = apache
listen.mode = 0660
; Process Management
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500
; Monitoring
pm.status_path = /php-fpm-status
ping.path = /php-fpm-ping
; Slow log
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 3s
Apache PHP-FPM Proxy Config
/etc/httpd/conf.d/php-fpm.conf
# Proxy PHP requests to PHP-FPM
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
</FilesMatch>
# Status pages (monitoring)
<LocationMatch "^/(php-fpm-(status|ping))$">
SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost"
Require local
</LocationMatch>
# Security: Deny PHP in uploads
<Directory "/var/www/html/uploads">
<FilesMatch "\.php$">
Require all denied
</FilesMatch>
</Directory>
PHP Configuration (Essential Settings)
/etc/php.ini
; PHP Configuration - Key Settings Only
; Error Handling
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
log_errors = On
error_log = /var/log/php-fpm/error.log
; Resource Limits
max_execution_time = 30
max_input_time = 60
memory_limit = 128M
; File Uploads
file_uploads = On
upload_max_filesize = 64M
post_max_size = 64M
max_file_uploads = 20
; Database - Works for MySQL AND MariaDB!
mysqli.default_socket = /var/lib/mysql/mysql.sock
pdo_mysql.default_socket = /var/lib/mysql/mysql.sock
; Timezone (CHANGE THIS!)
date.timezone = America/New_York
; OPcache (Performance)
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 4000
; Security
expose_php = Off
allow_url_include = Off
default_charset = "UTF-8"
๐ Python WSGI Configuration
mod_wsgi Configuration
/etc/httpd/conf.d/wsgi.conf
# Python WSGI Configuration
LoadModule wsgi_module modules/mod_wsgi_python3.so
# Simple test application
WSGIScriptAlias /python /var/www/wsgi-scripts/test.wsgi
<Directory /var/www/wsgi-scripts>
Require all granted
</Directory>
Example Python WSGI Script
/var/www/wsgi-scripts/test.wsgi
#!/usr/bin/python3
# Simple WSGI test with database
def application(environ, start_response):
status = '200 OK'
output = b'Python WSGI Test\n'
import sys
output += f'Python: {sys.version}\n'.encode()
# Test database (works with MySQL or MariaDB!)
try:
import pymysql
conn = pymysql.connect(
host='localhost',
user='webapp_user',
password='YourPassword',
database='webapp_db'
)
cursor = conn.cursor()
cursor.execute("SELECT VERSION()")
ver = cursor.fetchone()
output += f'Database: {ver[0]}\n'.encode()
conn.close()
except Exception as e:
output += f'DB Error: {e}\n'.encode()
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
return [output]
๐ช Perl mod_perl Configuration
mod_perl Configuration
/etc/httpd/conf.d/perl.conf
# Perl mod_perl Configuration
LoadModule perl_module modules/mod_perl.so
Alias /perl /var/www/perl
<Directory /var/www/perl>
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions +ParseHeaders
Options +ExecCGI
Require all granted
</Directory>
<FilesMatch "\.pl$">
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions +ParseHeaders
Options +ExecCGI
</FilesMatch>
Example Perl CGI Script
/var/www/perl/test.pl
#!/usr/bin/perl
use strict;
use warnings;
use CGI;
use DBI;
my $q = CGI->new;
print $q->header('text/plain');
print "Perl mod_perl Test\n";
print "Perl: $]\n\n";
# Test database (works with MySQL or MariaDB!)
eval {
my $dbh = DBI->connect(
"DBI:mysql:database=webapp_db;host=localhost",
"webapp_user",
"YourPassword",
{RaiseError=>1}
);
my $ver = $dbh->selectrow_array("SELECT VERSION()");
print "Database: $ver\n";
$dbh->disconnect();
};
print "DB Error: $@\n" if $@;
๐งช Testing the Stack
Create Test Files
Setup Test Environment
# Create directories
sudo mkdir -p /var/www/{wsgi-scripts,perl}
sudo chmod 755 /var/www/{wsgi-scripts,perl}
# PHP test (with database check)
cat << 'EOF' | sudo tee /var/www/html/test.php
<?php
echo "PHP Version: " . phpversion() . "\n";
// Test database connection (MySQL or MariaDB!)
$conn = new mysqli('localhost', 'webapp_user', 'YourPass', 'webapp_db');
if ($conn->connect_error) die("Failed: " . $conn->connect_error);
echo "Database: Connected!\n";
$result = $conn->query("SELECT VERSION()");
$row = $result->fetch_assoc();
echo "DB Version: " . $row['VERSION()'] . "\n";
$conn->close();
?>
EOF
# Python WSGI test (see example above)
sudo vi /var/www/wsgi-scripts/test.wsgi
sudo chmod 755 /var/www/wsgi-scripts/test.wsgi
# Perl test (see example above)
sudo vi /var/www/perl/test.pl
sudo chmod 755 /var/www/perl/test.pl
# Set SELinux contexts
sudo restorecon -Rv /var/www
# Test in browser:
# http://your-server/test.php
# http://your-server/python
# http://your-server/perl/test.pl
๐ Troubleshooting
Common Issues
| Problem | Solution |
|---|---|
| PHP files download instead of execute | PHP-FPM not configuredsudo systemctl restart httpd php-fpm |
| PHP can't connect to database | SELinux!sudo setsebool -P httpd_can_network_connect_db on |
| Permission denied errors | sudo chown -R apache:apache /var/www/htmlsudo restorecon -Rv /var/www |
| 403 Forbidden | Check Directory permissions in httpd.conf Check SELinux contexts |
| Apache won't start | sudo apachectl configtestsudo tail /var/log/httpd/error_log |
Useful Commands
# Test Apache configuration
sudo apachectl configtest
# View loaded modules
httpd -M
# Check syntax of individual conf files
sudo apachectl -t -D DUMP_VHOSTS
# Restart services
sudo systemctl restart httpd php-fpm
# View logs in real-time
sudo tail -f /var/log/httpd/error_log
sudo tail -f /var/log/php-fpm/error.log
# Check SELinux denials
sudo ausearch -m avc -ts recent | grep httpd
# List SELinux booleans for httpd
getsebool -a | grep httpd
# Check file contexts
ls -Z /var/www/html
โก Performance & Security
Enable Compression
/etc/httpd/conf.d/compression.conf
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE text/css text/javascript
AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>
Security Headers
/etc/httpd/conf.d/security.conf
# Security headers
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# HSTS (only after SSL is working!)
# Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
โ Best Practices Summary
๐ก LAMP Stack Best Practices
- SELinux: Configure it, don't disable it!
- PHP-FPM: Use Unix sockets, not TCP (better performance)
- Database: php-mysqlnd works with both MySQL and MariaDB
- OPcache: Always enable it for PHP performance
- SSL/TLS: Use Let's Encrypt (free, automated)
- Firewall: Only open ports 80/443
- Logs: Monitor them regularly
- Updates: Keep everything patched
- Backups: Automate them!
- Security: Disable directory listing, hide PHP version
โ ๏ธ Don't Forget!
- Enable
httpd_can_network_connect_dbSELinux boolean - Start both httpd AND php-fpm services
- Open firewall ports (80, 443)
- Set correct file ownership (apache:apache)
- Test configuration before reloading:
apachectl configtest
๐ Important Files & Logs
Configuration Files
/etc/httpd/conf/httpd.conf # Main Apache config
/etc/httpd/conf.d/*.conf # Additional configs
/etc/httpd/conf.modules.d/*.conf # Module configs
/etc/php.ini # PHP config
/etc/php-fpm.d/www.conf # PHP-FPM pool config
Log Files
/var/log/httpd/access_log # Apache access log
/var/log/httpd/error_log # Apache error log
/var/log/httpd/ssl_access_log # SSL access log
/var/log/httpd/ssl_error_log # SSL error log
/var/log/php-fpm/error.log # PHP-FPM errors
/var/log/php-fpm/www-slow.log # Slow PHP scripts