There is a new version of this tutorial available for Ubuntu 22.04 (Jammy Jellyfish).

Dockerizing Wordpress with Nginx and PHP-FPM on Ubuntu 16.04

Docker-Compose is a command line tool for defining and managing multi-container docker containers as if they were a single service. Compose is written in python and can be installed with the Python pip command. With compose, we can run multiple docker containers just with a single command. It allows you to create a container as a service, great for your development, testing and staging environment.

In this tutorial, I will guide you step-by-step to use docker-compose. We will deploy 'Wordpress' with Nginx, MySQL, and PHP-FPM. Each service has its own container, and we will use images from the docker hub registry. I will show you how to create containers from docker images and manage all containers with docker-compose.

Prerequisite

  • Ubuntu 16.04
  • Root Privileges

Step 1 - Install Docker

We will start from scratch, by installing docker and docker compose manually with the apt command.

Before we begin, update the Ubuntu repository and install latest updates:

sudo apt-get update
sudo apt-get upgrade

By default, docker is available in the Ubuntu repository, so we can continue to install it right away:

sudo apt-get install -y docker.io

When the installation is done, start docker and add it to start automatically at boot time:

systemctl start docker
systemctl enable docker

Now test your docker installation with the command below:

docker run hello-world

You will see hello-world from docker.

Step 2 - Install Docker-Compose

Docker-compose is a script written in python, it's available in the PyPI python repository and can be installed with python pip. So we need to install python and python pip on our system first.

Install python and python-pip:

sudo apt-get install -y python python-pip

Next, install docker-compose with the pip command:

pip install docker-compose

wait for the installation process to finish. Then check the installation with the docker-compose command:

docker-compose -v

You will get the docker-compose version.

Step 3 - Setup Wordpress

Now, docker and docker-compose are installed on the system. In this step, will show you how to create and setup the docker-compose environment for our WordPress project.

We will deploy the 'Wordpress' PHP application with Nginx as the web server, and MariaDB for the MySQL database as docker containers managed by docker-compose. Each application (Wordpress, Nginx, and MySQL) will run in its own container, you can see the list below:

- Nginx: We use the official docker image, latest version 'nginx: latest'.

- Wordpress: Wordpress provides some docker images on docker-hub, and we will use WordPress 4.7 with PHP-FPM 7.0 on it.

- MySQL: We will use MariaDB official container, latest version.

So we need 3 docker images from the docker hub registry.

We will not run docker as root, we will use normal Linux user. So just create a new user with command below (feel free to use a different username here, just ensure the user does not exist yet. If you choose a different name, ensure to change it in all commands that follow in this tutorial):

useradd -m -s /bin/bash hakase
passwd hakase

Now add the user to the 'docker' group so the user can use the docker command, and restart the docker service:

usermod -a -G docker hakase
systemctl restart docker

Login to the user 'hakase' and create a new directory for the WordPress project:

su - hakase
mkdir -p wordpress-compose
cd wordpress-compose/

Next, create a new file called 'docker-compose.yml', and create a new directory for the project. Just type the commands below:

touch docker-compose.yml
mkdir -p nginx/
mkdir -p db-data/
mkdir -p logs/nginx/
mkdir -p wordpress/

File and Directory List of the project:

- docker-compose.yml: This is the docker-compose configuration file, you must create it when starting new docker-compose project.

- nginx/: This directory is used for our additional nginx configuration like the virtual host etc.

- db-data/: The volume/directory for the mysql data. The sql from data '/var/lib/mysql' is mounted to db-data directory.

- logs/: Directory for application log, nginx, mariadb and php-fpm.

- wordpress/: All wordpress files will be available in that directory.

In the 'nginx' directory, create a new configuration file for our wordpress virtual host.

Create a new file wordpress.conf:

vim nginx/wordpress.conf

Paste configuration below:

