Back to Home

LAMP Stack

Deploy Linux + Apache + MySQL + PHP on Debian 12

What is LAMP?

LAMP is a powerful, time-tested open-source web development platform that powers millions of websites worldwide. The acronym represents four key components that work together to create dynamic, database-driven web applications.

The Components

L - Linux
Debian 12 (Bookworm)

Stable, secure operating system

A - Apache
Apache 2.4

Industry-leading web server

M - MySQL
MySQL 8.0 / MariaDB 10.11

Robust database management

P - PHP
PHP 8.2

Dynamic content processing

Why LAMP?

  • Battle-Tested: Powers major platforms like WordPress, Drupal, and countless enterprise applications
  • Open Source: Completely free with strong community support and extensive documentation
  • Flexible: Suitable for everything from personal blogs to high-traffic e-commerce sites
  • Well-Supported: Extensive libraries, frameworks, and tools available
  • Performance: Optimized for speed with mature caching and optimization solutions

What You'll Build

By the end of this guide, you'll have a fully functional web server capable of:

  • Serving static and dynamic web content via Apache
  • Processing PHP scripts for dynamic page generation
  • Managing data with MySQL/MariaDB databases
  • Hosting multiple websites with virtual hosts
  • Running popular CMS platforms like WordPress or Drupal

Perfect For: This stack is ideal for web developers, system administrators, and anyone looking to host their own web applications. Whether you're building a personal blog, development environment, or production server, LAMP provides a solid foundation.

System Preparation

Before installing the LAMP stack components, we need to prepare the system and ensure it's up to date.

Update System Packages

First, update your package lists and upgrade existing packages:

# Update package lists
sudo apt update

# Upgrade installed packages
sudo apt upgrade -y

# Install essential tools
sudo apt install -y curl wget git nano software-properties-common apt-transport-https ca-certificates

Best Practice: Always update your system before installing new software to ensure you get the latest security patches and bug fixes.

Set System Hostname (Optional)

If you're setting up a production server, configure a proper hostname:

# Set hostname
sudo hostnamectl set-hostname lamp-server

# Edit hosts file
sudo nano /etc/hosts

# Add this line (replace with your IP):
# 192.168.1.100  lamp-server.local  lamp-server

Configure Firewall with iptables

Set up iptables for robust network security:

# Install iptables-persistent to save rules
sudo apt install -y iptables-persistent

# Flush existing rules (clean slate)
sudo iptables -F
sudo iptables -X
sudo iptables -Z

# Set default policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT

# Allow loopback interface
sudo iptables -A INPUT -i lo -j ACCEPT

# Allow established and related connections
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow SSH (port 22) - CRITICAL: Don't skip this!
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow HTTP (port 80)
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# Allow HTTPS (port 443)
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Allow ping (ICMP)
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# Log dropped packets (optional, useful for debugging)
sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-DROP: " --log-level 7

# Save the rules
sudo netfilter-persistent save

# Verify rules
sudo iptables -L -v -n

Critical: Always configure SSH access (port 22) before setting the default INPUT policy to DROP, or you will lock yourself out of remote systems!

iptables Management: View rules: sudo iptables -L -v -n | Save rules: sudo netfilter-persistent save | Reload rules: sudo netfilter-persistent reload

Verify System Resources

Ensure your system meets the minimum requirements:

Minimum RAM
1 GB
Recommended RAM
2+ GB
Disk Space
10+ GB free
CPU Cores
1+ cores
# Check available memory
free -h

# Check disk space
df -h

# Check CPU info
lscpu

Step 1: Install Apache Web Server

Apache HTTP Server is the most widely-used web server software. It's reliable, powerful, and highly configurable.

Installation

# Install Apache
sudo apt install -y apache2

# Start Apache service
sudo systemctl start apache2

# Enable Apache to start on boot
sudo systemctl enable apache2

# Check status
sudo systemctl status apache2

Verify Installation

Test that Apache is running correctly:

# Check Apache version
apache2 -v

# Test configuration syntax
sudo apache2ctl configtest

# Test from command line
curl http://localhost

Open your web browser and navigate to http://your-server-ip. You should see the Apache default page.

Success Indicator: If you see the "Apache2 Debian Default Page" with the message "It works!", Apache is installed correctly and serving content.

Important Apache Directories

