Reverse Proxy with NGINX

Reverse Proxy with NGINX
Photo by 愚木混株 cdd20 / Unsplash

Because many people don't want to spend too much money for a big server farm where they can run many VMs under a hypervisor, especially at home, it makes sense to run several services on only one server or mini-pc. In my case I solve this problem with Docker and Docker Compose. But I also want these services to be reachable under one ip address. This is where the proxy manager of nginx comes into play. This allows me to cover exactly this scenario and instead of multiple ethernet interfaces I simply distribute the services to different ports. This can also be interesting for you if you are using a VPS with only one IPv4 address.

Anyway, in this post I will explain how to install nginx proxy manager with docker compose.

Prerequisites:

  1. You've root permission on your system.
  2. You've installed Docker and Docker Compose on your system like in this post.

Step 1: Add firewall rules

You should have a firewall like Firewalld installed, up and running.

ufw status

OUTPUT:
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)

If your firewall is not running already:

sudo ufw allow "OpenSSH" #If you are using SSH
sudo ufw enable

Open ports 80, 81 and 443 for later usage of nginx:

sudo ufw allow 80
sudo ufw allow 81
sudo ufw allow 443

Apply changes:

sudo ufw reload
ufw status

OUTPUT:
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80                         ALLOW       Anywhere
81                         ALLOW       Anywhere
443                        ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80 (v6)                    ALLOW       Anywhere (v6)
81 (v6)                    ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)

Step 2: Create the docker-compose.yml file

Create your container root folder first, where all the containers should be located:

#If not already exists
mkdir /opt/containers/

Create your container specific folder:

mkdir /opt/containers/nginx-proxy

Create docker-compose.yml file for editing:

cd /opt/containers/nginx-proxy
touch docker-compose.yml

Create directories where user data and SSL certificates are mapped to:

mkdir {data,letsencrypt}

Edit the docker-compose.yml file:

sudo nano docker-compose.yml
#NGINX PROXY MANAGER
version: "3"
services:
  npm-app:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm-app
    restart: unless-stopped
    ports:
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
      # Add any other Stream port you want to expose
      # - '21:21' # FTP
    environment:
      DB_MYSQL_HOST: 'npm-db'
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: 'npm-user'
      DB_MYSQL_PASSWORD: 'npm-password'
      DB_MYSQL_NAME: 'npm'
      # Uncomment the line below if IPv6 is not enabled on your host
      DISABLE_IPV6: 'true'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    depends_on:
      - npm-db

  npm-db:
    image: 'mariadb:latest'
    container_name: npm-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: 'npm-password'
      MYSQL_DATABASE: 'npm'
      MYSQL_USER: 'npm-user'
      MYSQL_PASSWORD: 'npm-password'
    volumes:
      - ./data/mysql:/var/lib/mysql

Save the file by pressing CTRL + X and Y when promted.

But, what does this file do?

Let us go through the file. The first part of the file is where we import Nginx proxy manager's image and set some environment variables in the form of database credentials. We also expose ports 80, 81 and 443 to the server for access. You can expose more ports, such as 21, for FTP access. You can disable IPV6 support by uncommenting the line DISABLE_IPV6: 'true'. We have mapped several directories from our host to the Docker for data and SQL storage.

We are using two networks here. One is the internal network npm-internal to connect the proxy manager and the database. This one is optional since it is automatically created. But here, we are doing it manually by giving it a name of our choice.

We have added an external network to the proxy manager container called npm-nw. This network is essential because you can use it to connect the proxy manager to any other docker containers you install. Using a network will allow you to connect any container directly without the need to expose its ports to the server.

Step 3: Create the Docker container for Proxy Manager

Build the container:

docker compose up -d

Check if the new created container is running with:

docker ps

Step 4: Access Nginx Proxy Manager

Your Proxy Manager can now be accessed with your browser of choice via http://server-ip-address-or-hostname:81

Default credentials are:

After the initial login you are forced to change the Email address and password.

Step 5: Create an dyndns account

This step may vary to your specific approach to handle certificates and domains with the Proxy Manager. In my case I use my pawnda.de domain for this but you can also just use https://www.duckdns.org/ and point an A-Record to your public IP-address of your ISP. But your ISP sometimes changes your public IP-address which means you need dyndns for an automatic refresh of your IP-address.

I'm using https://selfhost.co to solve this problem. Feel free to use any dyndns service of your choice.

Once you've set up a dyndns record to your public IP you can use your public domain and create a CNAME-record and point the CNAME to your duckdns or dyndns name. For example:

CNAME home.your.domain.com - dyndns.selfhost.co
CNAME *.home.your.domain.com - dyndns.selfhost.co

To explain this: we want our locally hosted services, such as the proxy manager itself or even a nextcloud, to be accessible via our domain of choice. The goal is that these pages are then also encrypted with letsencrypt ssl certificates. To make this possible from our local network, without port-forwarding or similar approaches, we have to proceed as explained above.

After all this is done and we do have a domain for our approach, we need to configure our NGINX Proxy Manager.

Step 6: Add SSL to Proxy Manager

Open your Proxy Manager like http://your-server-ip:81 and go to SSL Certificates.

  1. Add SSL Certificate
  2. Let's Encrypt
  3. Add your domain from Step 5. In my case home.pawnda.de and *.home.pawnda.de which we created a CNAME to our dyndns
  4. Activate "Use a DNS Challenge"
  5. If you chose duckdns for your setup you can copy the token given by duckdns website
  6. If you chose anything else (like me) you maybe need an API key from your provider
  7. Save your config and wait a minute. This takes a while
  8. After that, it should look something like this:

If you are running in any errors saving your config you should try increase the propagation time.

Step 7: Add your first proxy host

Open your Proxy Manager like http://your-server-ip:81 and go to Hosts->Proxy Hosts.

  1. Add Proxy Host
  2. Choose your domain name. In my case: npm.home.pawnda.de

Switch to SSL tab:

Hit save.

Now you should be able to reach the Proxy Manager with the new url: npm.home.pawnda.de.

If not, you probably have a custom DNS running as I do: PiHole.

Step 8: Add your domain to your local DNS

Login to your local DNS-Server (PiHole) and add under Local DNS->DNS Records your domain as A-record:

After that, go to CNAME Records and point your npm to your A-record above:

You have to do this for all your future sites you'll set up later, even when not using the container names f.e. an IP-address. You should manage the forwarding all via NPM. For example for nextcloud you need another CNAME-record with something like nextcloud.home.pawnda.de and as IP for example 192.168.1.100:443. I think you get the point here.

Troubleshooting:

I forgot my password of the user I created in the beginning because I was sure I saved it into my password manager. Well, I was wrong.

Connect to your docker container:

docker exec -it npm-db bash

Open mysql command line: (password was set in docker-compose.yml)

 mysql -u root -p

Open database: (the one we've set up in docker-compose.yml)

use npm

Disable users:

UPDATE user SET is_deleted=1;

Restart/Recreate your containers:

cd /opt/containers/nginx-proxy/
docker compose up -d --force-recreate

Access the Proxy Manager website and log in with default credentials:

Create a new user with different username and this time, save the password.

Enable users again otherwise you'll get an error showing your previously added certificates and proxy hosts:

UPDATE user SET is_deleted=0;

Now you are able to see all created users under the users menu on the Proxy Manager website. Change the password of your old user or use the new one instead. I like my system clean, so I changed the password of the "old" user, logged in and deleted the new user.

Source: https://www.howtoforge.com/how-to-install-and-use-nginx-proxy-manager/