13

I am attempting to get nginx-proxy to work with the php-fpm variant of the official php image via fastcgi. Unfortunately, I seem to be unable to do so. I'm sure the problem is just something simple that I don't know about.

I have followed the instructions for nginx-proxy to the best of my ability and have boiled it down to a very simple way to re-create the issue. Here's my docker-compose.yml file:

version: "3"

services:
  proxy:
    image: jwilder/nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
    environment:
      - DEFAULT_HOST=test.local

  fpm:
    image: php:fpm
    environment:
      - VIRTUAL_HOST=test.local
      - VIRTUAL_PROTO=fastcgi

I then drop in a simple index.php file by running:

docker container exec -it web_fpm_1 /bin/bash -c 'echo "<?php phpinfo(); ?>" > /var/www/html/index.php'

(It puts web_ in front because this project is in a directory named web/.)

I also modify my hosts file to point test.local to 127.0.0.1, so I can test it. However, every attempt to browse to test.local results in a blank white page.

The logs for the web_proxy_1 container don't indicate anything out of the ordinary, as far as I know:

❯ docker container logs web_proxy_1
WARNING: /etc/nginx/dhparam/dhparam.pem was not found. A pre-generated dhparam.pem will be used for now while a new one
is being generated in the background.  Once the new dhparam.pem is in place, nginx will be reloaded.
forego     | starting dockergen.1 on port 5000
forego     | starting nginx.1 on port 5100
dockergen.1 | 2020/07/20 19:24:54 Generated '/etc/nginx/conf.d/default.conf' from 2 containers
dockergen.1 | 2020/07/20 19:24:54 Watching docker events
dockergen.1 | 2020/07/20 19:24:54 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
nginx.1    | test.local 172.18.0.1 - - [20/Jul/2020:19:25:12 +0000] "GET / HTTP/1.1" 200 5 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
nginx.1    | test.local 172.18.0.1 - - [20/Jul/2020:19:25:13 +0000] "GET /favicon.ico HTTP/1.1" 200 5 "http://test.local/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"

The logs for the web_fpm_1 container show that nothing gets sent except a 200 response:

❯ docker container logs web_fpm_1
[20-Jul-2020 19:24:54] NOTICE: fpm is running, pid 1
[20-Jul-2020 19:24:54] NOTICE: ready to handle connections
172.18.0.3 -  20/Jul/2020:19:25:12 +0000 "- " 200
172.18.0.3 -  20/Jul/2020:19:25:13 +0000 "- " 200

What am I doing wrong?

Incidentally, I have asked this question on the nginx-proxy repo, the nginx-proxy Google Group, and the php repo. I either get no response or they pass the buck.