Directory Purpose
/var/www/html/ Default web root directory
/etc/apache2/ Apache configuration files
/etc/apache2/sites-available/ Virtual host configurations
/etc/apache2/sites-enabled/ Enabled virtual hosts (symlinks)
/var/log/apache2/ Apache log files

Enable Essential Apache Modules

Enable commonly used modules for enhanced functionality:

# Enable mod_rewrite (URL rewriting)
sudo a2enmod rewrite

# Enable mod_ssl (HTTPS support)
sudo a2enmod ssl

# Enable mod_headers (HTTP headers control)
sudo a2enmod headers

# Restart Apache to apply changes
sudo systemctl restart apache2

Module Explanation: mod_rewrite is essential for clean URLs and many CMS platforms. mod_ssl enables HTTPS connections. mod_headers allows you to modify HTTP response headers for security and caching.

Basic Apache Commands

# Start Apache
sudo systemctl start apache2

# Stop Apache
sudo systemctl stop apache2

# Restart Apache (full restart)
sudo systemctl restart apache2

# Reload configuration (no downtime)
sudo systemctl reload apache2

# Check syntax of config files
sudo apache2ctl configtest

# View Apache status
sudo systemctl status apache2

Step 2: Install MySQL/MariaDB

MySQL is a powerful relational database management system. On Debian 12, you can choose between MySQL or MariaDB (a MySQL fork). We'll use MariaDB as it's the default in Debian repositories.

Installation

# Install MariaDB server and client
sudo apt install -y mariadb-server mariadb-client

# Start MariaDB service
sudo systemctl start mariadb

# Enable MariaDB to start on boot
sudo systemctl enable mariadb

# Check status
sudo systemctl status mariadb

MySQL vs MariaDB: MariaDB is a drop-in replacement for MySQL, developed by MySQL's original creators. It's fully compatible with MySQL and offers better performance in many scenarios.

Secure MySQL Installation

Run the security script to improve MySQL security:

sudo mysql_secure_installation

Answer the prompts as follows:

  • Enter current password for root: Press Enter (no password set yet)
  • Switch to unix_socket authentication: N (No)
  • Change the root password: Y (Yes) - Enter a strong password
  • Remove anonymous users: Y (Yes)
  • Disallow root login remotely: Y (Yes)
  • Remove test database: Y (Yes)
  • Reload privilege tables: Y (Yes)

Security Critical: Always set a strong root password and remove anonymous users. Never allow root remote login in production environments.

Verify Installation

# Check MariaDB version
mysql --version

# Login to MySQL (as root)
sudo mysql -u root -p

# Once logged in, try these commands:
SHOW DATABASES;
SELECT VERSION();
EXIT;

Create a Database and User

Create a sample database and user for your applications:

# Login to MySQL
sudo mysql -u root -p

# Create a database
CREATE DATABASE myapp_db;

# Create a user with password
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'secure_password';

# Grant privileges
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost';

# Flush privileges
FLUSH PRIVILEGES;

# Verify
SHOW DATABASES;
SELECT User, Host FROM mysql.user;

# Exit
EXIT;

Best Practice: Create separate database users for each application with only the privileges they need. Never use the root account for application connections.

Basic MySQL Commands

# Start MySQL
sudo systemctl start mariadb

# Stop MySQL
sudo systemctl stop mariadb

# Restart MySQL
sudo systemctl restart mariadb

# Check MySQL status
sudo systemctl status mariadb

# View MySQL logs
sudo journalctl -u mariadb -n 50

Step 3: Install PHP

PHP is the scripting language that processes your dynamic content. Debian 12 includes PHP 8.2, which offers significant performance improvements and modern features.

Installation

Install PHP along with essential modules:

# Install PHP and common extensions
sudo apt install -y php php-cli php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-xmlrpc php-zip php-intl

# Restart Apache to load PHP module
sudo systemctl restart apache2

PHP Extensions Explained

Extension Purpose
php-cli Command-line PHP interpreter
php-mysql MySQL/MariaDB database connectivity
php-curl HTTP requests and API calls
php-gd Image processing and manipulation
php-mbstring Multibyte string handling (internationalization)
php-xml XML parsing and processing
php-zip ZIP archive handling

Verify PHP Installation

# Check PHP version
php -v

# View loaded PHP modules
php -m

# Check PHP configuration
php -i | grep "Configuration File"

Configure PHP

Edit the PHP configuration file to optimize settings:

# Edit PHP configuration for Apache
sudo nano /etc/php/8.2/apache2/php.ini

Recommended changes for better performance and security:

