Short description:
Nginx running on docker, how to configure nginx so that it forwards calls to host.
Long description:
We have one web application which communicates to couple of backends (lets says rest1, rest2 and rest3). We are responsible for rest1.
Lets consider that I started rest1 manually on my pc and running on 2345 port. I want nginx (which is running in docker) to redirect all call torest1 to my own running instance(note, the instance is running on host, not any container and not in docker). And for rest2 and rest3 to some other docker node or may be some other server (who cares).
What I am looking for is:
docker-compose.yml configurations (if needed).
nginx configuration.
Thanks in advance.
Configure nginx like the following (make sure you replace IP of Docker Host) and save it as default.conf:
server {
listen 80;
server_name _;
location / {
proxy_pass http://<IP of Docker Host>;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Now bring up the container:
docker run -d --name nginx -p 80:80 -v /path/to/nginx/config/default.conf:/etc/nginx/conf.d/default.conf nginx
If you are using Docker Compose file version 3 you don't need any special config for docker-compose.yml file at all, just use the special DNS name host.docker.internal to reach a host service, as on the following nginx.conf example:
events {
worker_connections 1024;
}
http {
upstream host_service {
server host.docker.internal:2345;
}
server {
listen 80;
access_log /var/log/nginx/http_access.log combined;
error_log /var/log/nginx/http_error.log;
location / {
proxy_pass http://host_service;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $realip_remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
Solution 1
Use network_mode: host, this will bind your nginx instance to host's network interface.
This could result in conflicts when running multiple nginx containers: every exposed port is binded to host's interface.
Solution 2
I'm running more nginx instances for every service I would like expose to outside world.
To keep the nginx configurations simple and avoid binding every nginx to host use the container structure:
dockerhost - a dummy container with network_mode: host
proxy - nginx container used as a proxy to host service,
link dockerhost to proxy, this will add an /etc/hosts entry in proxy contianer - we can use 'dockerhost' as a hostname in nginx configuration.
docker-compose.yaml
version: '3'
services:
dockerhost:
image: alpine
entrypoint: /bin/sh -c "tail -f /dev/null"
network_mode: host
proxy:
image: nginx:alpine
links:
- dockerhost:dockerhost
ports:
- "18080:80"
volumes:
- /share/Container/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
default.conf
location / {
proxy_pass http://dockerhost:8080;
This method allows us to have have automated let's encrtypt certificates generated for every service running on my server. If interested I can post a gist about the solution.
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://host.docker.internal:3000;
}
}
Docker expose host address is host.docker.internal in Mac os
There a couple of things you have to keep in mind:
Docker compose (from version 3) by default uses the service name as hostname for inter container networking
Nginx need to know the upstream first
I strongly recommend mounting the default.conf directly into your docker-compose.yml.
Lastly you have to dockerize your backend to make use of docker internal networking.
An example repo where I use nginx and docker-compose in a full-stack project: https://gitlab.com/datails/api.
The following example have some prerequisites:
you have a folder structure like:
- backend/
- frontend/
- default.conf
- docker-compose.yml
Secondly the backend and front-end dit have a Dockerfile that exposes an application on port 3000.
Example default.conf:
upstream backend {
server backend:3000;
}
upstream frontend {
server frontend:3000;
}
server {
listen 80;
location /api {
proxy_pass http://backend;
}
location / {
proxy_pass http://frontend/;
}
}
Example docker-compose.yml:
version: '3.8'
services:
nginx:
image: nginx:1.19.4
depends_on:
- server
- frontend
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- '8080:80'
Then make sure you have your backend dockerized and called (in this case) backend as a service and a front-end (if needed) called frontend as a service in your docker-compose:
version: '3.8'
services:
nginx:
image: nginx:1.19.4
depends_on:
- server
- frontend
volumes:
- ./default.conf:/etc/nginx/conf.d/default.conf
ports:
- '8080:80'
frontend:
build: ./frontend
backend:
build: ./backend
This is a bare minimum example to get started. Hope this will help future developers.
Related
I have a FastAPI API that I want to serve using gunicorn, nginx and docker compose.
I manage to make the FastApi and Gunicorn work with docker compose, now I add nginx. But I cannot manage to make it work. When I do curl http://localhost:80 I get this messsage: If you see this page, the nginx web server is successfully installed and working. Further configuration is required.
So this is my docker compose file:
version: '3.8'
services:
web:
build:
dockerfile: Dockerfile.prod
context: .
command: gunicorn main:app --bind 0.0.0.0:8000 --worker-class uvicorn.workers.UvicornWorker
expose:
- 8000
env_file:
- ./.env.prod
nginx:
build:
dockerfile: Dockerfile.prod
context: ./nginx
ports:
- 1337:80
depends_on:
- web
On this one, if I set ports to 80:80 I get an error when the image is composed: Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use, which I don't know why.
If I put [some random number]:80 (e.g. 1337:80) then the docker build works, but I get the If you see this page, the nginx web server is successfully installed but... error message state before. I think 1337 is not where nginx is listening, and that's why.
This is my nginx conf file:
upstream platic_service {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://platic_service;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
I tried to change it to listen to 8080 but does not work.
What am I doing wrong?
We had many applications on single vultr cloud instance, but it has only one default healthcheck for a single https loadBalancer with SSL certificate.
so we used nginx to configure mutliple /backend URL with http specified and running using docker-compose to make applications running on a single network.
server {
listen 80;
listen [::]:80;
server_name *.example.com;
access_log /var/log/nginx/host.access.log main;
location / {
proxy_pass http://strapi-container:1337/;
}
location /chat {
proxy_pass http://rocketchat-container:3000;
}
location /auth {
proxy_pass http://keycloak-container:8080;
proxy_set_header Host $host;
}
}
}
The backend url is http://instance-ip/, http://instance-ip/chat, http://instance-ip/auth respectively
nginx:
image: nginx:1.20
container_name: nginx
ports:
- 80:80
restart: unless-stopped
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- strapi-cms
- rocketchat
- keycloak
networks:
- test-network
Everything works fine and we are able to access the applications through nginx default port 80 with the above backend URL's.
But our intentions is to somehow connect the nginx with HTTPS LoadBalancer in vultr, it should works as
For example: https://qa.example.com/, https://qa.example.com/chat, https://qa.example.com/auth
What you will want to do is setup a single forwarding rule on the Vultr Load Balancer.
Forwarding rule 443->443 for TLS on the instance
Forwarding rule 443->80 for TLS on the LB.
This will have the LB forward all incoming traffic on the defined LB port to your NGINX defined port. Then your nginx instance should route the the location to the appropriate proxy_pass you have defined.
As for the health check...Vultr Load Balancers only have a single health check as they were designed to work with single applications behind the LB. However, you could have a /health endpoint that when hit would check the status of all of your other applications and return 200 ok if they are all running.
We have some detailed docs available at https://www.vultr.com/docs/vultr-load-balancers
Full disclosure I am the Technical Lead on load balancers for Vultr.
Currently I want to setup one server that has a Docker WordPress and Nginx that serves as a proxy in front. I would like in future to be able have multiple WordPress, NodeJS, ROR, etc, sitting behind this Nginx proxy.
When ever I try to connect to my server on port 80 I get a 403 forbidden.
I am able to build a Docker WordPress image and can connect to it on port 8080 on a remote PC.
Here is the compose.yml for my Docker WordPress:
version: "3.1"
services:
my_wordpress:
image: wordpress
ports:
- 8080:80
environment:
WORDPRESS_DB_PASSWORD: password
WORDPRESS_DB_HOST: my_mysql_wordpress
my_mysql_wordpress:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: password
This is the part when I try to build a Nginx container I am getting a 403 forbidden.
Nginx DockerFile:
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
Nginx compose.yml:
version: "2"
services:
web:
restart: always
image: nginx
ports:
- "80:80"
volumes:
- /path/in/vm/www:/usr/share/nginx/html
external_links:
- mywordpress_wordpress_1:mywordpress
networks:
default:
external:
name: mywordpress_default
Nginx nginx.conf:
http {
#...
upstream wordpress {
server mywordpress:8080;
}
#...
server {
listen 80;
server_name 192.168.1.124 test.me;
location / {
proxy_pass http://wordpress/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}
}
}
Now for me it would seem that this most likely has to do with my nginx.conf as I am still able to connect to my WordPress site on port 8080. As well as I stated I am also able to connect to my Nginx proxy and I don't see any errors when it launches.
Is what I'm trying to doing even possible or do I need to have the Nginx application sitting on the OS and not inside a docker container?
You are putting Nginx and Wordpress in 2 different compose files. If you are running then on same machines then
external_links:
- mywordpress_wordpress_1:mywordpress
Above would not work if you are on different machines. Also make sure the external link you are using the correct name by checking docker ps.
Also check the logs of your nginx container to see if it is showing in any error. Because the error log will give a pointer as to why a 403 is being thrown, and it could be that the proxy_pass is not able to connect to your wordpress server because of the way you have configured it.
If you are running these compose files on different machines then instead of external_links use extra_hosts
extra_hosts:
- "mywordpress:<IP of the wordpress machine>"
If I am right, You want to run multiple wordpress docker images and use nginx to reverse proxy to the wordpress instances. In that Use-case, The nginx should sit on your OS and not inside a docker image. That way, the nginx will have the ability to proxy to ports on your OS which are tied to the wordpress containers.
lets say that now I have different app running on the same server on different path:
10.200.200.210/app1
10.200.200.210/app2
10.200.200.210/app3
I want to run each app on a different Docker container using nginx as a proxy.
I tried jwilder/nginx-proxy and works great if I use different domain names (app1.domain.com, app2.domain.com, etc), but I'm not able to use domains, I need to use the same IP.
also I can't use different ports like:
10.200.200.210:81/app1
10.200.200.210:82/app2
10.200.200.210:83/app3
all must work on port 80.
Is there a way to configure jwilder/nginx-proxy to do this?
Is there another Docker image like jwilder/nginx-proxy that make it.
or pls could you give me some hint to build an nginx docker container by myself?
In case if somebody is still looking for the answer.
jwilder/nginx-proxy allows you to use custom Nginx configuration either a proxy-wide or per-VIRTUAL_HOST basis.
Here's how can you do it with Per-VIRTUAL_HOST location configuration.
Inside your poject folder create another folder - "vhost.d".
Create file "whoami.local" with custom nginx configuration inside "vhost.d" folder. This file must have the same name as VIRTUAL_HOST!
./vhost.d/whoami.local
location /app1 {
proxy_pass http://app1:8000;
}
location /app2 {
proxy_pass http://app2:8000;
}
Create docker-compose.yml file.
./docker-compose.yml
version: '3'
services:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- "8080:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- /path/to/vhost.d:/etc/nginx/vhost.d:ro
gateway:
image: jwilder/whoami
environment:
- VIRTUAL_HOST=whoami.local
app1:
image: jwilder/whoami
app2:
image: jwilder/whoami
Run docker-compose up
Check configuration
In bash run:
$ curl -H "Host: whoami.local" localhost:8080
I'm 1ae273bce7a4
$ curl -H "Host: whoami.local" localhost:8080/app1
I'm 52b1a7b1992a
$ curl -H "Host: whoami.local" localhost:8080/app2
I'm 4adbd3f9e7a0
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6a659a4d4b0a jwilder/nginx-proxy "/app/docker-entrypo…" 54 seconds ago Up 53 seconds 0.0.0.0:8080->80/tcp nginxreverseproxy_nginx-proxy_1
4adbd3f9e7a0 jwilder/whoami "/app/http" 54 seconds ago Up 53 seconds 8000/tcp nginxreverseproxy_app2_1
52b1a7b1992a jwilder/whoami "/app/http" 54 seconds ago Up 53 seconds 8000/tcp nginxreverseproxy_app1_1
1ae273bce7a4 jwilder/whoami "/app/http" 54 seconds ago Up 53 seconds 8000/tcp nginxreverseproxy_gateway_1
You can also add "whoami.local" domain to /etc/hosts file and make calls to this domain directly.
/etc/hosts
...
127.0.0.1 whoami.local
...
Result:
$ curl whoami.local:8080
I'm 52ed6da1e86c
$ curl whoami.local:8080/app1
I'm 4116f51020da
$ curl whoami.local:8080/app2
I'm c4db24012582
Just use nginx image to create container,**do remember set net "host" **which will make your container share same address and port with host machine.mount nginx.conf file and config proxy table.for example:
docker command:
docker run --name http-proxy -v /host/nginx.conf:/etc/nginx/nginx.conf --net host -itd --restart always nginx
nginx.conf:
server {
listen 80;
location /app1 {
proxy_pass YOUR_APP1_URL;
}
location /app2 {
proxy_pass YOUR_APP2_URL;
}
}
Here is a full nginx.conf
It redirects all to root, and only /api to a different container.
Source and an example container using it
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
proxy_pass http://frontend:3000/;
}
location /api {
proxy_pass http://backend/api;
}
}
}
just put this inside /etc/nginx/nginx.conf
worker_processes 1;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location /api {
proxy_pass http://awesome-api;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
}
Default bridge network has gateway on 172.17.0.1. You can use this IP address in your nginx.conf
server {
listen 80;
server_name example.com;
location /app1 {
proxy_pass http://172.17.0.1:81;
}
location /app2 {
proxy_pass http://172.17.0.1:82;
}
}
They will be accessible using port 80 from outside
You can check your bridge gateway IP address by running command docker network inspect bridge
My situation is a little bit different. I'm working on a project where Django and a couple of other apps sit behind nginx (acting as a reverse proxy)
The accepted solution did not work for me, and I think it is because the various apps do not serve files (i.e. /app1-uri/bla/blo/bli/ and /app1-uri/bla/blo/bli are exactly equivalent). All static files are gathered for nginx to serve.
The 'problematic' behavior is explained in the docs here. In essence, nginx picks up the uris without trailing slash and try to resolve them as resources, if it can't, it then redirects to /bla/blo/bli/ instead of /app1-uri/bla/blo/bli/
This is what finally worked for me. In this example app1-uri is repairapp.
server {
listen 80;
listen 443 ssl http2;
...
# This is the line that fixes the issue.
rewrite ^/repairapp/([^static].*[^/])$ /repairapp/$1/ permanent;
# This is nginx serving my static files
location /repairapp/static/ {
alias /var/www/repairapp/static/;
}
# This is the uri that maps to my app (no file serving here)
location /repairapp/ {
proxy_pass http://repairappcontainer:8000/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
...
Note the line rewrite ^/repairapp/([^static].*[^/])$ /repairapp/$1/ permanent;
It rewrites and adds a trailing forward slash to any (of my app) uris that misses it; except for those that start with /repairapp/static. Those uris map to resources that nginx will then serve.
To debug, open a shell in the nginx container and run curl -IL http://[server-name]/repairapp/[string-of-uris-without-trailing-slash] to see exactly what happens.
I am trying to reverse-proxy an ejabberd connection manager with nginx in docker.
Following is my docker-compose file
version: '2'
services:
nginx:
image: nginx
ports:
- "80:80"
- "443:443"
depends_on:
- ejabberd
links:
- ejabberd
ejabberd:
image: ejabberd:16.04
depends_on:
- mysql
ports:
- "5280:5280"
links:
- mysql
mysql:
image: mysql:5.6
ports:
- "3306:3306"
The images nginx, ejabberd:16.04 and mysql:5.6 are available in my local docker.
Following is my server config file which is included in the nginx.conf file
upstream ejabberd-server {
server ejabberd:5280;
}
server {
# regular silverstripe things here
# location should match your JabberPage::BOSHUrl
# with a leading slash
listen 80;
#server_name oops.hereim.co
location /http-bind {
# Local ejabberd with http-bind
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ejabberd-server;
}
}
But when I try to access the url http://192.168.99.102/http-bind
I get the error 404 not found.
Am I missing something in the configuration above?
192.168.99.102 is the IP of my docker machine.
docker-compose exec nginx ping ejabberd
The above command returns a reply.
The upstream has to be setup to host ejabberd as nginx and ejabberd are not on the same ejabberd container.
If you see the docker-compose.yml, the ejabberd and mysql connection works fine and I am able to connect to mysql from ejabberd. But there seems to be some missing piece while connection nginx to ejabberd.
The issue is probably due to an nginx issue. nginx does not pick up .conf files under the /etc/nginx/conf.d directory.
I had to change the file /etc/nginx/sites-enabled/default and enter the location inside that file. This solved my problem for this question.
The next action item is to check why nginx does not read conf file from /etc/nginx/conf.d directory
Looks like your server part in upstream is a bit wrong.
Can you try server 127.0.0.1:5280; instead of server ejabberd:5280; ?