Nginx Reverse Proxy Set Up Guide – Docker

In light of the recent “hacking” stories, last week I set myself the goal of implementing Nginx. I read most related posts in this forum and elsewhere, but was unable to find a step by step guide for “enthusiasts” with limited it skills. Therefore I proceeded to do it the hard way: read, trial and error.

Regretfully the forum was of no use as some of the individuals with “apparent subject matter experience” chose to offer condescending advice with no real benefit or substance. I do, however, want to thank @Ludeeus as he patiently helped me diagnose the multiple bugs I was self-inflicting.

Anyway, on the subject this is how I got it working:
OS: Ubuntu 18.04LTS Running Docker. If you’re not running Docker, I would encourage you to use it; even if only for this.

I have attempted listing all the steps (that worked) and in the process may make some assumptions on what you have. I’m far from an expert, but feel free to ask if stuck.

Use this container: https://hub.docker.com/r/linuxserver/swag
In short its fantastic. It combines Nginx and Letsencrypt. Once run, it creates all the default files, directories, ssl certificates and dependencies that you may need. If you want to expose multiple components securely i.e. configurator, terminal, grafana, etc. It creates an SSL with Subject Alternative Name. What this means is that the one certificate will be good for all the sub-subdomains you want to use. A little explanation on that, since I was confused with it and no doubt there will be others.

Example: https://configurator.mydomain.duckdns.org
##########sub-subdomain.subdomain.domain.tld

Therefore you can create numerous sub-subdomains. Below find my process:

  1. You will need deactivate ssl in any enabled components starting with home assistant itself: for example

http:

api_password: !secret http_password
#ssl_certificate: /certs/ fullchain.pem
#ssl_key: /certs/privkey.pem
ip_ban_enabled: true #blocks unathorized ips from accessing HA
login_attempts_threshold: 5 #number of attempts before ip is banned
base_url: hass.mydomain.duckdns.org

  1. Please read the instructions in Docker. It’s 5 minutes and will help understand the process below
  2. Forward your router ports 80 to 80 and 443 to 443
  3. Establish the docker user - PGID= and PUID=.

The command is $ id dockeruser.
Output will be 4 digits, which you need to add in these variables respectively

  1. Create a host directory to support persistence. For example:
  • /home/user/docker/swag/config:/config
  1. Run the following command line or use the docker compose file

docker create
–cap-add=NET_ADMIN
–name=letsencrypt \
-v /home/user/docker/swag/config:/config \
-e PGID= -e PUID= \
-e EMAIL= [email protected]
-e URL=mydomain.duckdns.org
-e SUBDOMAINS=hass,sub1,sub2
-e VALIDATION=http
-p 80:80 -p 443:443
-e TZ=XXX/XXX \
linuxserver/letsencrypt

(get your timezone from here List of tz database time zones - Wikipedia)

Sample docker-compose

swag:
  image: linuxserver/swag
  container_name: swag
  restart: unless-stopped
  cap_add:
  - NET_ADMIN
  volumes:
  - /etc/localtime:/etc/localtime:ro
  - /home/user/docker/swag/config:/config
  environment:
  - PGID=1004
  - PUID=1000
  - [email protected]
  - URL=mydomain.duckdns.org
  - SUBDOMAINS=hass,sub1,sub2
  - VALIDATION=http
  - TZ=XXX/XXXX
  ports:
  - "80:80"
  - "443:443"

You must pick at least one subdomain, which will represent home assistant. (in this example hass). So once it’s running HA will be, for example, in https://hass.mydomain.duckdns.org

Once you run the container, you’ll need to edit the default file at (example) home/user/docker/swag/config/nginx/site-confs/default

Some notes on this:

I. Make sure you comment out the following lines in the server blocks.

  auth_basic "Restricted"#;
  auth_basic_user_file /config/nginx/.htpasswd;

These lines will enforce password protection from Nginx and when you try to login you will not be able. You may need to activate this for some component. For instance the configurator component, for some reason will no longer follow its settings file and once live you’ll be able to access it without password. So you’ll need to create an Nginx user:password with this command: docker exec -it letsencrypt htpasswd -c /config/nginx/.htpasswd .

II. Next tip took me a while to discover/resolve. There’s a separate proxy.conf file in
home/user/docker/swag/config/nginx/proxy.conf

the proxy serever blocks include by default the below line which calls this file:

include /config/nginx/proxy.conf;

It worked right away with 8 other components but NOT with Home Assistant. After much reading it turns out that Home Assistant’s “handshake” is different etc, etc, and therefore the proxy configuration is different. So the server block for Home Assistant is different to the other 3 examples below. Basically I commented out the reference to the default proxy.conf file and inserted the specific configuration in the server block itself.

Below find my file (this would replace default nginx/default.conf)

You need to edit all sections containing “mydomain.duckdns.org” as well as “fastcgi_pass hostip:9000;” and “proxy_pass http://hostip:XXXX;”

Once done. Restart the container and you should be able to see https://hass.mydomain.duckdns.org; https://conf.mydomain.duckdns.org; etc

default.conf