# Find and modify these directives:

# Maximum execution time for scripts (seconds)
max_execution_time = 300

# Maximum file upload size
upload_max_filesize = 64M
post_max_size = 64M

# Memory limit per script
memory_limit = 256M

# Timezone (change to yours)
date.timezone = Europe/Paris

# Display errors (disable in production!)
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log

Production Security: Always set display_errors = Off in production environments. Displaying errors can expose sensitive information about your server configuration.

Restart Apache

After modifying PHP settings, restart Apache:

sudo systemctl restart apache2

Check PHP Integration with Apache

# Verify PHP module is loaded
apache2ctl -M | grep php

# Should show: php_module (shared)

Testing Your LAMP Stack

Now let's verify that all components are working together correctly.

Test 1: PHP Info Page

Create a PHP info page to verify PHP is processing correctly:

# Create PHP info file
sudo nano /var/www/html/info.php

Add this content:

<?php
phpinfo();
?>

Save and exit (Ctrl+X, Y, Enter), then visit http://your-server-ip/info.php in your browser.

What to Look For: You should see a detailed page showing PHP version, loaded extensions, configuration settings, and environment variables. This confirms PHP is working with Apache.

Security: Delete this file after testing! It exposes sensitive server information.

sudo rm /var/www/html/info.php

Test 2: PHP-MySQL Connection

Create a test script to verify PHP can connect to MySQL:

# Create test script
sudo nano /var/www/html/dbtest.php

Add this content (replace with your database credentials):

<?php
// Database credentials
$servername = "localhost";
$username = "myapp_user";
$password = "secure_password";
$database = "myapp_db";

// Create connection
$conn = new mysqli($servername, $username, $password, $database);

// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

echo "<h1>Success!</h1>";
echo "<p>Successfully connected to MySQL database: <strong>" . $database . "</strong></p>";
echo "<p>MySQL version: <strong>" . $conn->server_info . "</strong></p>";

$conn->close();
?>

Visit http://your-server-ip/dbtest.php. You should see a success message with the database name and MySQL version.

Security: Delete this test file immediately after verification!

sudo rm /var/www/html/dbtest.php

Test 3: Create a Sample Application

Create a simple web page that uses all three components:

# Create sample page
sudo nano /var/www/html/index.php

