As a seasoned web developer, I've spent countless hours wrestling with local development environments. From conflicting dependencies to "it works on my machine" woes, the struggle is real. But what if I told you there's a better way? A way to ensure consistency, portability, and isolation for your projects? Today, we're diving deep into how to set up a PHP development environment with Docker, transforming your workflow from a battlefield into a well-oiled machine. Forget XAMPP or MAMP; Docker is the modern developer's superpower for PHP.
Why Docker is a Game Changer for PHP Development
Traditional PHP development often involves installing PHP, a web server like Apache or Nginx, and a database like MySQL directly on your host machine. This approach quickly becomes problematic. Different projects often require different PHP versions, specific extensions, or unique database configurations, leading to inevitable dependency conflicts.
Docker elegantly solves this by containerizing each service. With Docker, each component (PHP, Nginx, MySQL) runs in its own isolated container, providing significant advantages:
- Consistency: Your development environment precisely mirrors production, drastically reducing "works on my machine" issues.
- Isolation: Each project can have its own specific PHP version and dependencies without affecting other projects on your machine.
- Portability: Your entire environment is defined in code (
Dockerfileanddocker-compose.yml), making it incredibly easy to share with team members or deploy across different machines. - Efficiency: Spin up and tear down complex environments in seconds, allowing you to focus more on coding and less on configuration.
This level of isolation and consistency is crucial, especially when working on projects that might eventually scale into Mastering Microservices for Modern Web Development. Docker provides the foundational environment for robust development practices.
Prerequisites: Getting Started with Docker
Before we jump into the setup, you'll need Docker installed on your system. Docker Desktop is the easiest way to get started for Windows, macOS, and Linux. Head over to the official Docker website and download the appropriate installer for your operating system.
Once installed, open your terminal or command prompt and verify Docker is running correctly by typing:
docker --version
docker compose version
You should see version numbers for both Docker and Docker Compose (which is often bundled with Docker Desktop).
Project Structure and Core Files
Let's define a simple project structure for our PHP application. Create a new directory, say my-php-app, and inside it, we'll place our configuration files and application code.
my-php-app/
├── public/
│ └── index.php
├── docker-compose.yml
├── Dockerfile
└── .env
The public/ directory will hold our PHP application files, with index.php being our entry point. The other files are for Docker configuration.
Step 1: The Dockerfile for PHP-FPM
First, we need a Dockerfile to build our custom PHP-FPM image. This file defines how our PHP container will be constructed, including the PHP version, necessary extensions, and configurations.
Create a file named Dockerfile in your project root with the following content:
FROM php:8.2-fpm-alpine
WORKDIR /var/www/html
RUN apk update && apk add --no-cache \
curl \
mysql-client \
git \
build-base \
autoconf \
nginx
RUN docker-php-ext-install pdo_mysql opcache
COPY . /var/www/html
RUN chown -R www-data:www-data /var/www/html
EXPOSE 9000
CMD ["php-fpm"]
This Dockerfile uses an Alpine-based PHP 8.2 FPM image for a smaller footprint. It installs common utilities and the pdo_mysql extension (crucial for database interaction) and sets up permissions. We're explicitly defining how to set up a php development environment with docker by building a custom PHP image here.
Step 2: Defining Services with docker-compose.yml
Next, we'll use docker-compose.yml to orchestrate our multiple services: Nginx (our web server), PHP-FPM (our custom PHP service), and MySQL (our database). This file describes the entire stack and how the services interact.
Create a file named docker-compose.yml in your project root:
version: '3.8'
services:
nginx:
image: nginx:stable-alpine
container_name: my-php-nginx
ports:
- "80:80"
volumes:
- ./public:/var/www/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php
networks:
- my-php-network
php:
build:
context: .
dockerfile: Dockerfile
container_name: my-php-fpm
volumes:
- ./public:/var/www/html
networks:
- my-php-network
mysql:
image: mysql:8.0
container_name: my-php-mysql
env_file:
- ./.env
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- dbdata:/var/lib/mysql
ports:
- "3306:3306" # Optional: Expose for local access if needed
networks:
- my-php-network
networks:
my-php-network:
driver: bridge
volumes:
dbdata:
This configuration defines three services: nginx, php, and mysql. They all share a custom network (`my-php-network`) for seamless internal communication. Nginx is exposed on port 80, and MySQL on 3306 (optional, for local tools). The PHP service builds from our custom Dockerfile.
Step 3: Nginx Configuration
We need a simple Nginx configuration to serve our PHP files and pass PHP requests to our PHP-FPM container. Create a new directory named `nginx` in your project root, and inside it, create `default.conf`:
server {
listen 80;
index index.php index.html;
root /var/www/html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
This configuration tells Nginx to listen on port 80 and pass all requests for .php files to our `php` service (which is listening on port 9000 within the Docker network).
Step 4: Environment Variables (.env)
For our MySQL service, we're using environment variables for sensitive data. Create a file named .env in your project root:
DB_DATABASE=mydatabase
DB_USERNAME=myuser
DB_PASSWORD=mypassword
Remember to use strong, unique passwords for production environments!
Step 5: Your PHP Application (index.php)
Finally, let's create a simple PHP file to test our entire Dockerized setup. In the public/ directory, create index.php:
<?php
echo "<h1>Hello from PHP running in Docker!</h1>";
// Try to connect to MySQL
$servername = 'mysql'; // Service name in docker-compose.yml
$username = getenv('DB_USERNAME');
$password = getenv('DB_PASSWORD');
$dbname = getenv('DB_DATABASE');
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "<p>Successfully connected to MySQL!</p>";
} catch (PDOException $e) {
echo "<p>Connection failed: " . $e->getMessage() . "</p>";
}
?>
This script will output a greeting and attempt to connect to our MySQL container, confirming our entire stack is working. This is a crucial step in understanding how to set up a php development environment with docker correctly.
Bringing Your Environment to Life
With all the files in place, navigate to your my-php-app directory in your terminal and run:
docker compose up -d
This command will build your PHP image, create, and start all defined services in detached mode (`-d`). It might take a few minutes the first time as it downloads base images and builds your custom PHP image.
Once everything is up, open your web browser and go to http://localhost. You should see "Hello from PHP running in Docker!" and "Successfully connected to MySQL!".
Stopping and Removing the Environment
When you're done developing, you can stop the containers:
docker compose stop
To stop and remove all containers, networks, and volumes associated with your project:
docker compose down -v
The `-v` flag removes the `dbdata` volume, which is useful for starting fresh or cleaning up sensitive data. You can always start it back up with `docker compose up -d`.
FAQ
What if I need a different PHP version or more extensions?
You simply modify your Dockerfile! Change FROM php:8.2-fpm-alpine to, for instance, FROM php:7.4-fpm-alpine. For extensions, add more RUN docker-php-ext-install <extension_name> lines. After modifications, remember to rebuild your PHP image with docker compose build php and then restart your environment with docker compose up -d.
How do I debug PHP applications within Docker?
Xdebug is the go-to tool for PHP debugging. You would typically install Xdebug within your Dockerfile (e.g., RUN docker-php-ext-install xdebug) and configure it to connect back to your host machine's IDE. This often involves setting xdebug.client_host to host.docker.internal (for Docker Desktop) or your host IP, and exposing port 9003 in your docker-compose.yml for the Xdebug client.
Can I use this setup for other frameworks like Laravel or Symfony?
Absolutely! This Docker setup provides a robust foundation for any PHP framework. For frameworks like Laravel, you would typically move your entire framework project into the public/ directory (or adjust the Nginx volume mapping and root accordingly, e.g., root /var/www/html/public;). You might also need to install additional PHP extensions specific to your chosen framework. For more complex setups, consider customising your Dockerfile further, much like how you might tailor configurations when you Create Custom WordPress REST API Endpoints Tutorial for specific WordPress needs.
Conclusion
Setting up a PHP development environment with Docker fundamentally changes how you approach project setup and management. By containerizing your services, you gain unparalleled consistency, isolation, and portability, freeing you from dependency conflicts and "works on my machine" headaches. We've walked through the essential steps, from defining your custom PHP image to orchestrating multiple services with Docker Compose, giving you a powerful, reproducible development stack.
Now that you know how to set up a PHP development environment with Docker, it's time to supercharge your development workflow. Embrace Docker, and never look back at the days of arduous local environment configurations. What's the first PHP project you'll containerize? Share your thoughts and experiences in the comments below!