## Version 2018/04/20 - Changelog: https://github.com/linuxserver/docker-letsencrypt/commits/master/root/defaults/default

# listening on port 80 disabled by default, remove the "#" signs to enable
# redirect all traffic to https
#server {
#	listen 80;
#	server_name _;
#	return 301 https://$host$request_uri;
#}

################################################################################
#### PORT 80 ACTIVE ############################################################
# listening on port 80 disabled by default, remove the "#" signs to enable
# redirect all traffic to https
server {
	listen 80;
	server_name mydomain.duckdns.org;
	return 301 https://$host$request_uri;
}
################################################################################
################################################################################

# main server block
server {
	listen 443 ssl default_server;

	root /config/www;
	index index.html index.htm index.php;

	server_name mydomain.duckdns.org;

	# enable subfolder method reverse proxy confs
	include /config/nginx/proxy-confs/*.subfolder.conf;

	# all ssl related config moved to ssl.conf
	include /config/nginx/ssl.conf;

	client_max_body_size 0;

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

	location ~ \.php$ {
		fastcgi_split_path_info ^(.+\.php)(/.+)$;
		# With php7-cgi alone:
		fastcgi_pass hostip:9000;
		#fastcgi_pass 127.0.0.1:9000;
		# With php7-fpm:
		#fastcgi_pass unix:/var/run/php7-fpm.sock;
		fastcgi_index index.php;
		include /etc/nginx/fastcgi_params;
	}

# sample reverse proxy config for password protected couchpotato running at IP 192.168.1.50 port 5050 with base url "cp"
# notice this is within the same server block as the base
# don't forget to generate the .htpasswd file as described on docker hub
#	location ^~ /cp {
#		auth_basic "Restricted";
#		auth_basic_user_file /config/nginx/.htpasswd;
#		include /config/nginx/proxy.conf;
#		proxy_pass http://192.168.1.50:5050/cp;
#	}

}

# sample reverse proxy config without url base, but as a subdomain "cp", ip and port same as above
# notice this is a new server block, you need a new server block for each subdomain
#server {
#	listen 443 ssl;
#
#	root /config/www;
#	index index.html index.htm index.php;
#
#	server_name cp.*;
#
#	include /config/nginx/ssl.conf;
#
#	client_max_body_size 0;
#
#	location / {
#		auth_basic "Restricted";
#		auth_basic_user_file /config/nginx/.htpasswd;
#		include /config/nginx/proxy.conf;
#		proxy_pass http://192.168.1.50:5050;
#	}
#}

################################################################################
### SUBDOMAIN 1 ##################################################################
server {
	listen 443 ssl;

	root /config/www;
	index index.html index.htm index.php;

	server_name sub1.mydomain.duckdns.org;

	include /config/nginx/ssl.conf;

	client_max_body_size 0;

	location / {
#		auth_basic "Restricted";
#		auth_basic_user_file /config/nginx/.htpasswd;
		include /config/nginx/proxy.conf;
		proxy_pass http://hostip:5050;
	}
}

### SUBDOMAIN 2 ###############################################################
server {
	listen 443 ssl;

	root /config/www;
	index index.html index.htm index.php;

	server_name sub2.mydomain.duckdns.org;

	include /config/nginx/ssl.conf;

	client_max_body_size 0;

	location / {
#		auth_basic "Restricted";
#		auth_basic_user_file /config/nginx/.htpasswd;
		include /config/nginx/proxy.conf;
		proxy_pass http://hostip:8181;
	}
}

### HOMEASSISTANT ##############################################################
server {
	listen 443 ssl;

	root /config/www;
	index index.html index.htm index.php;

	server_name hass.mydomain.duckdns.org;

	include /config/nginx/ssl.conf;

	client_max_body_size 0;

	location / {
#		auth_basic "Restricted";
#		auth_basic_user_file /config/nginx/.htpasswd;
		proxy_set_header Host $host;
		proxy_redirect http:// https://;
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection "upgrade";
		proxy_buffering               off;
		proxy_ssl_verify              off;
#		include /config/nginx/proxy.conf;
		proxy_pass http://hostip:8123;
	}
}

# enable subdomain method reverse proxy confs
include /config/nginx/proxy-confs/*.subdomain.conf;

Edit: March 11, 2019
Since the original post I’ve made a small change to the set-up as a result of the linuxserver guys improving this docker container.
Initially I was adding each subdomain under the - SUBDOMAINS=hass,sub1,sub2 environment variable.
If you’re using duckdns (as I am) you can now pull a wildcard ssl certificante, which does not require you specifying the sub-subdomain.
To clarify: earlier the certificate would be issued for sub-subdomain.mydomain.duckdns.org in line with the Subject Alternative Name (protocol).
with this revised setup your certificate will be issued for **.mydomain.duckdns.org which means you can create new sub-domains which are covered by the “*” (the wildcard) without adding sub-subdomains to the - SUBDOMAINS variable. You only need create the server block in the nginx/default.conf file as before. Hope this helps understanding.

Here’s my updated docker-compose

  swag: #https://hub.docker.com/r/linuxserver/swag
    container_name: swag
    image: linuxserver/swag
    restart: unless-stopped
    cap_add:
    - NET_ADMIN
    volumes:
    - /home/user/docker/swag/config:/config
    - /etc/localtime:/etc/localtime:ro
    environment:
    - PGID=1004
    - PUID=1000
    - [email protected]
    - URL=mydomain.duckdns.org
    - SUBDOMAINS=wildcard
    - VALIDATION=duckdns
    - TZ=Asia/Dubai
    - DUCKDNSTOKEN=XXXXXXXXX   (you need to get this from your duckdns account)
    ports:
    - "180:80"
    - "1443:443"

Edit: Sept 13, 2020
On request I’m adding samples from my docker-compose file.

Some dockers use the “deploy/resources” flag. When using this you need to add the following to your docker compose command:
docker-compose --compatibility up -d

compose-sample

51 Likes

Thanks to you @juan11perez (and @ludeeus, who also tried helping me out before I found this), I have stopped just short of pulling out all my hair - head, armpits and elsewhere! A big sigh of relief from here, as https://hass.MYSECRET.duckdns.org finally showed the frontend for the fist time with everything installed in docker containers! :grinning:

Thank you so much for this (for me, very timely; I started with this yesterday morning) guide.

@Aephir, Im happy you found it useful and got your set-up working. I do not exaggerate when I tell you I spent an entire “holiday” week, probably 14 hours a day trashing this until I got it working.

4 Likes

I assure you, it is much appreciated! I’ll be sure to try spreading this far and wide.

1 Like

@Aephir welcome.

Awesome! Glad you were able to get the suggested linuxserver Let’s Encrypt container going and adapted to your setup. It’s awesome and I’ve been using it for over a year or so. Going to take a look at your config and see if I can improve mine any and also get configurator going through Nginx.

Depending on your apps you have, I did a few other mods where I mapped some logs from various containers out to my SSD, and created maps where Let’s Encrypt could read them to watch for login attempts for fail2ban. Works pretty great.

@digiblur thank you indeed for suggesting the container. It’s really good.
On configurator, first I just added a password with the command mentioned above and it worked. It’s locked out, but still when I open it doesn’t land where I want (homeassistant) as it ignores the settings.conf file. So I installed this.

docker-compose

cloud9: # Docker
container_name: cloud9
image: sapk/cloud9
restart: unless-stopped
volumes:
- /home/user/docker/homeassistant:/workspace
command: --auth user:password
ports:
- “8181:8181”

Not only is it “pretty”. it’s awsome. try it. You wont be dissapointed

2 Likes

Nice. I’ll definitely try it, a little different on the install of the docker configs as I go through the web GUI Docker interface on unRaid but I can adapt the config over without any issue.

You’re adding to my growing list of containers! I have almost 30 going right now, so what’s 1 more right? Get more use out of the 24 gigs of RAM that I barely use right now.

@digiblur. Definitely. Another container i got working is the facebox.
Face recogintion locally.
I got one of these https://www.hikvision.com/uk/Products/Video-Intercom/Wi-Fi-Door-Bell/DS-KB6003-WIP installed.
Recognises the face and announces via TTS or anything.
The image is very good and full control of your pictures/videos. Stored locally.
I use zoneminder (container). But you can use the sd card slot.

3 Likes

I have a few Hikvisions around and I recently saw this container as well, definitely going to try it as well. It’s been a while since I played with Zoneminder, I was going to go with BlueIris but I just didn’t want a Windows VM on my box eating up RAM and CPU time when containers are so lightweight on things. Running on a 24gig ECC RAM, 24 TB of spinners, 500 gig SSD on a Xeon E3-1225 v3 CPU, but still like to keep things lightweight (spoiled by those complete HA container restarts taking 2-3 seconds).

1 Like

@digiblur been using this container ldocky/zoneminder for couple of months. works perfect. also recommended.

Has anyone looked into using traefik (https://traefik.io)? Seems to offer easy reverse proxy for docker (and others) and also supporting let’s encrypt?

what about google assistant integration? You need a block for that. Anyone using this setup with GA? If so, do you have the block configuration?

Im not using Google Assistant. Please share if you come across it.

Not to derail the thread to a new topic but do you have any experience using Traefik yourself. Looks interesting and if it’s as easy as they claim it could be a good tool.

do you have a good working docker-compose file that incorporates Home Assistant and Let’sEncrypt?

I have NGINX reverse proxy working with GA. There was quite literally nothing special that I had to do.

Can you share your ngnix code?

So played a bit with it and got Google Home Assistant working.
you need to go into the nginxt, default file and include http.
In my example the file is at home/user/docker/letsencrypt/config/nginx/site-confs/default

add this block or uncomment at the top respectively:

/#### PORT 80 ACTIVE #############################################################
/# listening on port 80 disabled by default, remove the “#” signs to enable
/# redirect all traffic to https
server {
listen 80;
server_name mydomain.duckdns.org;
return 301 https://$host$request_uri;
}
/################################################################################

After that just follow the set up guide Google Assistant - Home Assistant

1 Like

hmm thats odd, I thought GA required https. But if you got it working, cool! BTW, does your cert auto update?