Add this content:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LAMP Stack - Running!</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 50px auto;
            padding: 20px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        .card {
            background: rgba(255, 255, 255, 0.1);
            border-radius: 10px;
            padding: 30px;
            backdrop-filter: blur(10px);
            box-shadow: 0 8px 32px rgba(0,0,0,0.3);
        }
        h1 { margin-top: 0; }
        .info { background: rgba(255,255,255,0.2); padding: 15px; border-radius: 5px; margin: 10px 0; }
        .success { color: #4ade80; font-weight: bold; }
    </style>
</head>
<body>
    <div class="card">
        <h1>🚀 LAMP Stack is Running!</h1>
        
        <div class="info">
            <h3>Server Information</h3>
            <p><strong>Server Software:</strong> <?php echo $_SERVER['SERVER_SOFTWARE']; ?></p>
            <p><strong>PHP Version:</strong> <?php echo phpversion(); ?></p>
            <p><strong>Server Name:</strong> <?php echo $_SERVER['SERVER_NAME']; ?></p>
            <p><strong>Server IP:</strong> <?php echo $_SERVER['SERVER_ADDR']; ?></p>
        </div>
        
        <div class="info">
            <h3>System Status</h3>
            <p class="success">✓ Linux: Debian 12 (Bookworm)</p>
            <p class="success">✓ Apache: Running</p>
            <p class="success">✓ MySQL/MariaDB: Running</p>
            <p class="success">✓ PHP: <?php echo phpversion(); ?></p>
        </div>
        
        <p style="margin-top: 30px; text-align: center; opacity: 0.8;">
            Your LAMP stack is configured and ready for development!
        </p>
    </div>
</body>
</html>

Visit http://your-server-ip/ to see your new landing page!

Success! If you see the styled page with your server information, congratulations! Your LAMP stack is fully operational.

Troubleshooting Common Issues

Issue Solution
PHP code shows as plain text Check Apache PHP module: apache2ctl -M | grep php
Cannot connect to database Verify MySQL is running: sudo systemctl status mariadb
Permission denied errors Check file ownership: sudo chown -R www-data:www-data /var/www/html
Changes don't appear Clear browser cache or restart Apache: sudo systemctl restart apache2

Security Hardening

Secure your LAMP stack against common vulnerabilities and attacks.

1. Secure Apache

# Hide Apache version information
sudo nano /etc/apache2/conf-available/security.conf

# Set these directives:
ServerTokens Prod
ServerSignature Off

# Enable the security configuration
sudo a2enconf security

# Restart Apache
sudo systemctl restart apache2

2. Disable Directory Listing

# Edit Apache configuration
sudo nano /etc/apache2/apache2.conf

# Find the Directory section for /var/www/ and change:
Options Indexes FollowSymLinks
# to:
Options -Indexes +FollowSymLinks

# Restart Apache
sudo systemctl restart apache2

3. Set Proper File Permissions

# Set ownership to Apache user
sudo chown -R www-data:www-data /var/www/html

# Set directory permissions
sudo find /var/www/html -type d -exec chmod 755 {} \;

# Set file permissions
sudo find /var/www/html -type f -exec chmod 644 {} \;

Permission Explained: 755 for directories (rwxr-xr-x) allows the owner to modify, while others can only read and execute. 644 for files (rw-r--r--) allows owner to write, others to only read.

4. Enable HTTPS with Let's Encrypt

Install Certbot for free SSL certificates:

# Install Certbot
sudo apt install -y certbot python3-certbot-apache

# Obtain and install certificate (replace with your domain)
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com

# Test automatic renewal
sudo certbot renew --dry-run

5. Configure PHP Security

# Edit PHP configuration
sudo nano /etc/php/8.2/apache2/php.ini

# Add/modify these security settings:
expose_php = Off
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
allow_url_fopen = Off
allow_url_include = Off
sql.safe_mode = On

# Restart Apache
sudo systemctl restart apache2

6. Install Fail2Ban

Protect against brute-force attacks:

# Install Fail2Ban
sudo apt install -y fail2ban

# Create local configuration
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

# Edit configuration
sudo nano /etc/fail2ban/jail.local

# Enable Apache protections (find and uncomment):
[apache-auth]
enabled = true

[apache-badbots]
enabled = true

[apache-noscript]
enabled = true

[apache-overflows]
enabled = true

# Start Fail2Ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

# Check status
sudo fail2ban-client status

7. Regular Updates

Keep your system and packages updated:

# Update package lists and upgrade
sudo apt update && sudo apt upgrade -y

# Enable automatic security updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

8. Backup Strategy

Create a simple backup script:

# Create backup directory
sudo mkdir -p /var/backups/lamp

# Create backup script
sudo nano /usr/local/bin/lamp-backup.sh

Add this content:

#!/bin/bash
# LAMP Stack Backup Script

DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/var/backups/lamp"

# Backup web files
tar -czf $BACKUP_DIR/www_$DATE.tar.gz /var/www/html

# Backup all databases
mysqldump --all-databases -u root -p > $BACKUP_DIR/databases_$DATE.sql

# Keep only last 7 days of backups
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete

echo "Backup completed: $DATE"
# Make executable
sudo chmod +x /usr/local/bin/lamp-backup.sh

# Test the backup
sudo /usr/local/bin/lamp-backup.sh

# Add to crontab for daily backups at 2 AM
sudo crontab -e
# Add this line:
0 2 * * * /usr/local/bin/lamp-backup.sh >> /var/log/lamp-backup.log 2>&1

Backup Critical: Store backups on a separate disk or remote location. Local-only backups won't protect against hardware failure or ransomware.

Security Checklist

  • ✓ iptables firewall configured with proper rules
  • ✓ Apache version information hidden
  • ✓ Directory listing disabled
  • ✓ Proper file permissions set
  • ✓ HTTPS/SSL certificate installed
  • ✓ PHP security settings configured
  • ✓ Fail2Ban installed and running
  • ✓ Automatic security updates enabled
  • ✓ Regular backups scheduled
  • ✓ Strong MySQL root password set
  • ✓ MySQL remote root login disabled
  • ✓ Test/demo files removed

Next Steps & Resources

Virtual Hosts (Multiple Websites)

Configure Apache to host multiple websites on one server:

# Create directory for new site
sudo mkdir -p /var/www/mysite.com/public_html

# Set permissions
sudo chown -R www-data:www-data /var/www/mysite.com
sudo chmod -R 755 /var/www/mysite.com

# Create virtual host configuration
sudo nano /etc/apache2/sites-available/mysite.com.conf

Add this configuration:

<VirtualHost *:80>
    ServerName mysite.com
    ServerAlias www.mysite.com
    ServerAdmin admin@mysite.com
    
    DocumentRoot /var/www/mysite.com/public_html
    
    <Directory /var/www/mysite.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog ${APACHE_LOG_DIR}/mysite.com_error.log
    CustomLog ${APACHE_LOG_DIR}/mysite.com_access.log combined
</VirtualHost>
# Enable the site
sudo a2ensite mysite.com.conf

# Disable default site (optional)
sudo a2dissite 000-default.conf

# Test configuration
sudo apache2ctl configtest

# Reload Apache
sudo systemctl reload apache2

Install Popular CMS Platforms

WordPress

# Download WordPress
cd /tmp
wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
sudo mv wordpress /var/www/html/

# Set permissions
sudo chown -R www-data:www-data /var/www/html/wordpress
sudo chmod -R 755 /var/www/html/wordpress

# Create database for WordPress
sudo mysql -u root -p
CREATE DATABASE wordpress_db;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

# Visit http://your-server-ip/wordpress to complete installation

phpMyAdmin (Optional)

Install phpMyAdmin for easier database management:

# Install phpMyAdmin
sudo apt install -y phpmyadmin

# During installation:
# - Select "apache2" (use spacebar to select)
# - Configure database: Yes
# - Enter MySQL root password
# - Set phpMyAdmin password

# Restart Apache
sudo systemctl restart apache2

# Access at: http://your-server-ip/phpmyadmin

phpMyAdmin Security: For production servers, secure phpMyAdmin by changing its URL, enabling HTTPS, and restricting access by IP address.

Performance Optimization

Enable Apache MPM Event

# Disable prefork MPM
sudo a2dismod mpm_prefork

# Enable event MPM and PHP-FPM
sudo a2enmod mpm_event proxy_fcgi setenvif
sudo a2enconf php8.2-fpm

# Restart services
sudo systemctl restart apache2 php8.2-fpm

Install and Configure OPcache

# Install OPcache (should already be installed)
sudo apt install -y php8.2-opcache

# Configure OPcache
sudo nano /etc/php/8.2/apache2/conf.d/10-opcache.ini

# Add/modify:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60

# Restart Apache
sudo systemctl restart apache2

Install Memcached

# Install Memcached and PHP extension
sudo apt install -y memcached php-memcached

# Start and enable Memcached
sudo systemctl enable memcached
sudo systemctl start memcached

# Restart Apache
sudo systemctl restart apache2

Monitoring & Maintenance

Check Service Status

# View all services
sudo systemctl status apache2 mariadb

# Check resource usage
htop
# (install with: sudo apt install htop)

# Monitor Apache connections
sudo ss -tulpn | grep :80
sudo ss -tulpn | grep :443

# View Apache access logs
sudo tail -f /var/log/apache2/access.log

# View Apache error logs
sudo tail -f /var/log/apache2/error.log

# View MySQL logs
sudo tail -f /var/log/mysql/error.log

Disk Space Management

# Check disk usage
df -h

# Find large files
sudo du -h /var/www | sort -rh | head -20

# Clean old logs
sudo journalctl --vacuum-time=7d

# Rotate Apache logs
sudo logrotate -f /etc/logrotate.conf

Useful Commands Reference

Task Command
Restart all services sudo systemctl restart apache2 mariadb
Check Apache syntax sudo apache2ctl configtest
List enabled modules apache2ctl -M
List enabled sites ls /etc/apache2/sites-enabled/
MySQL root login sudo mysql -u root -p
Check PHP version php -v
List PHP modules php -m
View system logs sudo journalctl -xe

Additional Resources

Troubleshooting Resources

  • Apache logs: /var/log/apache2/
  • MySQL logs: /var/log/mysql/
  • PHP error log: /var/log/php_errors.log (if configured)
  • System logs: sudo journalctl -xe
  • Service status: sudo systemctl status [service]

Congratulations! You now have a fully functional, secure LAMP stack ready for development or production use. Start building your web applications, deploy your favorite CMS, or experiment with PHP frameworks like Laravel or Symfony.

What's Next?

  • Deploy your first web application or CMS
  • Set up automated backups to remote storage
  • Configure monitoring with tools like Netdata or Prometheus
  • Implement advanced caching with Redis or Varnish
  • Explore containerization with Docker for easier deployment
  • Learn about load balancing for high-traffic sites