Coder Perfect

How do I link the php-fpm and Nginx Docker containers correctly?

Problem

I’m attempting to connect two distinct containers:

The issue is that php scripts do not function. It’s possible that your php-fpm settings is incorrect. The source code is available on my repository. Here’s the docker-compose.yml file:

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

I used the following Dockerfile to create 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/

Finally, here is my Nginx virtual host configuration:

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 someone perhaps assist me in properly configuring these containers to run PHP scripts?

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

docker-compose up

from the root directory of the project

Asked by Victor Bocharsky

Solution #1

I realize this is an old post, but I was having the same issue and couldn’t figure out why your code didn’t work. I’ve figured out why after a slew of experiments.

It appears that fpm receives the complete path from nginx and attempts to locate the files in the fpm container, implying that it must be same to server. Even if root does not exist in the nginx container, it must be specified in the nginx config.

To demonstrate:

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/

Answered by Rafael Quintela

Solution #2

Don’t hardcode container IPs in nginx config; docker link adds the linked machine’s hostname to the container’s hosts file, so you should be able to ping it by name.

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.

When a docker container is created from an image (or even when an existing container is stopped/started), the docker host assigns new IP addresses to the containers. These IP addresses do not belong to the same subnet as your physical machines.

https://docs.docker.com/docs/docs/docs/docs (this is what compose uses in the background)

but the docker-compose documentation on links & expose explain it better.

Links automatically set a bunch of system variables if you set up your project to acquire the ports + other credentials through environment variables:

Answered by Vincent De Smet

Solution #3

The issue, as previously stated, was that the files were not visible to the fpm container. The preferred pattern for sharing data among containers is to use data-only containers (as explained in this article).

To summarize, construct a container that only houses your data, share it with a volume, then link this volume to volumes from in your apps.

The docker-compose.yml file might look like this when using compose (1.6.2 on my machine):

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

Data publishes a volume that is associated with the nginx and fpm services. Then there’s the data service Dockerfile, which contains your source code:

FROM busybox

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

The nginx Dockerfile just replaces the default configuration:

FROM nginx

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

For the purpose of completeness, here is the configuration file needed to make the example 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;
    }
}

It just instructs nginx to utilize the shared disk as document root and configures nginx to communicate with the fpm container (i.e., the correct HOST:PORT, which is fpm:9000 thanks to compose’s hostnames, and the SCRIPT FILENAME).

Answered by iKanor

Solution #4

The version of Docker Compose has been upgraded. They currently have a file format known as version 2.

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

According to their documentation, your file should look like this:

version: '2'

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

These containers would be able to communicate with each other because they are immediately added to the default myapp default network. In the Nginx configuration, you’d have:

fastcgi_pass fpm:9000;

Also, as @treeface stated in the comments, make sure PHP-FPM is listening on port 9000. You can do this by modifying /etc/php5/fpm/pool.d/www.conf and adding listen = 9000.

I’ve kept the information below for anyone who are using an older version of Docker/Docker compose and need it.

When I was seeking for an answer to this query on Google, I kept coming across this question, but it wasn’t precisely what I was looking for because the Q/A focused 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’s link feature has recently been deprecated in favor of its networks feature.

As a result, you can link containers using the Docker Networks feature by following these steps. Read the documentation mentioned above for a complete description of the options.

Create your network first.

docker network create --driver bridge mynetwork

After that, start your PHP-FPM container, making sure to open port 9000 and assign it to your new network (mynetwork).

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

The —name php-fpm at the end of the command, which is the name, is crucial; we’ll need it later.

After that, restart your Nginx container and attach it to the network you just built.

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

You can also add —volumes-from commands, etc. to the PHP and Nginx containers as needed.

Now it’s time to set up Nginx. Which should appear something like 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;
    }
}

In the location block, look for fastcgi pass php-fpm:9000;. That means you should contact the php-fpm container on port 9000. When you add containers to a Docker bridge network, their hosts files are automatically updated to match their container name to their IP address. Nginx will know to contact the PHP-FPM container you called php-fpm previously and allocated to your mynetwork Docker network when it sees that.

It’s up to you whether you add that Nginx config during the Docker container construction process or subsequently.

Answered by DavidT

Solution #5

I believe we also need to provide the volume to the fpm container, don’t we? Consequently,

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

If I don’t do this, I get the following error when I try to fire a request: fpm cannot find the requested file:

Answered by leberknecht

Post is based on https://stackoverflow.com/questions/29905953/how-to-correctly-link-php-fpm-and-nginx-docker-containers