DEV Community

Purushotam Adhikari
Purushotam Adhikari

Posted on

Docker Compose for Development: Setting Up a Complete LAMP Stack

Setting up a consistent development environment can be challenging, especially when working across different machines or with team members. Docker Compose solves this problem by allowing us to define and run multi-container applications with a single command. In this guide, we'll create a complete LAMP (Linux, Apache, MySQL, PHP) stack that's perfect for web development.

Why Docker Compose for LAMP Development?

Before diving into the setup, let's understand why Docker Compose is ideal for LAMP stack development:

  • Consistency: Same environment across all machines
  • Isolation: No conflicts with host system packages
  • Easy setup: One command to start everything
  • Version control: Infrastructure as code
  • Team collaboration: Share exact environment configurations

Project Structure

Let's start by creating our project structure:

lamp-docker/
├── docker-compose.yml
├── apache/
│   └── Dockerfile
├── php/
│   └── Dockerfile
├── www/
│   └── index.php
├── mysql/
│   └── init.sql
└── .env
Enter fullscreen mode Exit fullscreen mode

The Docker Compose Configuration

Create a docker-compose.yml file in your project root:

version: '3.8'

services:
  # Apache Web Server
  apache:
    build: ./apache
    container_name: lamp_apache
    ports:
      - "8080:80"
    volumes:
      - ./www:/var/www/html
    depends_on:
      - php
      - mysql
    networks:
      - lamp_network

  # PHP-FPM Service
  php:
    build: ./php
    container_name: lamp_php
    volumes:
      - ./www:/var/www/html
    networks:
      - lamp_network

  # MySQL Database
  mysql:
    image: mysql:8.0
    container_name: lamp_mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./mysql:/docker-entrypoint-initdb.d
    networks:
      - lamp_network

  # phpMyAdmin
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: lamp_phpmyadmin
    environment:
      PMA_HOST: mysql
      PMA_PORT: 3306
      PMA_USER: ${MYSQL_USER}
      PMA_PASSWORD: ${MYSQL_PASSWORD}
    ports:
      - "8081:80"
    depends_on:
      - mysql
    networks:
      - lamp_network

volumes:
  mysql_data:

networks:
  lamp_network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

Environment Variables

Create a .env file to store sensitive information:

# MySQL Configuration
MYSQL_ROOT_PASSWORD=rootpassword123
MYSQL_DATABASE=lamp_db
MYSQL_USER=lamp_user
MYSQL_PASSWORD=lamp_password123
Enter fullscreen mode Exit fullscreen mode

Apache Configuration

Create apache/Dockerfile:

FROM httpd:2.4

# Enable mod_rewrite and mod_proxy
RUN sed -i 's/#LoadModule rewrite_module/LoadModule rewrite_module/' /usr/local/apache2/conf/httpd.conf
RUN sed -i 's/#LoadModule proxy_module/LoadModule proxy_module/' /usr/local/apache2/conf/httpd.conf
RUN sed -i 's/#LoadModule proxy_fcgi_module/LoadModule proxy_fcgi_module/' /usr/local/apache2/conf/httpd.conf

# Configure Apache to work with PHP-FPM
RUN echo "DirectoryIndex index.php index.html" >> /usr/local/apache2/conf/httpd.conf
RUN echo "ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://php:9000/var/www/html/\$1" >> /usr/local/apache2/conf/httpd.conf

# Enable .htaccess files
RUN sed -i '/<Directory "\/usr\/local\/apache2\/htdocs">/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /usr/local/apache2/conf/httpd.conf
Enter fullscreen mode Exit fullscreen mode

PHP Configuration

Create php/Dockerfile:

FROM php:8.2-fpm

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Set working directory
WORKDIR /var/www/html

# Change ownership of our applications
RUN chown -R www-data:www-data /var/www/html
Enter fullscreen mode Exit fullscreen mode

Sample Application

Create www/index.php to test our setup:

<?php
$servername = "mysql";
$username = "lamp_user";
$password = "lamp_password123";
$dbname = "lamp_db";

