Image created with Canva

How Did I Set Up Docker for PHP in Just 30 Minutes?

Serghei Pogor
Published in
6 min readApr 17, 2024

Setting up Docker for PHP can sound big and scary, but it’s really not! 😄

Let’s dive in and see how I did it in just 30 minutes, which might help you too!

Install Docker

First, you need Docker on your computer. It’s like getting a new toy you need to put batteries in before you can play.

You can download it from the Docker website. Follow the instructions for your operating system, and voila, you’re set!

Create Your Dockerfile

A Dockerfile is like a recipe for Docker. It tells Docker what to do to set up your environment. Here’s a simple Dockerfile to get your PHP environment ready:

# Use the official PHP image from the Docker Hub
FROM php:8.3-apache

# Install all the PHP extensions you need
RUN docker-php-ext-install pdo_mysql

This Dockerfile starts with a PHP 8.3 image and adds the pdo_mysql extension, which many PHP applications need to talk to a database.

PHP Setup

# Start from the official PHP image with Apache
FROM php:8.3-apache

# Install system dependencies and PHP extensions in one step
RUN apt-get update && apt-get install -y \
libpng-dev \
libonig-dev \
libxml2-dev \
libzip-dev \
zip \
curl \
unzip \
git \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip \
&& a2enmod rewrite \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

This Dockerfile does everything in one RUN command:

  1. Updates package lists.
  2. Installs system dependencies needed by PHP extensions.
  3. Installs PHP extensions commonly used in web applications.
  4. Enables the Apache rewrite module for URL rewriting.
  5. Cleans up by removing unnecessary files to reduce the image size.

Build and run your Docker image with:

docker build -t my-php-app .
docker run -p 8080:80 my-php-app

This setup streamlines the installation into a single step, making your Dockerfile efficient and easy to manage.

Manage Your Services with Docker Compose

Now, we want to make managing our Docker container easier. That’s where Docker Compose comes in. It lets you manage multiple containers (like your web server, database, etc.) with just one file. Let’s create a docker-compose.yml file:

version: '3.8'
services:
web:
build: .
ports:
- "8000:80"
volumes:
- .:/var/www/html

db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: symfony
ports:
- "3306:3306"

This file sets up two services: web (your PHP environment) and db (a MySQL database). It builds the web service from the Dockerfile we created and keeps your code in sync with the container.

Run Everything with Docker Compose

Now, let’s start all our services with one command:

docker-compose up

When you run this, Docker Compose will set up both your PHP environment and your MySQL database, linked together. You can visit localhost:8000 to see your Symfony application in action, now with database superpowers! 💥

Add PHPMyAdmin for Database Management

Managing a database using the command line is cool, but sometimes, a graphical interface is handier, especially if you’re just starting out. Let’s add PHPMyAdmin to our Docker Compose setup:

version: '3.8'
services:
web:
build: .
ports:
- "8000:80"
volumes:
- .:/var/www/html

db:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: symfony
ports:
- "3306:3306"

phpmyadmin:
image: phpmyadmin/phpmyadmin
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: root
ports:
- "8080:80"

Now, run docker-compose up again, and you can access PHPMyAdmin at localhost:8080. This tool will make it much easier to view and manage your MySQL database through a friendly web interface.

Using Xdebug with PHP for Debugging

Debugging is like being a detective in your own code. Let’s add Xdebug to our Dockerfile to help us find bugs more efficiently. Here’s how you set it up:

# Install Xdebug
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug

# Configure Xdebug
COPY xdebug.ini $PHP_INI_DIR/conf.d/

You’ll need to create an xdebug.ini file with the proper configuration:

zend_extension=xdebug.so
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003

This setup starts Xdebug with every request and communicates with your IDE at port 9003.

Log PHP Errors for Better Insight

It’s important to know what goes wrong when it goes wrong. Let’s configure PHP to log errors. Modify your Dockerfile to include these lines:

# Set PHP ini settings for error logging
RUN echo "log_errors = On" >> $PHP_INI_DIR/php.ini \
&& echo "error_log = /var/log/php_errors.log" >> $PHP_INI_DIR/php.ini