server {
    listen 80;
    server_name wp-hakase.co;

    root /var/www/html;
    index index.php;

    access_log /var/log/nginx/hakase-access.log;
    error_log /var/log/nginx/hakase-error.log;

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

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

Save the file and exit vim.

Step 4 - Configure Docker-Compose

In this step, we will start editing the docker-compose.yml file. When you want to start the docker-compose project, make sure you create the docker-compose.yml file first like we do it below.

Edit docker-compose.yml with vim:

vim docker-compose.yml

Define your services, on the first line we will define Nginx. We are using Nginx official docker image, the latest version, and configure port mapping for port 80 on the container to port 80 on the host. Next, configure the docker volumes, the volume for our Nginx virtual host configuration, volume for Nginx log files and the web root directory volume '/var/www/html'. The Nginx container is linked to WordPress container.

Paste configuration below:

nginx:
    image: nginx:latest
    ports:
        - '80:80'
    volumes:
        - ./nginx:/etc/nginx/conf.d
        - ./logs/nginx:/var/log/nginx
        - ./wordpress:/var/www/html
    links:
        - wordpress
    restart: always

Next, define the MySQL server. We are using the MariaDB image, latest version. Configure port mapping for the container on port 3306, and configure the MySQL root password with the environment variable 'MYSQL_ROOT_PASSWORD'. Finally, configure the container volume for the MySQL data directory.

Paste configuration below:

mysql:
    image: mariadb
    ports:
        - '3306:3306'
    volumes:
        - ./db-data:/var/lib/mysql
    environment:
        - MYSQL_ROOT_PASSWORD=aqwe123
    restart: always

Then we will configure the WordPress service by using the WordPress 4.7 docker image with PHP-FPM 7.0 installed. Configure the port for PHP-fpm on port 9000, enable the docker volume for the web directory '/var/www/html' to the host directory 'wordpress', setup the database by defining WordPress environment variable, and link the WordPress service to mysql.

Paste configuration below:

wordpress:
    image: wordpress:4.7.1-php7.0-fpm
    ports:
        - '9000:9000'
    volumes:
        - ./wordpress:/var/www/html
    environment:
        - WORDPRESS_DB_NAME=wpdb
        - WORDPRESS_TABLE_PREFIX=wp_
        - WORDPRESS_DB_HOST=mysql
        - WORDPRESS_DB_PASSWORD=aqwe123
    links:
        - mysql
    restart: always

After adding the three parts into the docker-compose.yml file, save the file and exit the vim editor.

Our docker-compose configuration is ready.

Step 5 - Run Docker-compose

Start to create the new containers with docker compose. Go to the wordpress-compose directory and start the new containers based on our compose file.

cd ~/wordpress-compose/
docker-compose up -d

You can see the results of the command. Three containers were created. Let's check the container status with the ps option:

docker-compose ps

Below is the result:

Start Docker-compose Wordpress

If you want to see the log output from the container, you can use commands below:

docker-compose logs nginx
docker-compose logs mysql
docker-compose logs wordpress

Container logs:

an example for docker-compose logs command

Note:
If you see in the WordPress container log an error about MySQL connection refused, ignore it.

Step 6 - Install Wordpress

Before we do this step, let's check the available ports/open ports on the system. Make sure you have 3 ports opened, port 80, 3306 and port 9000.

netstat -plntu

The results are below:

Port open docker

Now open your web browser and type in the server URL or IP address.

http://serverIP/

You can see the WordPress installation page. Choose your language and click 'Continue'.

Wordpress Installation set languange

Fill in your website details like site title, admin user and password, your email address and then click 'Install Wordpress'.

Install Wordpress Fill User and Site Configuration

You will be redirected to the 'Wordpress Admin Dashboard'.

Wordpress Admin Dashboard Docker-Compose

And this is my WordPress sample post hello world.

Wordpress woth Docker-Compose

Wordpress has been installed with docker-compose.

Step 7 - Accessing the Docker Container

This is an additional step on how to access a container with docker-compose. Check that all containers are available and show their status:

docker-compose ps

We already have 3 containers, now we can try to login to each container. Log in to the first Nginx container with docker-compose command.

docker-compose exec nginx bash

nginx: service name on the docker-compose file docker-compose.yml

bash: execute the bash shell command

Now check our WordPress virtual host configuration.

cat /etc/nginx/conf.d/wordpress.conf

Nginx docker compose service

The files are available in the container.

Next, try login to the mysql container, and then log into the mysql shell with our password on the compose file.

docker-compose exec mysql bash
mysql -u root -p
TYPE MYSQL ROOT PASSWORD

See the list of databases:

show databases;

You will see our WordPress database.

MySQL Docker Compose service

In the 'wordpress' container, you will see all WordPress files.

docker-compose exec wordpress bash
ls -lah

Wordpress with PHP-FPM Docker Compose

All containers are accessible.

Reference

Share this page:

Suggested articles

26 Comment(s)

Add comment

Comments

By: TiTex

docker.io package is alwasy outdated , you should check this page to install latest docker https://docs.docker.com/engine/installation/linux/ubuntu/

usermod -a -G hakase dockershould be usermod -a -G  docker hakase

 

 

Next, create new file called 'docker-container.yml', and create a new directory for the project. Just type the commands below:should beNext, create new file called 'docker-compose.yml', and create a new directory for the project. Just type the commands below:

 

you do not need to expose ports on the host for mariadb and php-fpm , if you don't want to do anything with them

    ports:        - '9000:9000'     ports:        - '3306:3306

By: nam

some small mistake in command when add user to docker group

"usermod -a -G hakase docker" 

usermod -a -G docker hakase

By: till

Thank you @TiTex and @nam. I've corrected the tutorial.

By: David Dumonde

I haven't been able to create more than one wordpress site on a single host using docker-compose. Any suggestions for creating multiple wp containers with this setup?

By: PokePango

Don't know if it's the best solution but you could create another Nginx docker that will use the port 80. It would be the only one to use this port and all the others Nginx docker will pass through it.

By: TiTex

you need to change each nginx container's ports to something unique like

    ports:        - '8080:80'

    ports:        - '8081:80'

By: David Dumonde

Thanks, @TiTex, that works in a sense, but it will leave the url exposed as example.com:8080 or example.com:8081. Without docker, I can set up nginx server blocks for each domain so that they listen :80, and then are separated by their root, such as root /var/www/html/site1 or root /var/www/html/site2 without needing separate ports. So I don't think the port settings are the answer I'm looking for, but I appreciate your help.Right now I'm looking at how to change the location block setting for different sites to fastcgi_pass site1:9001 or fastcgi_pass site2:9002. I got it to work with two sites, then when I added a third site I started getting 502 bad gateway errors. I'm still tinkering.

By: Marc

Hey David, did you finally worked it out? I'm struggling with a very similar issue by using multiple compose configurations.Any information / update on this would be wonderful :)

By: ofthesun9

Thanks for this clear & simple howto !

What would be the changes required to have wordpress install in "mysite.com/blog" instead of mysite.com ?

By: Joe Sixpack

Great tutorial. Thanks for posting and making docker easy. One feature request is to have phpmyadmin added to the docker-compose mix. 

By: RonNa

I followed exactly above. It worked ;-)

