Do we have any nginx gurus here?
I have a dockerized network with 6 containers running in it:
What I'm trying to achieve is to configure nginx, so I can serve the react and laravel app the following way:
http://localhost:8000/react
-> will return the react apphttp://localhost:8000/laravel
-> will return the laravel appdocker-compose.yaml
:
services:
react:
container_name: react
build:
context: ./react
dockerfile: ./Dockerfile
expose:
- 3000
networks:
- dev_network
volumes:
- ./react/src:/app/src
platform: linux/amd64
laravel:
container_name: laravel
build:
context: .
dockerfile: ./docker/php/Dockerfile
expose:
- 9000
volumes:
- .:/usr/src/app
- ./:/usr/src/app
depends_on:
- dev_db
networks:
- dev_network
platform: linux/amd64
dev_nginx:
container_name: dev_nginx
build:
context: .
dockerfile: ./docker/nginx/Dockerfile
volumes:
- ./public:/usr/src/app/public
ports:
- 8000:80
depends_on:
- laravel
- react
environment:
NGINX_FPM_HOST: laravel
NGINX_NODE_HOST: react
NGINX_ROOT: /usr/src/app
networks:
- dev_network
platform: linux/amd64
dev_db:
container_name: dev_db
image: mysql:8.0.20
platform: linux/amd64
restart: always
volumes:
- ./storage/db-data:/var/lib/mysql
ports:
- 3307:3306
environment:
MYSQL_DATABASE: "${DB_DATABASE}"
MYSQL_ROOT_PASSWORD: root
MYSQL_USER: "${DB_USERNAME}"
MYSQL_PASSWORD: "${DB_PASSWORD}"
networks:
- dev_network
phpmyadmin:
image: phpmyadmin/phpmyadmin
depends_on:
- dev_db
environment:
- PMA_HOST=dev_db
- PMA_PORT=3306
networks:
- dev_network
ports:
- 8085:80
platform: linux/amd64
dev_redis:
container_name: dev_redis
image: redis:latest
ports:
- 6379:6379
networks:
- dev_network
platform: linux/amd64
networks:
dev_network:
driver: bridge
Nginx Dockerfile
:
FROM nginx:latest
ENV NGINX_REACT_ROOT /usr/src/app/public
ENV NGINX_LARAVEL_ROOT /usr/src/app/
ENV NGINX_FPM_HOST localhost
ENV NGINX_FPM_PORT 9000
ENV NGINX_NODE_PORT 3000
RUN rm -f /etc/nginx/conf.d/default.conf
RUN rm -f /etc/nginx/nginx.conf
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./docker/nginx/fpm-template.conf /etc/nginx/fpm.tmpl
COPY ./docker/nginx/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 80
ENTRYPOINT ["entrypoint.sh"]
entrypoint.sh
:
#!/bin/bash
envsubst '$NGINX_REACT_ROOT $NGINX_LARAVEL_ROOT $NGINX_FPM_HOST $NGINX_FPM_PORT $NGINX_NODE_HOST $NGINX_NODE_PORT' < /etc/nginx/fpm.tmpl > /etc/nginx/conf.d/default.conf
exec nginx -g "daemon off;"
fpm-template.conf
:
server {
listen 80;
# this path MUST be exactly the same as your path in FPM even if it doesn't
# exist here. Nginx will send the full path of the file to render to fpm.
# root ${NGINX_ROOT};
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/x-javascript text/xml
application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml image/x-icon;
location /react {
root /usr/src/app/public;
proxy_pass http://${NGINX_NODE_HOST}:${NGINX_NODE_PORT};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /laravel {
root /usr/src/app;
index index.php;
try_files $uri $uri/ /index.php?$query_string;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass ${NGINX_FPM_HOST}:${NGINX_FPM_PORT};
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
client_max_body_size 2m;
}
In my laravel application the index.php
is placed in the root directory, so I had to indicate nginx to not look in the public directory.
Now when I access localhost:8000/react
, it works as expected. But when I access localhost:8000/laravel
it returns 404 and the nginx container gives me this log error:
[error] 9#9: *1 open() "/etc/nginx/html/index.php" failed (2: No such file or directory)
Any idea why it keeps looking in /etc/nginx/html
and not the root directory I configured (usr/src/app
)?
My devops skills have its limitations, so possible that I have a misunderstanding of how the nginx container works here.
Thanks.
If you had read the try_files
directive documentation more carefully, you would have found that the last argument in try_files
is treated completely differently from the others:
If none of the files were found, an internal redirect to the
uri
specified in the last parameter is made.
...
The last parameter can also point to a named location ... Starting from version 0.7.51, the last parameter can also be acode
.
This means that if the request does not correspond to a physical file but instead, for example, to a laravel route, the URI to be processed after a failed physical file check will be /index.php?$query_string
. However, according to your nginx configuration, it should be /laravel/index.php?$query_string
. Furthermore, you don't even have a location to handle requests that don't start with /react
or /laravel
(which is considered bad practice, BTW). Additionally, since you haven't defined a web root at the server
context level, the default root directory for such requests will be {prefix}/html
, where {prefix}
is a precompiled value (which can be viewed using the nginx -V
command), and in your case it is most likely equal to /etc/nginx
.
That is, the minimal change you need to make to your nginx configuration is to update the try_files
directive as follows:
try_files $uri $uri/ /laravel/index.php?$query_string;
With this configuration, nginx will look for static files in the /usr/src/app/laravel
directory. If this is not the desired behavior, you should use an alias
directive instead of the root
one (be sure to carefully read the documentation to understand the difference).
However, serving a PHP web application under a URI prefix with the alias
directive can be a bit tricky due to longstanding side effects when using try_files
in combination with alias
. Refer to this answer for potential workaround methods. Note that the only reliable method to correctly set the SCRIPT_FILENAME
FastCGI parameter is to use the $request_filename
variable. The commonly used combination $document_root$fastcgi_script_name
will not produce the correct result when an alias
directive is used.