Here is a possible solution for using nginx as a router without upstream
definitions. upstream
can be undesirable when the server definitions include named hosts because nginx will fail to start if a named host could not be resolved (which could be the case with docker networks). The following configuration assumes that only HTTPS traffic will be routed and redirects all HTTP traffic to HTTPS.
This configuration is designed for nginx running in a docker instance and sharing a network with other docker services. dockerservice1
and dockerservice2
are expected to listen on port 443. The resolver 127.0.0.11 ipv6=off valid=1s;
line enables host name resolution by the docker's local DNS resolver (127.0.0.11
), so that dockerservice1
and dockerservice2
can be resolved to ips from the docker internal network. With this configuration nginx will always start, regardless of the up status of the referenced docker servces/hosts.
The configuration for logging can be omitted but is useful for diagnostic purposes.
# /etc/nginx/nginx.conf from the nginx docker instance
user nginx;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
}
http {
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
log_format basic
'$time_local router:RD $status $request_time client: $remote_addr '
'http://$host$request_uri -> https://$host$request_uri';
access_log /var/log/nginx/access.log basic;
error_log /var/log/nginx/error.log;
}
stream {
map $ssl_preread_server_name:$server_port $upstream {
hostnames;
mydomain.com:443 dockerservice1;
www.mydomain.com:443 dockerservice1;
test.mydomain.com:443 dockerservice2;
default dockerservice1;
}
server {
listen 443;
resolver 127.0.0.11 ipv6=off valid=1s;
proxy_pass $upstream:443;
ssl_preread on;
}
log_format basic
'$time_local router:RT $status $session_time client: $remote_addr '
'$ssl_preread_server_name:$server_port -> $upstream ($upstream_addr) '
'bytes from/to client $protocol $bytes_sent $bytes_received '
'bytes from/to upstream $upstream_bytes_sent/$upstream_bytes_received '
'upstream time: $upstream_connect_time';
access_log /var/log/nginx/access.log basic;
error_log /var/log/nginx/error.log;
}
NOTE: with Docker version 20.10.12, build e91ed57
, the DNS resolver at 127.0.0.11
seems to work unreliably if there are a docker service and a docker host name with the same name, on different docker projects/docker-compose.yml files (and presumably on the same network), e.g.
# project1/docker-compose.yml
version: "3"
services:
sharedName:
container_name: sharedName
hostname: sharedName
networks:
- default
- external
...
networks:
external:
name: someNetwork
# project2/docker-compose.yml
version: "3"
services:
sharedName:
container_name: OTHERContainerName
hostname: OTHERContainerName
networks:
- default
- external
...
networks:
external:
name: someNetwork
The expected behavior in the above case would be for the host name sharedName
to always resolve to the ip of the container created from project1/docker-compose.yml
. The observed behavior is that sharedName
is randomly resolved to the ip of either container sharedName
or OTHERContainerName
. This is also valid for a larger number of projects/containers.
A possible solution seems to be to enforce unique host names that have no duplicates among service names.