By Victor Bocharsky


2015-04-27 21:08:34 8 Comments

I am trying to link 2 separate containers:

The problem is that php scripts do not work. Perhaps the php-fpm configuration is incorrect. Here is the source code, which is in my repository. Here is the file docker-compose.yml:

nginx:
    build: .
    ports:
        - "80:80"
        - "443:443"
    volumes:
        - ./:/var/www/test/
    links:
        - fpm
fpm:
    image: php:fpm
    ports:
        - "9000:9000"

and Dockerfile which I used to build a custom image based on the nginx one:

FROM nginx

# Change Nginx config here...
RUN rm /etc/nginx/conf.d/default.conf
ADD ./default.conf /etc/nginx/conf.d/

Lastly, here is my custom Nginx virtual host config:

server {
    listen  80;

    server_name localhost;
    root /var/www/test;

    error_log /var/log/nginx/localhost.error.log;
    access_log /var/log/nginx/localhost.access.log;

    location / {
        # try to serve file directly, fallback to app.php
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/.+\.php(/|$) {
        fastcgi_pass 192.168.59.103:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS off;
    }
}

Could anybody help me configure these containers correctly to execute php scripts?

P.S. I run containers via docker-composer like this:

docker-compose up

from the project root directory.

7 comments

@Vincent De Smet 2015-04-29 06:44:08

Don't hardcode ip of containers in nginx config, docker link adds the hostname of the linked machine to the hosts file of the container and you should be able to ping by hostname.

EDIT: Docker 1.9 Networking no longer requires you to link containers, when multiple containers are connected to the same network, their hosts file will be updated so they can reach each other by hostname.

Every time a docker container spins up from an image (even stop/start-ing an existing container) the containers get new ip's assigned by the docker host. These ip's are not in the same subnet as your actual machines.

see docker linking docs (this is what compose uses in the background)

but more clearly explained in the docker-compose docs on links & expose

links

links:
 - db
 - db:database
 - redis

An entry with the alias' name will be created in /etc/hosts inside containers for this service, e.g:

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

expose

Expose ports without publishing them to the host machine - they'll only be accessible to linked services. Only the internal port can be specified.

and if you set up your project to get the ports + other credentials through environment variables, links automatically set a bunch of system variables:

To see what environment variables are available to a service, run docker-compose run SERVICE env.

name_PORT

Full URL, e.g. DB_PORT=tcp://172.17.0.5:5432

name_PORT_num_protocol

Full URL, e.g. DB_PORT_5432_TCP=tcp://172.17.0.5:5432

name_PORT_num_protocol_ADDR

Container's IP address, e.g. DB_PORT_5432_TCP_ADDR=172.17.0.5

name_PORT_num_protocol_PORT

Exposed port number, e.g. DB_PORT_5432_TCP_PORT=5432

name_PORT_num_protocol_PROTO

Protocol (tcp or udp), e.g. DB_PORT_5432_TCP_PROTO=tcp

name_NAME

Fully qualified container name, e.g. DB_1_NAME=/myapp_web_1/myapp_db_1

@Vincent De Smet 2015-04-29 07:08:15

you also don't need to publish port 9000 on the host, ports are open between the linked docker containers, unless you want to troubleshoot the port directly from your host.

@Victor Bocharsky 2015-04-30 06:11:09

Yeas, you're right, thanks. In my case I should use fastcgi_pass fpm:9000 instead of direct ip. I don't know that Docker add it to host automatically, my bad.

@Victor Bocharsky 2015-04-30 06:19:05

What about port, so better to use expose instead of ports? Or I could not use any of this ports and expose directives because linked containers will have access to this port?

@Vincent De Smet 2015-04-30 10:48:06

sorry for late reply - I think you may need to use expose, sorry I can't check right now

@Vincent De Smet 2015-04-30 11:02:55

here is full reference of docker-compose links - docs.docker.com/compose/yml/#links I add this info to my answer

@Victor Bocharsky 2015-04-30 12:00:14

OK, It will be useful, thanks!

@therobyouknow 2018-06-13 19:00:19

--links are now obsolete, according to the docker documention that you reference. They are still currently supported but the apparent plan is for them to be obsoleted.

@therobyouknow 2018-06-13 19:03:10

also as per EDIT in answer. And the docker 'expose' statement can be used to connect to a specific port on another container (when used with the hostname) as the question answerer advises.

@myol 2018-09-25 01:32:48

For anyone else getting

Nginx 403 error: directory index of [folder] is forbidden

when using index.php while index.html works perfectly and having included index.php in the index in the server block of their site config in sites-enabled

server {
    listen 80;

    # this path MUST be exactly as docker-compose php volumes
    root /usr/share/nginx/html;

    index index.php

    ...
}

Make sure your nginx.conf file at /etc/nginx/nginx.conf actually loads your site config in the http block...

http {

    ...

    include /etc/nginx/conf.d/*.conf;

    # Load our websites config 
    include /etc/nginx/sites-enabled/*;
}

@Rafael Quintela 2015-10-07 18:35:21

I know it is kind an old post, but I've had the same problem and couldn't understand why your code didn't work. After a LOT of tests I've found out why.

docker-compose.yml

nginx:
    build: .
    ports:
        - "80:80"
    links:
        - fpm
fpm:
    image: php:fpm
    ports:
        - ":9000"

    # seems like fpm receives the full path from nginx
    # and tries to find the files in this dock, so it must
    # be the same as nginx.root
    volumes:
        - ./:/complex/path/to/files/

/etc/nginx/conf.d/default.conf

server {
    listen  80;

    # this path MUST be exactly as docker-compose.fpm.volumes,
    # even if it doesn't exist in this dock.
    root /complex/path/to/files;

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

    location ~ ^/.+\.php(/|$) {
        fastcgi_pass fpm:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Dockerfile

FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/

@Alfred Huang 2016-01-23 14:11:01

WELL DONE!!! That's exact the point! I set the nginx root to an alternative path other than /var/www/html with failure.

@shriek 2016-06-04 12:02:48

Also, just a note that :9000 is the port that's being used in the container not the one that's exposed to your host. Took me 2 hours to figure this out. Hopefully, you don't have to.

@030 2017-04-04 06:59:24

services.fpm.ports is invalid: Invalid port ":9000", should be [[remote_ip:]remote_port[-remote_port]:]port[/protocol]

@Seer 2017-06-02 13:47:42

You don't actually need to include a ports section at all here. You may just need to expose it if it's not already in the image (which it probably is). If you're doing inter-container communication, you shouldn't be exposing the PHP-FPM port.

@cptPH 2018-05-06 20:24:38

Was looking for a solution for AH01071: Got error 'Primary script unknown\n' and that the php-fpm container has to share the same directory with the web nodes was the solution!

@iKanor 2016-03-23 20:14:19

As pointed out before, the problem was that the files were not visible by the fpm container. However to share data among containers the recommended pattern is using data-only containers (as explained in this article).

Long story short: create a container that just holds your data, share it with a volume, and link this volume in your apps with volumes_from.

Using compose (1.6.2 in my machine), the docker-compose.yml file would read:

version: "2"
services:
  nginx:
    build:
      context: .
      dockerfile: nginx/Dockerfile
    ports:
      - "80:80"
    links:
      - fpm
    volumes_from:
      - data
  fpm:
    image: php:fpm
    volumes_from:
      - data
  data:
    build:
      context: .
      dockerfile: data/Dockerfile
    volumes:
      - /var/www/html

Note that data publishes a volume that is linked to the nginx and fpm services. Then the Dockerfile for the data service, that contains your source code:

FROM busybox

# content
ADD path/to/source /var/www/html

And the Dockerfile for nginx, that just replaces the default config:

FROM nginx

# config
ADD config/default.conf /etc/nginx/conf.d

For the sake of completion, here's the config file required for the example to work:

server {
    listen 0.0.0.0:80;

    root /var/www/html;

    location / {
        index index.php index.html;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass fpm:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }
}

which just tells nginx to use the shared volume as document root, and sets the right config for nginx to be able to communicate with the fpm container (i.e.: the right HOST:PORT, which is fpm:9000 thanks to the hostnames defined by compose, and the SCRIPT_FILENAME).

@Aftab Naveed 2016-09-16 06:59:27

Looks like the Data does not get update from host to the containers, and when I do docker ps -a I see the data container stopped, is that an issue?

@iKanor 2016-09-24 11:45:11

That's the expected behaviour. A data-only container does not run any command, and it will just be listed as stopped. Also, the Dockerfile of the data container is copying your sources to the container on build time. That is why they will not be updated if you change the files in the host. If you want to share the sources between the host and the container you need to mount the directory. Change the data service in the compose file to load image: busybox, and in the volumes section enter ./sources:/var/www/html, where ./sources is the path to your sources in the host.

@DavidT 2015-11-11 15:11:12

New Answer

Docker Compose has been updated. They now have a version 2 file format.

Version 2 files are supported by Compose 1.6.0+ and require a Docker Engine of version 1.10.0+.

They now support the networking feature of Docker which when run sets up a default network called myapp_default

From their documentation your file would look something like the below:

version: '2'

services:
  web:
    build: .
    ports:
      - "8000:8000"
  fpm:
    image: phpfpm
  nginx
    image: nginx

As these containers are automatically added to the default myapp_default network they would be able to talk to each other. You would then have in the Nginx config:

fastcgi_pass fpm:9000;

Also as mentioned by @treeface in the comments remember to ensure PHP-FPM is listening on port 9000, this can be done by editing /etc/php5/fpm/pool.d/www.conf where you will need listen = 9000.

Old Answer

I have kept the below here for those using older version of Docker/Docker compose and would like the information.

I kept stumbling upon this question on google when trying to find an answer to this question but it was not quite what I was looking for due to the Q/A emphasis on docker-compose (which at the time of writing only has experimental support for docker networking features). So here is my take on what I have learnt.

Docker has recently deprecated its link feature in favour of its networks feature

Therefore using the Docker Networks feature you can link containers by following these steps. For full explanations on options read up on the docs linked previously.

First create your network

docker network create --driver bridge mynetwork

Next run your PHP-FPM container ensuring you open up port 9000 and assign to your new network (mynetwork).

docker run -d -p 9000 --net mynetwork --name php-fpm php:fpm

The important bit here is the --name php-fpm at the end of the command which is the name, we will need this later.

Next run your Nginx container again assign to the network you created.

docker run --net mynetwork --name nginx -d -p 80:80 nginx:latest

For the PHP and Nginx containers you can also add in --volumes-from commands etc as required.

Now comes the Nginx configuration. Which should look something similar to this:

server {
    listen 80;
    server_name localhost;

    root /path/to/my/webroot;

    index index.html index.htm index.php;

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

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm:9000; 
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

Notice the fastcgi_pass php-fpm:9000; in the location block. Thats saying contact container php-fpm on port 9000. When you add containers to a Docker bridge network they all automatically get a hosts file update which puts in their container name against their IP address. So when Nginx sees that it will know to contact the PHP-FPM container you named php-fpm earlier and assigned to your mynetwork Docker network.

You can add that Nginx config either during the build process of your Docker container or afterwards its up to you.

@treeface 2016-07-24 05:48:07

Also remember to make sure php-fpm is listening on port 9000. This would be listen = 9000 in /etc/php5/fpm/pool.d/www.conf.

@DavidT 2016-07-25 09:03:32

Thanks @treeface good point. I have updated with your comment.

@Phillip 2016-07-06 18:47:36

As previous answers have solved for, but should be stated very explicitly: the php code needs to live in the php-fpm container, while the static files need to live in the nginx container. For simplicity, most people have just attached all the code to both, as I have also done below. If the future, I will likely separate out these different parts of the code in my own projects as to minimize which containers have access to which parts.

Updated my example files below with this latest revelation (thank you @alkaline )

This seems to be the minimum setup for docker 2.0 forward (because things got a lot easier in docker 2.0)

docker-compose.yml:

version: '2'
services:
  php:
    container_name: test-php
    image: php:fpm
    volumes:
      - ./code:/var/www/html/site
  nginx:
    container_name: test-nginx
    image: nginx:latest
    volumes:
      - ./code:/var/www/html/site
      - ./site.conf:/etc/nginx/conf.d/site.conf:ro
    ports:
      - 80:80

(UPDATED the docker-compose.yml above: For sites that have css, javascript, static files, etc, you will need those files accessible to the nginx container. While still having all the php code accessible to the fpm container. Again, because my base code is a messy mix of css, js, and php, this example just attaches all the code to both containers)

In the same folder:

site.conf:

server
{
    listen   80;
    server_name site.local.[YOUR URL].com;

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

    location /
    {
        try_files $uri =404;
    }

    location ~ \.php$ {
        fastcgi_pass   test-php:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

In folder code:

./code/index.php:

<?php
phpinfo();

and don't forget to update your hosts file:

127.0.0.1 site.local.[YOUR URL].com

and run your docker-compose up

$docker-compose up -d

and try the URL from your favorite browser

site.local.[YOUR URL].com/index.php

@Alkaline 2016-07-09 13:24:15

Your nginx config file assumes that your website has only php files. It's a best practice to create an nginx location rule for static files (jpg,txt,svg, ...) and avoid the php interpreter. In that case both the nginx and php containers need access to the website files. @iKanor 's answer above takes care of that.

@Phillip 2016-07-14 18:06:36

Thanks @Alkaline , static files are problem with my original answer. In fact, nginx really needs, at a minimum, the css and js files to be local to that machine in order to work properly.

@leberknecht 2015-07-16 22:31:15

I think we also need to give the fpm container the volume, dont we? So =>

fpm:
    image: php:fpm
    volumes:
        - ./:/var/www/test/

If i dont do this, i run into this exception when firing a request, as fpm cannot find requested file:

[error] 6#6: *4 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream, client: 172.17.42.1, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://172.17.0.81:9000", host: "localhost"

@Victor Bocharsky 2015-07-17 06:12:04

Yes, you are right! We must to share files with fpm and nginx

@Victor Bocharsky 2015-07-17 09:12:43

I have work example with Nginx and PHP-FPM on GitHub

Related Questions

Sponsored Content

20 Answered Questions

28 Answered Questions

[SOLVED] How can I prevent SQL injection in PHP?

13 Answered Questions

[SOLVED] How to list containers in Docker

  • 2013-05-30 15:41:46
  • w00t
  • 622848 View
  • 805 Score
  • 13 Answer
  • Tags:   docker

15 Answered Questions

[SOLVED] Copying files from Docker container to host

36 Answered Questions

[SOLVED] How do I check if a string contains a specific word?

36 Answered Questions

[SOLVED] Copying files from host to Docker container

54 Answered Questions

[SOLVED] How to remove old Docker containers

  • 2013-06-21 13:41:42
  • qkrijger
  • 663660 View
  • 1091 Score
  • 54 Answer
  • Tags:   docker

3 Answered Questions

[SOLVED] Share Nginx server configuration

  • 2014-06-11 04:04:32
  • Anam
  • 1352 View
  • 3 Score
  • 3 Answer
  • Tags:   php nginx

19 Answered Questions

[SOLVED] How is Docker different from a virtual machine?

2 Answered Questions

php-fpm & nginx in separated docker container, can not execute .php file

  • 2016-10-11 10:55:20
  • Hacksign
  • 996 View
  • 0 Score
  • 2 Answer
  • Tags:   php nginx docker

Sponsored Content