Tutorials  /  Linux Basics

Deploy a PHP App with Nginx, MySQL & Docker Compose

Ccentron Redaktion · July 2025 ·8 min read ·Linux Basics, Tutorial

Typical PHP setups consist of three main components: a web server, a relational database system, and the PHP interpreter. In this walkthrough, we will assemble a full PHP environment using Docker. You will learn how to create and manage containers for Nginx (web server), MySQL (database), and PHP.

Creating a Simple PHP App to Read from a Database

We will build a basic web application that retrieves a list of cities from a MySQL database and shows them on a web page. This practical example will demonstrate how to implement a functional PHP-based setup.

VM

Matching infrastructure at centron

No hardware needed to follow along: ccloud³ VMs with full root access start at €3.12 per month, billed by the hour and ready in seconds. Rent a cloud server →

Prerequisites for This Tutorial

This guide assumes that Docker-CE is already installed on your system and that you have a basic understanding of Docker.

Setting Up the Project Environment

In real-world scenarios, Docker-based applications often consist of several containers. Managing these manually can quickly become chaotic. Docker Compose simplifies this by allowing control over multiple containers through a YAML configuration file.

Installing Docker Compose

Console
curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

Creating the Project Directory

Start by setting up a main directory for your project files and move into it. This will be your working directory (referred to here as WORKING_DIR):

Console
mkdir ~/docker
cd ~/docker

Now create three subdirectories inside it:

Console
mkdir php nginx app

The php directory will store the custom PHP image, the nginx folder will contain the Nginx configuration, and app will house the application code and config files.

Setting Up the PHP Container

We will configure PHP-FPM to communicate with Nginx. The PHP image will be based on the official lightweight Alpine image. It also requires some additional extensions to interact with the MySQL database. Inside the php folder, create a file named Dockerfile and include the following:

Code
FROM php:7.1-fpm-alpine3.4
RUN apk update --no-cache \
    && apk add --no-cache $PHPIZE_DEPS \
    && apk add --no-cache mysql-dev \
    && docker-php-ext-install pdo pdo_mysql

This setup uses Alpine Linux for its minimal footprint, ideal for containerized environments. The command docker-php-ext-install provided by the PHP image simplifies enabling required extensions.

Building the PHP Docker Image

Build your Docker image from the WORKING_DIR:

Console
docker build -t centron-php php/

Creating the docker-compose.yml File

Docker Compose uses a configuration file—commonly named docker-compose.yml—to define and control multi-container applications. Create this file inside the app directory:

Code
touch app/docker-compose.yml

Insert the following content into it:

YAML
version: '2'
services:
  php:
    image: centron-php
    volumes:
      - ./:/app
    working_dir: /app

Understanding the docker-compose Configuration

The first line, version: '2', defines the Compose file format. Under services, we declare all the containers to be created—in this case, only one for PHP.

The service php refers to the image we just built. The volume mapping:

YAML
volumes:
  - ./:/app

...binds the current host directory to /app inside the container. The working_dir specifies where future container commands will run by default.

Running the Containers

Now navigate to the app directory and bring up the containers:

Console
cd ~/docker/app
docker-compose up -d

To confirm that the PHP container is running properly, use the following:

Console
docker ps

Running Commands Inside Containers with Docker Compose

From the app directory, you can execute commands inside any containerized service using the following syntax:

Console
docker-compose exec [service] [command]

For instance, to verify the PHP version inside the container, run:

Console
docker-compose exec php php -v

This will output something like:

Code
PHP 7.1.14 (cli) (built: Feb  7 2018 00:40:45) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies

Customizing the Nginx Container

Similar to the PHP setup, we need to configure a custom container for Nginx. This time, we just need to supply a virtual host configuration file. While in WORKING_DIR, create a Dockerfile for the Nginx container:

Console
cd ~/docker
touch nginx/Dockerfile

Insert this content into the Dockerfile:

Code
FROM nginx:1.13.8-alpine
COPY ./default.conf /etc/nginx/conf.d/default.conf

This setup uses the lightweight Alpine-based Nginx image and copies a configuration file into place. Create the configuration file:

Code
touch nginx/default.conf

Paste the following into default.conf:

Code
server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    root /app;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

The line fastcgi_pass php:9000; links to the PHP container by service name. Docker Compose creates an internal network where each service is accessible by its name.

Now build the Nginx container image:

Console
docker build -t centron-nginx nginx/

Expanding the docker-compose.yml File

Update the app/docker-compose.yml file to include the web server:

YAML
version: '2'
services:
  php:
    image: centron-php
    volumes:
      - ./:/app
    working_dir: /app
  web:
    image: centron-nginx
    volumes:
      - ./:/app
    depends_on:
      - php
    ports:
      - 80:80

The depends_on ensures the PHP container starts first. ports exposes container port 80 to the host.

Create a simple PHP file at app/index.php:

PHP
<?php phpinfo();

Ensure your firewall allows port 80. Start the containers:

Console
cd ~/docker/app
docker-compose up -d
docker ps

Open your browser and navigate to your server's IP address. Find it by running:

Code
hostname -I

Setting Up the MySQL Container

The MySQL image can be configured using environment variables. Add a new mysql service in docker-compose.yml:

YAML
version: '2'
services:
  php:
    image: centron-php
    volumes:
      - ./:/app
    working_dir: /app
  web:
    image: centron-nginx
    volumes:
      - ./:/app
    depends_on:
      - php
    ports:
      - 80:80
  mysql:
    image: mysql:5.7.21
    volumes:
      - ./:/app
      - dbdata:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=world
      - MYSQL_ROOT_PASSWORD=root
    working_dir: /app
volumes:
  dbdata:

This configuration uses dbdata to persist MySQL data. Docker manages this named volume to retain your database even if the container is destroyed.

Importing the Sample Database

Download and extract the sample world database from the official MySQL resources:

Console
curl -L http://downloads.mysql.com/docs/world.sql.gz -o world.sql.gz
gunzip world.sql.gz

Now start the containers:

Console
docker-compose up -d
docker ps

Import the database into MySQL:

Console
docker-compose exec -T mysql mysql -uroot -proot world < world.sql

Access the MySQL CLI and confirm the data import:

Console
docker-compose exec mysql mysql -uroot -proot world

Inside MySQL, run:

SQL
select * from city limit 10;

You'll see a sample list of cities from the database. To exit MySQL:

Code
exit

Developing the Sample PHP Application

With all required containers now operational, we can shift focus to developing our demo application. Update the app/index.php file with the following content:

PHP
<?php

$pdo = new PDO('mysql:host=mysql;dbname=world;charset=utf8', 'root', 'root');

$stmt = $pdo->prepare("
    select city.Name, city.District, country.Name as Country, city.Population
    from city
    left join country on city.CountryCode = country.Code
    order by Population desc
    limit 10
");
$stmt->execute();
$cities = $stmt->fetchAll(PDO::FETCH_ASSOC);

?>

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>centron Rocks!</title>
</head>
<body>
    <h2>Most Populous Cities In The World</h2>
    <table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Country</th>
            <th>District</th>
            <th>Population</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach($cities as $city): ?>
            <tr>
                <td><?=$city['Name']?></td>
                <td><?=$city['Country']?></td>
                <td><?=$city['District']?></td>
                <td><?=number_format($city['Population'], 0)?></td>
            </tr>
        <?php endforeach ?>
    </tbody>
    </table>
</body>
</html>

Open a browser and go to your server’s IP address ([centron-instance-ip]). You’ll see a table displaying the ten largest cities in the world by population. Well done — you’ve successfully deployed a complete PHP application using Docker!

Final Thoughts

In this guide, you’ve learned how to build and configure a working PHP application environment using Docker. You created custom container images for PHP and Nginx and managed them using Docker Compose. Although simple, this layout closely resembles production-ready Docker environments.

All container images were built and tagged locally. To make your setup more scalable, consider pushing your images to a Docker registry. You can use Docker Hub or create a private registry, which allows for image sharing across multiple hosts.

If you want to expand this setup to support other frameworks or features, you may need to install additional PHP extensions. You can modify the Dockerfile accordingly. Some extensions require extra system libraries to function correctly, so be sure to check the official PHP documentation to understand their dependencies.

For a deeper dive into Docker Compose and advanced features, consult the official documentation.

Jetzt 200 € Guthaben sichern

Testen Sie Ihr Setup auf ccloud³

Registrieren Sie sich in der ccloud³ und erhalten Sie 200 € Startguthaben für Ihr Projekt – z. B. für eine PostgreSQL-VM mit automatischen Backups.

centron Redaktion Technische Redaktion

Das Redaktionsteam von centron schreibt Anleitungen aus dem Betriebsalltag: getestet auf unserer eigenen Plattform, betrieben im Rechenzentrum in Hallstadt bei Bamberg.

Kategorie Linux Basics
Teilen
Noch offene Fragen?

Unser Team hilft Ihnen bei Ihrem konkreten Setup weiter – von Menschen, die die Plattform selbst betreiben.

War dieses Tutorial hilfreich?

Ihre Antwort wird anonym gespeichert und hilft uns, die Tutorials zu verbessern.

Kommentare

Noch keine Kommentare – stellen Sie die erste Frage zu diesem Tutorial.

Zum Kommentieren anmelden

Kommentare stehen centron-Kunden offen. Melden Sie sich in Ihrem Konto an, um eine Frage zu diesem Tutorial zu stellen.

Weiterlesen

Das könnte Sie auch interessieren

Jetzt kostenlos anfangen

Melden Sie sich an und erhalten Sie in den ersten 60 Tagen ein Guthaben von 200 € bei centron.

Dieses Werbeangebot gilt nur für neue Konten. Angebot ausschließlich für Gewerbetreibende.

Jetzt loslegen Sales kontaktieren