I want to add phpmyadmin (complete compos file):

nano docker-compose.yml-------------------------------------version: '2'services:   nginx:     image: nginx:latest     ports:       - '80:80'     volumes:       - ./nginx:/etc/nginx/conf.d       - ./logs/nginx:/var/log/nginx       - ./wordpress:/var/www/html     links:       - wordpress     restart: always   mysql:     image: mariadb     ports:       - '3306:3306'     volumes:       - ./db-data:/var/lib/mysql     environment:       - MYSQL_ROOT_PASSWORD=aqwe123     restart: always   wordpress:     image: wordpress:4.7.1-php7.0-fpm     ports:        - '9000:9000'     volumes:       - ./wordpress:/var/www/html     environment:       - WORDPRESS_DB_NAME=wpdb       - WORDPRESS_TABLE_PREFIX=wp_       - WORDPRESS_DB_HOST=mysql       - WORDPRESS_DB_PASSWORD=aqwe123     links:       - mysql     restart: always   phpmyadmin:     depends_on:       - mysql     image: phpmyadmin/phpmyadmin     restart: always     ports:       - 8081:80     environment:       PMA_HOST: mysql       MYSQL_ROOT_PASSWORD: aqwe123     networks:       - backnetworks:   back:volumes:   db_data:-------------------------------------

However, if I try phpmyadmin it says host mysql not found. Where do I think wrong?

 

 

By: Denver Prophit Jr.

It would be really nice if you focused on https only with http2 instead of non https wordpress. Insecure login is very bad.

By: Paul

