Before we start…
The previous post about dockerising a PHP application (which you should read if you haven’t) got a lot more attention than I ever imagined, which is AWESOME. However, while introducing some of the suggested improvements a number discrepencies between the contents of the blog post and the GitHub repository emerged. Buuut…
The death of php-docker.local
The initial setup included a step that required you to update your hosts file and add an entry for php-docker.local. I received feedback from several people that this step is not completely clear and they ended up skipping it. It turns out this step can be removed easily, so why don’t we just go ahead and do it :)
Two things are needed to achieve this. First, we have to update the site.conf in order to handle the connections to localhost. Second, the default configuration in the Nginx image should be replaced with our new config.
For the first part we have to replace the server_name setting with:
server_name localhost;
and add the following setting:
listen 80;
Now our site.conf will look like this:
server {
listen 80;
index index.php index.html;
server_name localhost;
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 replace the default Nginx config we have to mount our site.conf in its place. To achieve that we have to tweak our docker-compose.yml a bit. The end result will look like this:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
- ./site.conf:/etc/nginx/conf.d/default.conf
links:
- php
php:
image: php:7-fpm
volumes:
- ./code:/code
For those who missed it, we changed one of the volumes for the web container with the following:
- ./site.conf:/etc/nginx/conf.d/default.conf
Now your PHP application will be accessible on any domain pointing to your Docker host.
Docker-compose v2
For a while now docker-compose supports version 2 for the docker-compose files, which adds some improvements to the setup. Let’s see how the docker-compose.yml will look like using the new format:
version: 2
services:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
- ./site.conf:/etc/nginx/conf.d/site.conf
php:
image: php:7-fpm
volumes:
- ./code:/code
This doesn’t look much different, except that we don’t have to specify the links between the containers. Docker-compose adds all the containers to the same network and they are “linked” by default. This is especially useful when you add more containers to the setup (e.g. database, cache, queue, etc.) since you don’t have to worry about specifing the links between containers.
Another thing the we can do using version 2 of the docker-compose files is to specify networks for the containers. For example:
version: 2
services:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
- ./site.conf:/etc/nginx/conf.d/default.conf
networks:
- code-network
php:
image: php:fpm
volumes:
- ./code:/code
networks:
- code-network
networks:
code-network:
driver: bridge
This option allows grouping different containers in different networks based on the services they need to connect to. In our setup this is not needed, but I believe it’s an important feature to be aware of, especially when expanding the setup with more services.
Special thanks to cipriantepes, who contributed to the repository with this improvement.
PHP logging to stdout
This is something trivial but I never really thought it’s needed until I got several requests and even a PR for it. The issue is that the default php-fpm Docker image is not configured to log the errors. The fix is to enable logging.
First, let’s add a log.conf file with the following content:
php_admin_flag[log_errors] = on
php_flag[display_errors] = off
Next, add this to the configuration of the PHP container:
version: '2'
services:
web:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./code:/code
- ./site.conf:/etc/nginx/conf.d/default.conf
networks:
- code-network
php:
image: php:fpm
volumes:
- ./code:/code
- ./log.conf:/usr/local/etc/php-fpm.d/zz-log.conf
networks:
- code-network
networks:
code-network:
driver: bridge
So… what happened? Well, we mounted our new log file in the PHP container, but we added a zz- prefix to it. Why? Because we want to have this configuration loaded last, so that it’s not overriden by the rest of the configs.
Here’s the line for the curious:
- ./log.conf:/usr/local/etc/php-fpm.d/zz-log.conf
Since this wasn’t part of the initial blog post, it resides in a separate branch – feature/log-to-stdout.
Final summation
I guess no matter how good you make something, there’s always room for improvement. In that sense, any remarks and PRs are more than welcome :)
You can find the updated code here – https://github.com/mikechernev/dockerised-php
Hi.
Great article. I started using Docker few weeks ago. It’s much better approach than using Vagrant. But I have a question.
Do You have good schema to build Production Docker env for hosting many applications?
I can configure one nginx, php-fpm container and add vhosts and pools. But is it good practice? How do it better?
Have a look at Kubernetes ;)
I have a “403 Forbidden” error.
What could be the problem?
Hey Otto,
Make sure that all your configurations are set properly. You might be missing the code volume in the PHP-FPM container, but I can’t tell for sure without the code. I would suggest comparing your code to the one in the repository – https://github.com/mikechernev/dockerised-php and see if that would give you any clues.
Cheers,
Mike
I’m sorry, my fault.
There were problems with file permissions.
Hi
Thanks for this post.
Can anyone help me connect to this docker-compose file MailHog docker container? I need to catch outgoing mail with MailHog.
I have index.php with this code:
I add this code to docker-compose.yml but it didnt work:
Hey,
Can you paste a gist or some kind of code sample?
The index.php content is missing from your comment and it makes it difficult to figure out what the problem might be.
Thanks :)
Sorry php code not displayed
Hi,
I tried your example now, but for me, it ends up in showing default Nginx page. Php page not loaded.
Have any tips ..? I’ve used the exact same code of yours.
Thank you
Hello,
Great tutorial !
I have only one question, when I scale with docker compose, i scale nginx and php, but, nginx go only to one container php.
my docker-cmpose :
version: ‘2’
networks:
testappnetwork:
driver: bridge
traefik:
external:
name: traefik
services:
testapp:
image: nginx:latest
volumes:
– ./code:/code
– ./site.conf:/etc/nginx/conf.d/default.conf
networks:
– testappnetwork
– traefik
labels:
– “traefik.frontend.rule=Host:testapp.anorak.ovh”
– “traefik.port=80”
– “traefik.backend=testapp”
– “traefik.frontend.entryPoints=http,https”
– “traefik.docker.network=traefik”
php:
image: php:7-fpm
volumes:
– ./code:/code
networks:
– testappnetwork
labels:
– “traefik.enable=false”
Can you help me please ?
hey, thank you for this awesome tutorial! The only thing what drives me a bit crazy is how to add one of these MySQLi, MySQL, PDO_MySQL to the php container.
would be awesome to add here ;)
I have the same problem as phil, how do we add these to the PHP container ?
Hey Adrian,
Can you see if the reply to Phil’s comment works for you too :)
Cheers,
Mike
Hey Phil,
You can add those to the image. You can find an example here https://github.com/mikechernev/php7-fpm-laravel.
If you want to use my prebuild image, you can see an example of how a Laravel application is dockerised (including a MySQL driver) here https://github.com/mikechernev/dockerised-canvas.
I hope this helps :)
Cheers,
Mike
thank you, i am going to try that! :)
my idea was to make a link to a local php.ini file which overwrites the php.ini file in the php container but i didnt get it to work :/ would be slick because i see no other way to change php options without building a new container, right?
uff, sadly it wont work. i used your php7-fpm-laravel/Dockerfile and pasted it in the compose file instead of the image:
php:
build: ./PHP_FPM_Dockerfile
volumes:
– ./code:/code
networks:
– code-network
but docker-compose up gives me always this weirdo error:
http://pastiebin.com/embed/59861a94ec876
woah, finally it’s working for me (replaced the standard fpm version with yours). thx very much for your tutorials!
cheers,
n.
Hi this is great post since i’m a guy kind who only believe if you use the official source. Most of tutorial will use image from unofficial which make me uncomfortable but using nginx:latest make me feel good about this since I’m totally new to Docker. But I wonder what the official image for phpfpm? is php:7-fpm or php:fpm
which one is the stable one
How can i enable the ldap extension to PHP?
I try to build a new image based on the php7.0-fpm but the extension has not been activated…. (apt-get install php7.0-ldap)
There is other way to do this ?
Hey Mike!
Thanks you very much for this and previous articles. There aren’t that much articles online on how to achieve this. It was a great help for me.
This is a great development environment solution, but shouldn’t the application code be removed from volumes when dealing with production?
The only solution I can find is to create an NGINX image with the application code copied and a PHP image with the application code copied. Which takes away the perk of using the latest and greatest official images. Do you have an ideas on how to get around this?
(Your example also works well using version 3 and running the integrated docker stack commands.)
Yes your described situation is best practice. But this is almost never used by the php comunity. They missunderstand the proper use of docker.
So the only solutions i see are:
– Use php-apache image-tag
– Create own Dockerfile installing php-fpm inside nginx or nginx inside php-fpm
– Copy application data to both containers but this is totaly bullshit in my opinion
Just wanted to say thanks! This was very helpful.
I used a docker-compose file version 3 and everything worked normally. Are there any signifigant changes using version 3 that I may be missing?
Hi, really a great and helpful article!
I have a problem I can’t figure out how to solve: I need to access a local JSON file on the nginx server from php. If I try to curl the resource from my host (mac), it works fine, but when I try to curl it with php, I have the following output on the console:
php_1 | [24-Apr-2018 04:31:12] WARNING: [pool www] child 5 said into stderr: “* Trying 127.0.0.1…”
php_1 | [24-Apr-2018 04:31:12] WARNING: [pool www] child 5 said into stderr: “* TCP_NODELAY set”
php_1 | [24-Apr-2018 04:31:12] WARNING: [pool www] child 5 said into stderr: “* connect to 127.0.0.1 port 8080 failed: Connection refused”
I guess that it is a routing problem inside the docker container. Changing the port from 8080 to 80 does not help. Any suggestion? Thanks!
Sorry, after hours of research I found the solution just after posting the comment. Using “host.docker.internal” instead of “localhost” works! I found this hint here: https://stackoverflow.com/questions/24319662/from-inside-of-a-docker-container-how-do-i-connect-to-the-localhost-of-the-mach
Thank you!
Hi, just wondering, why should both php and nginx have access to the ‘code’ dir? Shouldn’t it be sufficient with just one of them? (don’t know which though)
Nice article!
I have a doubt… if I go to 0.0.0.0:8080 it shows the Nginx index and to see the PHP file I have to go to 0.0.0.0:8080/index.php
Any idea how to change that to load PHP index when entering on 0.0.0.0:8080?
I read and understand it, but I have a simple question, now
1. How’d I create a single image of all these stuff (nginx, php-fpm and application codebase) and push it to ECS.?
2. How’d I setup nginx, php-fpm and code to ECS.
3. How can I make changes in existing image and push updated changes to AWS.
I will thank you for quick reply.
Thanks for the article. One thing I’d like to see is how to run everything as a non-privileged uid:gid and specify the uid:gid on the command line.
Hey Mike!
woah, finally it’s working for me . Thanks you very much for this and previous articles. thx again very much for your tutorials!
Hello very nice tutorial. Can you add mysql service also in your next tutorial and how to make database and connect with it?
Thank You
Thanks For Give Information PHP …Really nice blog