This will log PHP errors to /var/log/php_errors.log inside your Docker container. You can check this log anytime to see what's happening if things aren't working right.

Set Up Automated Testing with PHPUnit

Automated testing is like having a robot that checks your homework. It’s a great way to catch mistakes early. Let’s add PHPUnit, a popular testing framework for PHP, to our Docker setup:

First, modify your Dockerfile to install PHPUnit:

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

# Install PHPUnit
RUN composer require --dev phpunit/phpunit ^9.5

This snippet installs Composer and then uses it to install PHPUnit. Composer is a tool for managing PHP package dependencies.

Next, create a simple PHPUnit test case. Here’s an example test for a hypothetical Calculator class:

// tests/CalculatorTest.php
use PHPUnit\Framework\TestCase;

class CalculatorTest extends TestCase
{
public function testAdd()
{
$calculator = new Calculator();
$this->assertEquals(4, $calculator->add(2, 2));
}
}

To run your tests, you can use the following command:

docker-compose run web vendor/bin/phpunit tests

This command tells Docker to run PHPUnit tests inside your web service.

Introduce Continuous Integration with GitHub Actions

Continuous Integration (CI) means automatically testing your code every time you make changes. Let’s set up CI using GitHub Actions, which integrates directly with your GitHub repository:

Create a .github/workflows/php.yml file in your repository with the following content:

name: PHP CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: pdo_mysql
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Run Tests
run: vendor/bin/phpunit tests

This workflow configures GitHub Actions to set up a PHP environment, install dependencies, and run tests whenever you push changes to the main branch or create a pull request.

Optimize Docker for Production

When moving from a development to a production environment, performance and security become top priorities. Let’s make a few changes to our Docker setup to enhance both.

Reduce Image Size

First, let’s optimize our Docker image to make it lighter and faster. This means using smaller base images and removing unnecessary files:

# Use a smaller base image
FROM php:8.3-fpm-alpine

# Install only the necessary extensions
RUN docker-php-ext-install pdo pdo_mysql

This uses the Alpine version of the PHP image, which is much smaller than the standard version.

Secure Your Application

Security in production is critical. Here are a few things you should do:

  • Use environment variables for sensitive information (like database passwords).
  • Ensure only necessary ports are exposed.
  • Regularly update your images to include the latest security patches.
ENV MYSQL_PASSWORD="secure_password_here"

EXPOSE 80

Always store passwords and other sensitive data in environment variables and avoid hard-coding them into your application.

Set Up Continuous Deployment

Continuous Deployment (CD) means automatically deploying your application whenever you make changes. Let’s extend our GitHub Actions workflow to include deployment:

Add the following steps to your .github/workflows/php.yml file:

- name: Build Docker Image
run: docker build -t my-php-app .
- name: Push Docker Image to Registry
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker tag my-php-app ${{ secrets.DOCKER_USERNAME }}/my-php-app:latest
docker push ${{ secrets.DOCKER_USERNAME }}/my-php-app:latest
- name: Deploy to Production
run: echo "Deploying to production server..."
# Add your deployment script here

And we’re done! 🎉

We now have a full application set up with everything we need for it to work smoothly.
We’ve gone through installing Docker, setting up PHP with all the essential extensions, and integrating powerful tools and practices that elevate our development process.
This setup not only makes our application robust but also streamlines our workflow, ensuring we’re ready to tackle any project efficiently.

🔔 Click Subscribe to catch more coding fun.
👏🏻 Love it? Give a big clap.
💬 Got a cool idea or funny coding joke? Drop it in the comments.

Share these tips with your fellow developers to help each other succeed together.

Thanks for hanging out and reading. You rock! 🚀

Hold on a sec!!! Want more of my fun stuff in your inbox? Sign up here! 📩

Published in DevOps.dev

Devops.dev is a community of DevOps enthusiasts sharing insight, stories, and the latest development in the field.

Written by Serghei Pogor

Good code is its own best documentation

No responses yet

What are your thoughts?