Setting up a Reverse Proxy for a Home Lab

Once you start setting up a home lab you very quickly end up with a whole host of services and each one has it’s own port number and or host. To save you having to remember all of that you can set up a reverse proxy to bring everything together. The reverse proxy acts as a single endpoint for incoming requests, it then passes those requests on to the server that will actually deal with them.

Before you start you need to ask yourself if you want reverse proxy connections coming from the outside world or just within your home network? If it’s just your home network this is the article for you, if it’s the wider world then you’ll need to wait until I write the article on SWAG. An Nginx reverse proxy for your home network is an absolutely use case for an LXC container. You certainly could do it using a docker container as well but if you can easily do it with an LXC container that’s probably a better choice.

Note, I’m running as root while setting up this container, apply sudo as required.

Install the Container

I have a guide for this.

Install Nginx

Start by installing the nginx package

apt install nginx

Now, in Proxmox, turn on the firewall for the container and give it access to HTTP and HTTPS. Details for doing this are here. See if Nginx is set to run with this command:

systemctl status nginx

If you’ve restarted the server you’ll likely see something like this

* nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Tue 2023-07-25 19:23:04 UTC; 1min 36s ago
       Docs: man:nginx(8)
    Process: 136 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status>
    Process: 145 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCES>
   Main PID: 146 (nginx)
      Tasks: 3 (limit: 33451)
     Memory: 3.7M
        CPU: 10ms
     CGroup: /system.slice/nginx.service
             |-146 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             |-148 "nginx: worker process"
             `-149 "nginx: worker process"

Jul 25 19:23:04 rproxy systemd[1]: Starting nginx.service - A high performance web server and a reverse p>
Jul 25 19:23:04 rproxy systemd[1]: Started nginx.service - A high performance web server and a reverse pr>
lines 1-17/17 (END)

If it’s not started or enabled use this:

systemctl start nginx
systemctl enable nginx

Configure Nginx

The first thing you need to do is open the Nginx configuration file and check that a couple of lines are present and not commented out.

nano /etc/nginx/nginx.conf

Check that you have the line shown below. This tells Nginx to inlude any *.conf files that it find in the /etc/nginx/conf.d directory. You’ll also notice it loads from the sites-enabled directory which is configuration for sites Nginx is serving.

include /etc/nginx/conf.d/*.conf;

Exit Nano and switch to the config directory (cd /etc/nginx/conf.d) and create a file to configure the reverse proxy functionality (nano rproxy.conf). In that file make an entry like the one shown below.

server {
        listen 80;
        server_name jellyfin.example.co.uk;
        location / {
                proxy_pass      http://media.example.co.uk:8096;
        }
}

This tells Nginx to have all incoming requests to jellyfin.example.co.uk and route them to media.example.co.uk at port 8096 – media.example.co.uk is a virtual machine running Docker which in turn hosts Jellyfin. You’re not quite done yet, now you need to restart Nginx with the following command:

systemctl restart nginx

You’ll also need to make a DNS entry (in Pi-hole for example) that points jellyfin.example.co.uk at rproxy.example.co.uk, a CNAME entry is perfect for this.

Hopefully you can now see the benefits of this setup. Users no longer need to remember port numbers or enter them as pat of the URL which is something everyone seems to become confused about. It also gives you flexibility where you host your services. If you want to move a service to a different machine you just move it and update the Nginx rproxy.conf file.

Other Options

The example above showed you how to host services on different subdomains but that is not the only option you have, in fact out on the web this is not normally how it’s done. Subdomains, the jellyfin in jellyfin.example.co.uk, confuse people almost as much as port numbers so businesses tend to avoid them. The exception is the www subdomain and the other options is just to leave the subdomain off altogether to make the URL shorter. That, however, leaves us with a problem. How do you tell which service you want to use if you don’t have a different subdomain? The answer is the base URL. Most web applications will have a setting called Base URL or something like that (e.g. subfolder) which is used to set the first part of the URL after the domain. So in our example you’d maybe visit www.example.co.uk/jellyfin to get to the Jellyfin service.

That raises the question of why we don’t do this on our home network? At the end of the day it doesn’t really matter which option you choose but I find the subdomain option slightly preferable on my home network. Using a subdomain means you don’t need to worry about configuring the application and it works with applications that don’t support a base URL setting. The main downside is you need to make a lot of CNAME entries in the DNS.

If you decide to host services on the web you’ll almost certainly need to use the base URL option as many hosting providers limit the number of subdomains you are allowed.

Some Things to Note

If a machine has it’s own DNS entry with a service running on a high port number you can’t use a reverse proxy to map the port number (usually). To give a more concrete example, in my DNS server I have an entry for fileserver.example.co.uk which points as my file server container. The application running the file server is currently binding for 9090. I can’t easily use the reverse proxy to do a mapping from fileserver.example.co.uk:9090 to fileserver.example.co.uk:80. The problem is the DNS server has an A record for the subdomain, I can’t also make a CNAME record that points to the reverse proxy. There are three possible solutions, remove the A record and point the reverse proxy at the IP address, change the port number the server runs on and access the container directly, or use the fileserver subdomain on the reverse proxy and change the name of the server. I’m going to change the port number and access the container directly.

The reverse proxy configuration above only works for unsecured services, those running HTTP. If you want to access secured services, such as Portainer running on port 9443, you’ll need to do a bit more work (there may be a future article on this).

Some services, notable Calibre (it’s always Calibre), require additional configuration to pass through everything that is needed.

Conclusion

That’s all there is to it. Now you just need to make all the other entries in the Nginx configuration file. There’s loads more that Nginx can do but for now this is all we need.