Before we start…
Before we start, we have to agree on one thing – Docker is super cool! If you are not familiar with Docker, I suggest to have a look at the tons of “Getting starting with Docker” or “What is Docker?” articles and then come back here. :)
Since you keep reading, I will assume that you already have some Docker experience and you want to run your PHP applications in containers. Because who wants the trouble of installing all the dependencies on their local environment or manage a number of virtual machines for their different projects, right? Right!
The goal that we will try to achieve is to run a simple PHP application using the official Docker repositories for both PHP and Nginx. There are several docker repositories combining PHP-FPM with Nginx, but depending on the official repositories gives you several benefits, like using a service which is configured by its maintainers and you can always choose between the latest and greatest or different versions of both services, instead of relying on someone else’s choices.
The first thing you have to do is, of course, install Docker (if you haven’t already). The second prerequisite is getting Docker Compose (it is included in the Mac toolbox). Now that we know what we want to achieve and have the tools to accomplish it – let’s get our hands dirty!
Setting up Nginx
We’ll start by getting ourselves a web server and based on our requirements this will be a container running the official Nginx image. Since we’ll be using Docker Compose, we will create the following docker-compose.yml file, which will run the latest Nginx image and will expose its port 80 to port 8080:
web:
image: nginx:latest
ports:
- "8080:80"
Now we can run
docker-compose up
This should give you the default Nginx screen on port 8080 for localhost or the IP of your docker machine.
Now that we have a server let’s add some code. First we have to update the docker-compose.yml to mount a local directory. I will use a folder called code, which is in the same directory as my docker-compose.yml file, and it will be mounted as root folder code in the container.
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
The next step is to let Nginx know that this folder exists.
Let’s create the following site.conf on the same level as the docker-compose.yml file:
server {
index index.html;
server_name php-docker.local;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /code;
}
If you don’t have a lot of experience with Nginx, this is what we define here – index.html will be our default index, the server name is php-docker.local and it should be pointing (update your hosts file) to your Docker environment (localhost if you are on Linux or the docker machine if you are on Mac or Windows), we point the error logs to be the ones exposed by the default container, so that we will see the errors in our docker compose log, and finally we specify the root folder to be the one that we mounted in the container.
To activate this setup we need to apply yet another modification to our docker-compose.yml file:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
- ./site.conf:/etc/nginx/conf.d/site.conf
This will add site.conf to the directory where Nginx is looking for configuration files to include. You can now place an index.html file in the code folder with contents that is to your heart’s delight. And if we run
docker-compose up
again, the index.html file should be available on php-docker.local:8080.
Yeey! We are half way there
Adding PHP-FPM
Now that we have Nginx up and running let’s add the PHP in the game. The first thing we’ll do is pull the official PHP7-FPM repo and link it to our Nginx container. Our docker-compose.yml will look like this now:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
- ./site.conf:/etc/nginx/conf.d/site.conf
links:
- php
php:
image: php:7-fpm
The next thing to do is configure Nginx to use the PHP-FPM container for interpreting PHP files. Your updated site.conf should look like this:
server {
index index.php index.html;
server_name php-docker.local;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /code;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.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;
}
}
In order to test this let’s rename the index.html file to index.php and replace its content with the standard:
<?php
echo phpinfo();
One final
docker-compose up
And we should be good to go... but
Instead of getting the proper PHP info page we receive the rather unsettling
File not found.
Since PHP is running in its own environment (container) it doesn't have access to the code. In order to fix this, we need to mount the code folder in the PHP container too. This way Nginx will be able to serve any static files, and PHP will be able to find the files it has to interpret. One final change to the docker-compose.yml:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
- ./site.conf:/etc/nginx/conf.d/site.conf
links:
- php
php:
image: php:7-fpm
volumes:
- ./code:/code
Finally, this last (this time for real)
docker-compose up
present us with the much wanted PHP info
This is it.
We can run any simple PHP application inside Docker containers, using the official images for Nginx and PHP.
You can find the sample project here https://github.com/mikechernev/dockerised-php
EDIT: Since the GitHub repository changed quite a lot, I added a new blog post explaining the improvements - Making your dockerised PHP application even better
Hi,
Thanks for making this tutorial. I’ve followed the instruction before, maybe 3 weeks ago, it was working back then. But now when I run docker-compose up this error comes out.
ERROR: for nginx rpc error: code = 2 desc = “oci runtime error: could not synchronise with container process: not a directory”
Traceback (most recent call last):
File “”, line 3, in
File “compose\cli\main.py”, line 63, in main
AttributeError: ‘ProjectError’ object has no attribute ‘msg’
docker-compose returned -1
Hey,
As with Liam, I would suggest checking out the latest changes from this repository – https://github.com/mikechernev/dockerised-php
One more thing you could do is update the latest Nginx image by running
I hope this will help you :)
Cheers,
Mike
Hi.
This was so helpful. thanks a lot
I shared my repo for everyone to use it.
https://github.com/mortezaPRK/docker-php_fpm-nginx
I included mcrypt,curl,imagick,mysqli,pgsql and gd php extentions .
Thanks for this tutorial, I have followed this to the letter but it seems as though it is still trying to use the /usr/share/nginx/html/ directory rather than the ./code directory
Any ideas?
Hey,
I can’t help much without seeing the code, but maybe you can compare it to the repository which is alongside this tutorial – https://github.com/mikechernev/dockerised-php.
If this doesn’t give you a clue you can find me as MikeChernev on FreeNode I’ll be more than happy to help ;)
Cheers,
Mike
Hi site.conf is not a directory it is a file. the volume mapping is wrong. make site.conf on the root folder and that should work…
I had the same issue as well. It’s loading that from default.conf. I changed the line which copies site.conf in docker-compose.yml to the following and it worked:
– ./site.conf:/etc/nginx/conf.d/default.conf
Basically it’s replacing default.conf with site.conf
Hey Asim,
I added a lot of updates described here Making your dockerised PHP application even better. It includes this change as well and it has the arguments behind it :)
Cheers,
Mike
Very helpful ! searching for this, can understand more about docker
thanks Platypus! *i don’t even know ur name
I’m glad I could help :)
That was the droid I’m looking for ;) Thank you!
Missing php port in docker-compose.yml:
“`
php:
image: php:7-fpm
ports:
– “9000:9000”
volumes:
– ./html:/app
“`
Hey Thiago,
Thanks for the suggestion. The reason why I haven’t added this is because you need the ports if you want to connect to the PHP container from your host machine.
In this setup, all I need is for the Nginx container to connect the PHP one and that is already possible, since they share the same network internally.
I hope this clears things out for you ;)
Cheers,
Mike
Straight to the point, no fuss, and perfectly clear.
As a newbie to Docker, you helped me a lot, so thanks, sir.
Hello,
I need some help
I followed this tuto to build my php application on docker but it doesn’t work when I lauch my website
Hey Laura,
Can you provide a bit more info?
Maybe a link to your project in GitHub :)
Thanks,
Mike
Really helpful and easy enough!
Thanks for that :)
Veryyyy helpful!!!
Thx for that, really save my day!!
Great article. Really helps you getting started with docker!
you are the fucking best. thanks. read a ton of articles on trying to set up a nice PHP environment with docker and yours is the only one that explained things in a -real- manner, not with confusing useless info. your code was the only code that actually worked. thanks ;__;
Man,
You are awesome :D I loved the GIF’s, they made the article interesting.
Keep it up!
Platypus, your tutorial is wonderful. Definitely the BEST one I have found. I only had to make _one change_ to get it to work. For some reason, when I left it as you have it I got “502 Bad Gateway” and this was in my nginx logs:
2017/03/22 02:50:17 [error] 6#6: *2 connect() failed (111: Connection refused) while connecting to upstream, client: 172.17.0.1, server: php-docker.local, request: “GET / HTTP/1.1”, upstream: “fastcgi://192.168.0.1:9000”, host: “php-docker.local:8080”
I wasn’t sure where it was getting “192.168.0.1” from so I ran “docker exec -it ip a | grep eth0” on my host to figure out the IP address of the “php” container (it was “172.17.0.3” in my case). With that, I updated the row in site.conf from “fastcgi_pass php:9000;” -> “fastcgi_pass 172.17.0.3:9000;”.
Do you know why this was necessary? I’m running Docker for Mac… on a Mac… :P
I’m worried I’ll run into trouble if I hardcode the IP address like this.
One more question. How do I install additional libraries on the “php” container? For example, I want to run “apt-get install php5-mysql” but the problem is that you have to restart fpm to do it. Since the “CMD” of that container is “php-fpm” it looks like you can’t restart fpm without killing the container.
Nevermind on my original question, I missed the “links:” part :)
Question for installing additional libraries like “php5-mysql” still stands though.
Hey Sean,
I am happy that you managed to resolve your initial issue :)
As for installing additional libraries, it can be done via
Sadly, you will have to build your own image with the dependencies you need and then use that one in your docker compose file.
If you are looking for inspiration, you can have a look at https://github.com/mikechernev/php7-fpm-laravel, or just use the image which is a result of that:
It comes with all the Laravel dependencies (e.g. mysql, mcrypt, mbstring, etc).
I hope this helps and if you have more questions, feel free to ask :)
Cheers,
Mike
Thank you for this article!
Wonderful article! So clear and concise. Thank you!
thanks. nice info
hello sir,
i wanna ask something,
in this part
If you don’t have a lot of experience with Nginx, this is what we define here – index.html will be our default index, the server name is php-docker.local
can you tell us where exactly is location/path index.html
thanks
lovely intro … and makes it look easy…. looking forward to the next level up + “adding vagrant to your docker build” cheers