Sturm
  • 689
  • 2
  • 23
  • 52
  • try `curl http://test.local/var/www/html/index.php` let me know if this works – WSMathias9 Jul 20 '20 at 17:41
  • Can you show the nginx logs please. – Daniel W. Jul 20 '20 at 18:16
  • Hey, thanks for the replies. @WSMathias9: I'm afraid the result is nothing. It returns nothing, just like going to the page itself returns a blank white page. @ Daniel: Done. I've edited the original post to include the proxy's logs. – Sturm Jul 20 '20 at 19:32
  • Let both services share `/var/www/html/index.php` and `/var/run/docker.sock:/tmp/docker.sock` through `volumes` in `docker-compose.yml`. See what happens. Also you can put them in same network as well. – BentCoder Jul 20 '20 at 19:43
  • 1
    @BentCoder That's not what it supposed to be configured. Don't just try 777 open and give everything to everything and "see what happens". This is how you open your server for hackers and make system unstable. Very bad advice. Data should only be on 1 container. – Daniel W. Jul 20 '20 at 19:51
  • @Sturm there is either a mistake in `/etc/nginx/conf.d/default.conf` which is generated by the nginx proxy automatically, or there is a mistake in the `/var/www/html/index.php`, please add those too. – Daniel W. Jul 20 '20 at 19:53
  • @DanielW. Not only you are quick to come to a conclusion but also you disregarded the `See what happens` bit which was added on purpose. That was just the beginning of monkey patching for further debugging. – BentCoder Jul 20 '20 at 20:00
  • We can see from the logs both services are ready without any startup issues. So there is a configuration problem in the communication between them. What are you trying to "monkey patch" by giving the fpm container access to the docker socket? Only the reverse proxy does need to read it. Data in both containers, doesn't make sense. I'm not only quick but when I can avoid monkey debugging and do a more analytical approach, I prefer that. – Daniel W. Jul 20 '20 at 20:15
  • Daniel W. Please just focus on helping rather than having a go at people just because you believe what you think is right. I am done here, that's all from me. @Sturm sorry for unnecessary noise. – BentCoder Jul 20 '20 at 20:23
  • @BentCoder it's not a question of believe and your advice was not helpful so don't tell me what to focus on. I told you that by granting random containers access to host stuff they don't need nothing from is how you open up for hackers. It's not how you do it. You can read about it in [this link from traefik](https://docs.traefik.io/providers/docker/#docker-api-access) which is doing the same as jwilder's nginx proxy. – Daniel W. Jul 20 '20 at 21:23

2 Answers2

2

The default generated config of nginx-proxy is not fully working.

I think something is messed up with VIRTUAL_ROOT environment variable, because the root of the problem is PHP getting a wrong path via SCRIPT_FILENAME (that's why you see no PHP output) and there is no try_files with =404 symbol (that's why you get 200 with everything).

I have a prepared working setup using docker-compose in GitHub to demonstrate that it would work with an existing SCRIPT_FILENAME in the nginx config.

I have changed test.local to test.localhost.

I think to get it working as it should, you would have to use an nginx template for nginx-proxy, so the generated default.conf does work with php fpm and have the missing fastcgi param included.

Another, yet different approach would be to pack PHP and a manually configured webserver (nginx) in a project and having the automated reverse nginx proxy in a standalone project. This would cost you an additional process running but gives you more control and easier deployment.

Alternatively, you might want to have a look into traefik which does essentially the same as nginx-proxy.

Daniel W.
  • 31,164
  • 13
  • 93
  • 151
  • Okay, @Daniel, this is getting me closer than ever to getting nginx-proxy to work over fastcgi. I am now, at least, getting a "Welcome to nginx" page. Your github repo worked a treat and does highlight what I need to do, for the most part. I am still wondering how to get this to work with more than one server, however. Looks like I need to follow jwilder's [virtual host instructions](https://github.com/nginx-proxy/nginx-proxy#per-virtual_host-configuration). Do I just copy the entire `server {}` block to the vhost file? – Sturm Jul 21 '20 at 14:38
  • Also, I checked out traefik. It looks like it would be worth looking into further, but since I've come so far already with nginx-proxy, I'd like to complete this mission first. – Sturm Jul 21 '20 at 14:40
  • The magic is happening when the nginx-proxy is listening to the docker socket `/var/run/docker.sock:/tmp/docker.sock:ro`. The proxy can see when you load up a new container and automatically regenerates its own nginx config. The nginx-proxy uses environment variables `VIRTUAL_...` while traefik uses so called [labels](https://docs.traefik.io/v1.4/configuration/backends/docker/#labels-overriding-default-behaviour). You should try to alter the nginx-proxy template and provide your own with the missing `SCRIPT_FILENAME` OR you alter the nginx `fastcgi_params` to include `SCRIPT_FILENAME`. – Daniel W. Jul 22 '20 at 11:37
  • @Sturm I am using traefik for local development on my notebook and I use nginx (manually configured) on production systems. The "configure automatically" stuff is only really helpful locally or while developing but it's not worse doing the configuration by hand. – Daniel W. Jul 22 '20 at 11:43
  • So I'm guessing that you don't think I should follow the virtual host instructions? "You should try to alter the nginx-proxy template and provide your own with the missing SCRIPT_FILENAME OR you alter the nginx fastcgi_params to include SCRIPT_FILENAME" - I'm afraid I do not know where/how to do this. Could you please amend your answer to include instructions on exactly how to update the proxy template? – Sturm Jul 22 '20 at 17:08
  • @Sturm Usually, you have the reverse proxy and all your projects in seperate containers/projects. Check the docs at [Seperate Containers](https://github.com/nginx-proxy/nginx-proxy#separate-containers). You run the nginx container and then you run the docker-gen container that regenerates the default.conf according to the [template](https://github.com/nginx-proxy/nginx-proxy/blob/master/nginx.tmpl). You just need to add `try_files $uri /$uri /index.php` to your server section. You would also overwrite the nginx' `fastcgi_params` because it's missing the value `SCRIPT_FILENAME`. – Daniel W. Jul 23 '20 at 12:42
  • Okay, I think I'm beginning to understand a little more. The Separate Containers section of the docs, however, refer to separating `nginx` from `dockergen`, which are combined already in `jwilder/nginx-proxy` for simplicity. I suppose I could build my own version of `nginx-proxy` and substitute the default template with a modified version. – Sturm Jul 23 '20 at 12:56
  • Okay, so after several hours of trying different things with the `nginx.tmpl` file, the only thing I could think of to do that actually _works_ is to essentially remove the main `location` block from `default.conf` and just have all of the `location` blocks needed in each of the vhost's files. Thus, your answer was, indeed, the closest to get me to where I am. I've even saved my progress in [an offshoot of jwilder's project](https://hub.docker.com/repository/docker/sturmb/nginx-proxy). – Sturm Jul 24 '20 at 20:38
  • Feel free to have a look at that project and make any recommendations. I'll gladly work with further you on this, @Daniel, if you'd be interested. This seems like a serious bug in jwilder's project. Maybe no one else uses `fastcgi` or something it's they haven't run into it. Now, however, my biggest problem is bringing these changes over to my main project with several container'd web sites. It's not working and I don't know why. – Sturm Jul 24 '20 at 20:40
  • @Sturm I can't get to the github of your project and I don't run containers without sourcecode. I know it's not satisfying your answer but using nginx manually is easier than using this automated nginx config generator universally. – Daniel W. Jul 26 '20 at 13:25
  • Just figured out how to do "automated builds" with Docker Hub. There should now be a link to the [Github repo of my version of nginx-proxy](https://github.com/SturmB/nginx-proxy). After going through that process, I'm beginning to understand what you mean about using Nginx manually being probably better than jwilder/nginx-proxy. – Sturm Jul 27 '20 at 18:13
  • @Sturm another approach would be to use an nginx manually configured with php **in addition** to the automated reverse proxy project. Because http to http communication is way easier than http to custom php fastcgi. I'm using (in development) traefik (auto) -> nginx (manual) -> php while nginx (manual) and php are inside one project and traefik (or jwilder's nginx proxy) would be in a standalone project. – Daniel W. Jul 27 '20 at 21:45
2

Daniel's answer is definitely on the right track. I use the php-fpm image with nginx as my main stack for php sites. Having said that, I don't use the nginx-proxy docker image. Instead, I use plain nginx on the host machine, and configure ports to point to backend php-fpm docker images.

I'm not using docker-compose either. Since it's just docker containers running single sites, I don't need it. Here's an example docker run command:

docker rm -f www.example.com || true
docker run -itd -p 9001:9000 -P \
        --name www.example.com \
        --volume /var/www/html/www.example.com:/var/www/html/www.example.com \
        --link mariadb:database.example.com \
        --restart="always" \
        --hostname="example.com" \
    --log-opt max-size=2m \
    --log-opt max-file=5 \
        mck7/php-fpm:7.4.x-wordpress

And here is an example nginx config:

server {
  server_name example.com www.example.com;

  location ~ /.well-known {
    allow all;
  }

  location ~ /\.ht {
    deny all;
  }

  root /var/www/html/www.example.com/src;

  index index.php;

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

  location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    if (!-f $document_root$fastcgi_script_name) {
      return 404;
    }

    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO       $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

    fastcgi_pass   127.0.0.1:9001;
    fastcgi_index  index.php;
  }
}

A few things about this setup are key. The port re-mapping for the docker container. In this example I map port 9001 to 9000. The other "gotcha" is that the root for the container must be an actual location on the host. I have no idea why this is the case, but for whatever reason the path docker thinks it's using has to actually be the path on the host as well.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Cory Collier
  • 883
  • 1
  • 10
  • 20