I tried this on my mac and getting a 502 gateway error. Been trying lots of different configurations with no luck.

By: nbrb

502 error is caused by the following error:

request: "GET /info.php HTTP/1.1", upstream: "fastcgi://172.17.0.3:9000"

as you can see it is using ip 172.17.0.3 from hosts file on nginx container

 

By: Senrabdet

This post is really helpful.  It worked for me (eventually would like to add in the ssl stuff, but actually prefer just http as a way to get started).  I am trying to add in a reverse proxy, so one could have a single public ip and multiple instance of lamp/wordpress with logins as distint upstream docker instances.  My assumption is this would include being done with locations.  Has anyone done this?  My attemps so far are unsuccessful.  Can share my code attempts if that would help?  Thx

By: Apurva Bhandari

I did exactly the same as mentioned above as it is but not working at all.

nginx:

    image: nginx:latest

    ports:

        - '80:80'

    volumes:

        - ./nginx:/etc/nginx/conf.d

        - ./logs/nginx:/var/log/nginx

        - ./wordpress:/var/www/html

    links:

        - wordpress

    restart: always

 

mysql:

    image: mysql

    ports:

        - '3306:3306'

    volumes:

        - ./db-data:/var/lib/mysql

    environment:

        - MYSQL_ROOT_PASSWORD=root123

    restart: always

 

wordpress:

    image: wordpress:4.7.1-php7.0-fpm

    ports:

        - '9000:9000'

    volumes:

        - ./wordpress:/var/www/html

    environment:

        - WORDPRESS_DB_NAME=wpdb

        - WORDPRESS_TABLE_PREFIX=wp_

        - WORDPRESS_DB_HOST=mysql

        - WORDPRESS_DB_PASSWORD=aqwe123

    links:

        - mysql

    restart: always

 

here is output;

502 Bad Gateway

i tried with serverip/localhost/127.0.0.1/172.17.0.1 but same error

nginx/wordpress.conf

server {

    listen 80;

    server_name wp-abc.co;

 

    root /var/www/html;

    index index.php;

 

    access_log /var/log/nginx/access.log;

    error_log /var/log/nginx/error.log;

 

    location / {

        try_files $uri $uri/ /index.php?$args;

    }

 

    location ~ \.php$ {

        try_files $uri =404;

        fastcgi_split_path_info ^(.+\.php)(/.+)$;

        fastcgi_pass wordpress:9000;

        fastcgi_index index.php;

        include fastcgi_params;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        fastcgi_param PATH_INFO $fastcgi_path_info;

    }

}

 

logs/nginx/error.log

[error] 5#5: *20 connect() failed (111: Connection refused) while connecting to upstream, client: 172.17.0.1, server: wp-abc.co, request: "GET / HTTP/1.1", upstream: "fastcgi://172.17.0.3:9000", host: "localhost"

I don't know where is the issue. Please help me out for the configuration.

By: Sascha

I have the exact same problem with Ubuntu 18.04 and the same configuration now. How did you fixed it?

By: patrick

Bonjour,

je cherche ou se trouve php.ini car je suis limité a 2Mo 

Merci

 

ps: c'est le seul tuto qui fonctionne pour moi.

By: Raj

Great Explanation.

By: Patrick

I have the same problem.

Did you solved it?

If yes, what is the solution?

By: Sascha

I'm also looking for the solution of this problem. Did you solved it?

By: Philippe

Hello,

I am a bit confused about volumes;

Can anybody explain why 

    volumes:

        - ./wordpress:/var/www/html

Is needed for php-fpm and nginx, and how it does work behing the scenes ?

 

Thanks 

By: Noy Tsarfaty

Amazing! Very explicit and helpful. Thanks a lot :) 

By: Tobias

Hello,

I checked out the code but it is not working on my mac os in the browser. 

Any idea??

"This site can’t be reached

Check if there is a typo in wp-hakase.co.

DNS_PROBE_FINISHED_NXDOMAIN"

By: asuran

what will be the server ip?

how to find my server ip address?

 

By: srikanth

Hello Sir ,

Can you the same POC with separate compose files for each like nginx-php-fpm compose , sql compose and wordpress compose file please i am trying that please help me.

 

Regards,

Srikanth