echo "<h1>LAMP Stack with Docker Compose</h1>";

// Test PHP
echo "<h2>PHP Information</h2>";
echo "<p>PHP Version: " . phpversion() . "</p>";
echo "<p>Server Time: " . date('Y-m-d H:i:s') . "</p>";

// Test MySQL Connection
echo "<h2>Database Connection</h2>";
try {
    $pdo = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo "<p style='color: green;'>✅ Connected to MySQL successfully!</p>";

    // Create a test table and insert data
    $pdo->exec("CREATE TABLE IF NOT EXISTS users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(50) NOT NULL,
        email VARCHAR(100) NOT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )");

    // Insert sample data if table is empty
    $stmt = $pdo->query("SELECT COUNT(*) FROM users");
    if ($stmt->fetchColumn() == 0) {
        $pdo->exec("INSERT INTO users (name, email) VALUES 
            ('John Doe', '[email protected]'),
            ('Jane Smith', '[email protected]'),
            ('Bob Johnson', '[email protected]')");
    }

    // Display users
    echo "<h3>Sample Users:</h3>";
    $stmt = $pdo->query("SELECT * FROM users");
    echo "<table border='1' cellpadding='5'>";
    echo "<tr><th>ID</th><th>Name</th><th>Email</th><th>Created At</th></tr>";
    while ($row = $stmt->fetch()) {
        echo "<tr>";
        echo "<td>" . $row['id'] . "</td>";
        echo "<td>" . $row['name'] . "</td>";
        echo "<td>" . $row['email'] . "</td>";
        echo "<td>" . $row['created_at'] . "</td>";
        echo "</tr>";
    }
    echo "</table>";

} catch(PDOException $e) {
    echo "<p style='color: red;'>❌ Connection failed: " . $e->getMessage() . "</p>";
}

// Test Apache
echo "<h2>Server Information</h2>";
echo "<p>Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "</p>";
echo "<p>Document Root: " . $_SERVER['DOCUMENT_ROOT'] . "</p>";

echo "<h2>Access Points</h2>";
echo "<ul>";
echo "<li><a href='http://localhost:8080' target='_blank'>Web Application (Port 8080)</a></li>";
echo "<li><a href='http://localhost:8081' target='_blank'>phpMyAdmin (Port 8081)</a></li>";
echo "</ul>";
?>
Enter fullscreen mode Exit fullscreen mode

Database Initialization

Create mysql/init.sql for initial database setup:

-- Create database if it doesn't exist
CREATE DATABASE IF NOT EXISTS lamp_db;
USE lamp_db;

-- Create a sample table
CREATE TABLE IF NOT EXISTS posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT,
    author VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- Insert sample data
INSERT INTO posts (title, content, author) VALUES
('Welcome to LAMP Stack', 'This is your first post in the LAMP stack setup.', 'Admin'),
('Docker Compose Benefits', 'Using Docker Compose makes development environment setup incredibly easy.', 'Developer'),
('Database Connection Test', 'If you can see this, your MySQL connection is working perfectly!', 'System');

-- Create additional sample tables
CREATE TABLE IF NOT EXISTS categories (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO categories (name, description) VALUES
('Technology', 'Posts about technology and programming'),
('Tutorial', 'Step-by-step guides and tutorials'),
('News', 'Latest news and updates');
Enter fullscreen mode Exit fullscreen mode

Getting Started

Now let's launch our LAMP stack:

  1. Clone or create the project structure:
mkdir lamp-docker && cd lamp-docker
# Create all the files as shown above
Enter fullscreen mode Exit fullscreen mode
  1. Build and start the services:
docker-compose up -d --build
Enter fullscreen mode Exit fullscreen mode
  1. Access your applications:

Useful Docker Compose Commands

Here are essential commands for managing your LAMP stack:

# Start services
docker-compose up -d

# Stop services
docker-compose down

# View logs
docker-compose logs

# View logs for specific service
docker-compose logs apache

# Rebuild services
docker-compose up -d --build

# Execute commands in containers
docker-compose exec php bash
docker-compose exec mysql mysql -u root -p

# View running containers
docker-compose ps
Enter fullscreen mode Exit fullscreen mode

Development Workflow

Your typical development workflow will be:

  1. Start the environment: docker-compose up -d
  2. Edit files: Make changes in the www/ directory
  3. View changes: Refresh your browser at http://localhost:8080
  4. Database management: Use phpMyAdmin at http://localhost:8081
  5. Stop when done: docker-compose down

Adding SSL/HTTPS (Optional)

To add SSL support for development, modify your apache/Dockerfile:

FROM httpd:2.4

# Enable SSL module
RUN sed -i 's/#LoadModule ssl_module/LoadModule ssl_module/' /usr/local/apache2/conf/httpd.conf

# Enable other required modules
RUN sed -i 's/#LoadModule rewrite_module/LoadModule rewrite_module/' /usr/local/apache2/conf/httpd.conf
RUN sed -i 's/#LoadModule proxy_module/LoadModule proxy_module/' /usr/local/apache2/conf/httpd.conf
RUN sed -i 's/#LoadModule proxy_fcgi_module/LoadModule proxy_fcgi_module/' /usr/local/apache2/conf/httpd.confRUN sed -i 's/#Include conf\/extra\/httpd-ssl.conf/Include conf\/extra\/httpd-ssl.conf/' /usr/local/apache2/conf/httpd.conf

# Generate self-signed certificate for development
RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /usr/local/apache2/conf/server.key \
    -out /usr/local/apache2/conf/server.crt \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"

# Configure Apache for PHP-FPM
RUN echo "DirectoryIndex index.php index.html" >> /usr/local/apache2/conf/httpd.conf
RUN echo "ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://php:9000/var/www/html/\$1" >> /usr/local/apache2/conf/httpd.conf
Enter fullscreen mode Exit fullscreen mode

Then add port 443 to your Apache service in docker-compose.yml:

apache:
  build: ./apache
  container_name: lamp_apache
  ports:
    - "8080:80"
    - "8443:443"  # Add HTTPS port
  # ... rest of configuration
Enter fullscreen mode Exit fullscreen mode

Performance Optimization Tips

  1. Use named volumes for better performance:
volumes:
  - ./www:/var/www/html:cached  # On macOS for better performance
Enter fullscreen mode Exit fullscreen mode
  1. Add PHP OPcache configuration in php/Dockerfile:
RUN docker-php-ext-install opcache
COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini
Enter fullscreen mode Exit fullscreen mode
  1. Configure MySQL for development by adding to docker-compose.yml:
mysql:
  # ... existing config
  command: --innodb-buffer-pool-size=256M --max-connections=100
Enter fullscreen mode Exit fullscreen mode

Troubleshooting Common Issues

Port conflicts: If ports 8080 or 8081 are already in use, change them in docker-compose.yml

Permission issues: On Linux, you might need to adjust file permissions:

sudo chown -R $USER:$USER www/
Enter fullscreen mode Exit fullscreen mode

MySQL connection refused: Ensure the MySQL container is fully started before PHP tries to connect. Add a health check:

mysql:
  # ... existing config
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    timeout: 20s
    retries: 10
Enter fullscreen mode Exit fullscreen mode

Conclusion

You now have a fully functional LAMP stack running in Docker containers! This setup provides a consistent, isolated development environment that can be easily shared with team members or replicated across different machines.

The beauty of this approach is that you can customize each component according to your project's needs, add additional services like Redis or Elasticsearch, and maintain everything in version control.

This Docker Compose setup eliminates the "it works on my machine" problem and makes onboarding new developers incredibly simple. Just clone the repository and run docker-compose up -d!

Next Steps

Consider exploring:

  • Adding Redis for caching
  • Including Node.js for asset compilation
  • Setting up automated testing with PHPUnit
  • Implementing CI/CD pipelines
  • Adding monitoring with tools like Prometheus

Happy coding! 🚀


Did this guide help you set up your LAMP stack? Share your experience in the comments below, and don't forget to follow for more Docker and web development content!

Top comments (0)