Setup an nginx proxy for docker-compose

Roger Stringer
May 20, 2021
14 min read

I have a few servers running nginx-proxy using docker-compose, you can bring new sites online quickly by simply adding the network, virtual host info and exposing port 80.

So I thought I'd start with setting that up.

1. Set up your server

I'm going to assume you've got docker and docker-compose installed and skip that initial step.

First, we need to create a network

bash
1docker network create nginx-proxy
2

To get started, create a folder called sites and in that folder, create one called nginx-proxy.

Next, we'll create docker-compose.yaml:

// docker-compose.yaml:
1version: '3'
2
3services:
4  nginx-proxy:
5    image: nginx:alpine
6    container_name: nginx-proxy
7    ports:
8      - "80:80"
9      - "443:443"
10    volumes:
11      - ./volumes/conf:/etc/nginx/conf.d
12      - ./volumes/vhost:/etc/nginx/vhost.d
13      - ./volumes/html:/usr/share/nginx/html
14      - ./volumes/certs:/etc/nginx/certs:ro
15      - ./nginx-conf.conf:/etc/nginx/conf.d/nginx-conf.conf:ro
16      - /var/run/docker.sock:/var/run/docker.sock:ro
17
18  docker-gen:
19    image: nginxproxy/docker-gen
20    container_name: nginx-proxy-gen
21    command: -notify-sighup nginx-proxy -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
22    volumes_from:
23      - nginx-proxy
24    volumes:
25      - ./volumes/conf:/etc/nginx/conf.d
26      - ./volumes/vhost:/etc/nginx/vhost.d
27      - ./volumes/html:/usr/share/nginx/html
28      - ./volumes/certs:/etc/nginx/certs    
29      - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro
30      - /var/run/docker.sock:/tmp/docker.sock:ro
31    labels:
32      - "com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen"
33
34  acme-companion:
35    image: nginxproxy/acme-companion
36    container_name: nginx-proxy-acme
37    volumes_from:
38      - nginx-proxy
39    volumes:
40      - ./volumes/conf:/etc/nginx/conf.d
41      - ./volumes/vhost:/etc/nginx/vhost.d
42      - ./volumes/html:/usr/share/nginx/html    
43      - ./volumes/certs:/etc/nginx/certs:rw
44      - ./volumes/acme:/etc/acme.sh
45      - /var/run/docker.sock:/var/run/docker.sock:ro
46
47networks:
48  default:
49    external:
50      name: nginx-proxy
51

Now, make a file called nginx-conf.conf:

// nginx-conf.conf:
1client_max_body_size 512m;
2

Along with a final file called nginx.tmpl:

// nginx.tmpl:
1{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
2
3{{ define "upstream" }}
4	{{ if .Address }}
5		{{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}}
6		{{ if and .Container.Node.ID .Address.HostPort }}
7			# {{ .Container.Node.Name }}/{{ .Container.Name }}
8			server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }};
9		{{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}}
10		{{ else if .Network }}
11			# {{ .Container.Name }}
12			server {{ .Network.IP }}:{{ .Address.Port }};
13		{{ end }}
14	{{ else if .Network }}
15		# {{ .Container.Name }}
16		{{ if .Network.IP }}
17			server {{ .Network.IP }} down;
18		{{ else }}
19			server 127.0.0.1 down;
20		{{ end }}
21	{{ end }}
22
23{{ end }}
24
25# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
26# scheme used to connect to this server
27map $http_x_forwarded_proto $proxy_x_forwarded_proto {
28  default $http_x_forwarded_proto;
29  ''      $scheme;
30}
31
32# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
33# server port the client connected to
34map $http_x_forwarded_port $proxy_x_forwarded_port {
35  default $http_x_forwarded_port;
36  ''      $server_port;
37}
38
39# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
40# Connection header that may have been passed to this server
41map $http_upgrade $proxy_connection {
42  default upgrade;
43  '' close;
44}
45
46# Apply fix for very long server names
47server_names_hash_bucket_size 128;
48
49# Default dhparam
50{{ if (exists "/etc/nginx/dhparam/dhparam.pem") }}
51ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
52{{ end }}
53
54# Set appropriate X-Forwarded-Ssl header
55map $scheme $proxy_x_forwarded_ssl {
56  default off;
57  https on;
58}
59
60gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
61
62log_format vhost '$host $remote_addr - $remote_user [$time_local] '
63                 '"$request" $status $body_bytes_sent '
64                 '"$http_referer" "$http_user_agent"';
65
66access_log off;
67
68{{ if $.Env.RESOLVERS }}
69resolver {{ $.Env.RESOLVERS }};
70{{ end }}
71
72{{ if (exists "/etc/nginx/proxy.conf") }}
73include /etc/nginx/proxy.conf;
74{{ else }}
75# HTTP 1.1 support
76proxy_http_version 1.1;
77proxy_buffering off;
78proxy_set_header Host $http_host;
79proxy_set_header Upgrade $http_upgrade;
80proxy_set_header Connection $proxy_connection;
81proxy_set_header X-Real-IP $remote_addr;
82proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
83proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
84proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
85proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
86
87# Mitigate httpoxy attack (see README for details)
88proxy_set_header Proxy "";
89{{ end }}
90
91{{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }}
92server {
93	server_name _; # This is just an invalid value which will never trigger on a real hostname.
94	listen 80;
95	{{ if $enable_ipv6 }}
96	listen [::]:80;
97	{{ end }}
98	access_log /var/log/nginx/access.log vhost;
99	return 503;
100}
101
102{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
103server {
104	server_name _; # This is just an invalid value which will never trigger on a real hostname.
105	listen 443 ssl http2;
106	{{ if $enable_ipv6 }}
107	listen [::]:443 ssl http2;
108	{{ end }}
109	access_log /var/log/nginx/access.log vhost;
110	return 503;
111
112	ssl_session_tickets off;
113	ssl_certificate /etc/nginx/certs/default.crt;
114	ssl_certificate_key /etc/nginx/certs/default.key;
115}
116{{ end }}
117
118{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }}
119
120{{ $host := trim $host }}
121{{ $is_regexp := hasPrefix "~" $host }}
122{{ $upstream_name := when $is_regexp (sha1 $host) $host }}
123
124# {{ $host }}
125upstream {{ $upstream_name }} {
126
127{{ range $container := $containers }}
128	{{ $addrLen := len $container.Addresses }}
129
130	{{ range $knownNetwork := $CurrentContainer.Networks }}
131		{{ range $containerNetwork := $container.Networks }}
132			{{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
133				## Can be connected with "{{ $containerNetwork.Name }}" network
134
135				{{/* If only 1 port exposed, use that */}}
136				{{ if eq $addrLen 1 }}
137					{{ $address := index $container.Addresses 0 }}
138					{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
139				{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
140				{{ else }}
141					{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
142					{{ $address := where $container.Addresses "Port" $port | first }}
143					{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
144				{{ end }}
145			{{ else }}
146				# Cannot connect to network of this container
147				server 127.0.0.1 down;
148			{{ end }}
149		{{ end }}
150	{{ end }}
151{{ end }}
152}
153
154{{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
155{{ $default_server := index (dict $host "" $default_host "default_server") $host }}
156
157{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
158{{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
159
160{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
161{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
162
163{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}}
164{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }}
165
166{{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to "Mozilla-Intermediate" */}}
167{{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "Mozilla-Intermediate" }}
168
169{{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}}
170{{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) "max-age=31536000" }}
171
172{{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
173{{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
174
175
176{{/* Get the first cert name defined by containers w/ the same vhost */}}
177{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}
178
179{{/* Get the best matching cert  by name for the vhost. */}}
180{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}}
181
182{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}}
183{{ $vhostCert := trimSuffix ".crt" $vhostCert }}
184{{ $vhostCert := trimSuffix ".key" $vhostCert }}
185
186{{/* Use the cert specified on the container or fallback to the best vhost match */}}
187{{ $cert := (coalesce $certName $vhostCert) }}
188
189{{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}
190
191{{ if $is_https }}
192
193{{ if eq $https_method "redirect" }}
194server {
195	server_name {{ $host }};
196	listen 80 {{ $default_server }};
197	{{ if $enable_ipv6 }}
198	listen [::]:80 {{ $default_server }};
199	{{ end }}
200	access_log /var/log/nginx/access.log vhost;
201	return 301 https://$host$request_uri;
202}
203{{ end }}
204
205server {
206	server_name {{ $host }};
207	listen 443 ssl http2 {{ $default_server }};
208	{{ if $enable_ipv6 }}
209	listen [::]:443 ssl http2 {{ $default_server }};
210	{{ end }}
211	access_log /var/log/nginx/access.log vhost;
212
213	{{ if eq $network_tag "internal" }}
214	# Only allow traffic from internal clients
215	include /etc/nginx/network_internal.conf;
216	{{ end }}
217
218	{{ if eq $ssl_policy "Mozilla-Modern" }}
219	ssl_protocols TLSv1.2 TLSv1.3;
220	ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
221	{{ else if eq $ssl_policy "Mozilla-Intermediate" }}
222	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
223	ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:!DSS';
224	{{ else if eq $ssl_policy "Mozilla-Old" }}
225	ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
226	ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP';
227	{{ else if eq $ssl_policy "AWS-TLS-1-2-2017-01" }}
228	ssl_protocols TLSv1.2 TLSv1.3;
229	ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256';
230	{{ else if eq $ssl_policy "AWS-TLS-1-1-2017-01" }}
231	ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
232	ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
233	{{ else if eq $ssl_policy "AWS-2016-08" }}
234	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
235	ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
236	{{ else if eq $ssl_policy "AWS-2015-05" }}
237	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
238	ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA';
239	{{ else if eq $ssl_policy "AWS-2015-03" }}
240	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
241	ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA';
242	{{ else if eq $ssl_policy "AWS-2015-02" }}
243	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
244	ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA';
245	{{ end }}
246
247	ssl_prefer_server_ciphers on;
248	ssl_session_timeout 5m;
249	ssl_session_cache shared:SSL:50m;
250	ssl_session_tickets off;
251
252	ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }};
253	ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }};
254
255	{{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }}
256	ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
257	{{ end }}
258
259	{{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
260	ssl_stapling on;
261	ssl_stapling_verify on;
262	ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
263	{{ end }}
264
265	{{ if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }}
266	add_header Strict-Transport-Security "{{ trim $hsts }}" always;
267	{{ end }}
268
269	{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
270	include {{ printf "/etc/nginx/vhost.d/%s" $host }};
271	{{ else if (exists "/etc/nginx/vhost.d/default") }}
272	include /etc/nginx/vhost.d/default;
273	{{ end }}
274
275	location / {
276		{{ if eq $proto "uwsgi" }}
277		include uwsgi_params;
278		uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }};
279		{{ else if eq $proto "fastcgi" }}
280		root   {{ trim $vhost_root }};
281		include fastcgi.conf;
282		fastcgi_pass {{ trim $upstream_name }};
283		{{ else }}
284		proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
285		{{ end }}
286
287		{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
288		auth_basic	"Restricted {{ $host }}";
289		auth_basic_user_file	{{ (printf "/etc/nginx/htpasswd/%s" $host) }};
290		{{ end }}
291		{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
292		include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
293		{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
294		include /etc/nginx/vhost.d/default_location;
295		{{ end }}
296	}
297}
298
299{{ end }}
300
301{{ if or (not $is_https) (eq $https_method "noredirect") }}
302
303server {
304	server_name {{ $host }};
305	listen 80 {{ $default_server }};
306	{{ if $enable_ipv6 }}
307	listen [::]:80 {{ $default_server }};
308	{{ end }}
309	access_log /var/log/nginx/access.log vhost;
310
311	{{ if eq $network_tag "internal" }}
312	# Only allow traffic from internal clients
313	include /etc/nginx/network_internal.conf;
314	{{ end }}
315
316	{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
317	include {{ printf "/etc/nginx/vhost.d/%s" $host }};
318	{{ else if (exists "/etc/nginx/vhost.d/default") }}
319	include /etc/nginx/vhost.d/default;
320	{{ end }}
321
322	location / {
323		{{ if eq $proto "uwsgi" }}
324		include uwsgi_params;
325		uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }};
326		{{ else if eq $proto "fastcgi" }}
327		root   {{ trim $vhost_root }};
328		include fastcgi.conf;
329		fastcgi_pass {{ trim $upstream_name }};
330		{{ else }}
331		proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
332		{{ end }}
333		{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
334		auth_basic	"Restricted {{ $host }}";
335		auth_basic_user_file	{{ (printf "/etc/nginx/htpasswd/%s" $host) }};
336		{{ end }}
337		{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
338		include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
339		{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
340		include /etc/nginx/vhost.d/default_location;
341		{{ end }}
342	}
343}
344
345{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
346server {
347	server_name {{ $host }};
348	listen 443 ssl http2 {{ $default_server }};
349	{{ if $enable_ipv6 }}
350	listen [::]:443 ssl http2 {{ $default_server }};
351	{{ end }}
352	access_log /var/log/nginx/access.log vhost;
353	return 500;
354
355	ssl_certificate /etc/nginx/certs/default.crt;
356	ssl_certificate_key /etc/nginx/certs/default.key;
357}
358{{ end }}
359
360{{ end }}
361{{ end }}
362

Once these have been created in that folder, you can then start the containers:

bash
1docker-compose up -d --build
2

This will start up nginx on the server.

2. Configure your apps

Now that we have nginx-proxy running, we can set up any app and just point it to the nginx-proxy network along with the virtual host info.

Setting these env variables:

bash
1VIRTUAL_HOST: www.domain.com
2LETSENCRYPT_HOST: www.domain.com
3LETSENCRYPT_EMAIL: [email protected]
4

will configure the virtual host and lets encrypt to point to the site in question.

I'll demonstrate with wordpress:

Let's make a folder in your sites folder called wp.

First, create .dockerignore:

// .dockerignore:
1# comment
2*/temp*
3*/*/temp*
4temp?
5volumes/wordpress/*
6

Now, let's make our docker-compose.yaml file:

// docker-compose.yaml:
1version: '3'
2
3services:
4    mysql:
5        image: mariadb
6        expose:
7            - 3306
8        volumes:
9            - ./volumes/db-data:/var/lib/mysql
10        environment:
11            - MYSQL_ROOT_PASSWORD=whateverpassyouwant
12        restart: always
13    wordpress:
14        image: wordpress:5.7-php7.4-apache
15        expose:
16          - 80
17        working_dir: /var/www/html
18        volumes:
19          - ./volumes/wordpress:/var/www/html/
20        environment:
21            - WORDPRESS_DB_NAME=wpdb
22            - WORDPRESS_TABLE_PREFIX=wp_
23            - WORDPRESS_DB_HOST=mysql
24            - WORDPRESS_DB_PASSWORD=whateverpassyouwant
25            - VIRTUAL_HOST=wp.yourdomain.com
26            - LETSENCRYPT_HOST=SAME_AS_VIRTUAL_HOST
27            - LETSENCRYPT_EMAIL=[email protected]
28        links:
29            - mysql
30        restart: always
31
32networks:
33  default:
34    external:
35      name: nginx-proxy
36

Ok, hit save, and run:

bash
1docker-compose up -d --build
2

Once it finishes, you'll have wordpress running on the domain you specified in your VIRTUAL_HOST.

One final piece, if the container you want set up doesn’t use PORT 80, you can also set nginx to proxy to the port the container does use:

1version: '3'
2
3services:
4
5  whoami:
6    image: jwilder/whoami
7    expose:
8      - "8000"
9    environment:
10      - VIRTUAL_HOST=whoami.yourdomain.com
11      - LETSENCRYPT_HOST=SAME_AS_VIRTUAL_HOST
12      - [email protected]
13      - VIRTUAL_PORT=8000
14
15networks:
16  default:
17    external:
18      name: nginx-proxy
19

Setting VIRTUAL_PORT will tell nginx what port to proxy the domain to.

//

This post was originally published on my Coded Geekery site

Do you like my content?

Sponsor Me On Github