The self-hosted movement is gaining momentum, with users wanting to reduce their reliance on cloud services while keeping their data safe on their home network. But once you've spun up a few containers, maybe with Portainer to simplify management, you run into one issue. Accessing every individual service via their own IP address and port gets annoying fast and becomes a major source of friction when trying to get used to self-hosted apps.

To get around this annoyance, setting up a reverse proxy lets you access your self-hosted apps from a single IP address, which simplifies access and management. While there are many viable options for doing so, one of the most common (and oldest) ways is by using Nginx. It's free, open-source, and easy to set up, and you'll have a reverse proxy working in no time at all.

What you need before we start

An installed version of your fav Linux distro and access to the command line

Setting up Nginx (pronounced engine x) won't take you very long to begin with, and all you need are some basic command line skills. We're assuming you have Ubuntu or Ubuntu Server installed for this guide, but you'll be able to find a Nginx package for your preferred Linux distribution,

  • Ubuntu installed on a virtual machine, a server, or your NAS
  • Sudo or root privileges
  • Access to the terminal or command line interface

How to set up a reverse proxy in Nginx on Ubuntu

Give your home lab some sweet new skills

Nginx is a fantastic program for anyone who wants to expand their home lab and learn some production skills. It's also one of the best ways to create a reverse proxy on Ubuntu. It's been around for a couple of decades, so it's been optimized and added to and can be used for load balancing, SSL offloading, and protecting against DDoS attacks.

To get it installed on Ubuntu, all we need is the terminal.

  1. Make sure the repositories are up-to-date by running:
    sudo apt update
  2. Install Nginx with the following:
    sudo apt install nginx
  3. Now, it's time to get the firewall sorted. Run the following:
    sudo ufw app list

The firewall on Ubuntu is ufw (Uncomplicated Firewall), and Nginx will automatically register itself so your job of configuring is easier. This might show a range of already configured apps, with the most common below:

  • CUPS: Common Unix Printing System, this handles printer services on the network.
  • Nginx Full: The entire Nginx web server with both HTTP and HTTPS traffic enabled.
  • Nginx HTTP: Only enables HTTP traffic.
  • Nginx HTTPS: Only enables HTTPS traffic.
  • OpenSSH: Allows SSH traffic to come into the server.

Now it's time to grant some basic permissions so these services can work on the server and the reverse proxy once we set it up.

  1. For example, if we want the Nginx HTTP service to have access we type:
    sudo ufw allow 'Nginx HTTP'
  2. Now we have to ensure the firewall is up with:
    sudo ufw enable
  3. And finally, check if the service is set up with:
    sudo ufw status

The last thing before we continue is to make sure Nginx is running:

sudo systemctl status nginx

It should say active (running) in green. If it isn't, start Nginx with:

sudo systemctl start nginx

Check the Nginx landing page

You can also verify it's installed and running by going to the IP address of your server in any web browser on your local network. In the example here, it's HTTP://192.168.7.171. If you see the above text about Welcome to nginx!, then you've got it installed correctly.

Now, to get the reverse proxy running

The first thing we need to do is some housekeeping because the default Nginx settings will get in the way.

  1. Unlink the default configuration file by typing:
    sudo unlink /etc/nginx/sites-enabled/default
  2. Now, we need a new configuration file. You can use any text editor you're comfortable with here:
    sudo vim /etc/nginx/sites-available/reverse-proxy
  3. Here's a basic configuration to get you started:
    server {
    listen 80;
    server_name localhost;
    location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }
    }
    ​​​​​​​

This file contains the details of a single reverse proxy to one service. The server {...} block handles any requests matching these details. You'll want to use the actual IP address and server name of your self-hosted services here instead of the placeholder details:

  • listen 80: Configures the port that the reverse proxy listens to. You don't have to use port 80, but sometimes it's easier.
  • server_name localhost: Replace localhost with the domain name of your self-hosted app.
  • location / {...}: This configures the URI path to match any incoming requests.
  • proxy_pass https://127.0.0.1: This is the backend server address Nginx is proxying to. Change it to the correct IP and port for whatever Docker container or other service you are passing information to.
  • proxy_set_header: These set the HTTP headers for the proxied request, including additional information about the client's request.

Once you've saved and exited vim, it's time to get the proxy up and tested.

Testing Nginx

Time to see if our reverse proxy works

Now to put it all into practice, so that incoming data requests to our self-hosted app are routed accordingly.

  1. First we have to link the configuration file we just made:
    sudo ln -s /etc/nginx/sites-available/reverse-proxy /etc/nginx/sites-enabled/
    This creates a symbolic link in the sites-enabled directory so that we have a configuration file that we can change by simply removing the symbolic links instead of accidentally editing the file directly.
  2. Now test the syntax with:
    sudo nginx -t
  3. And restart the Nginx server to apply our changes:
    sudo systemctl restart nginx

Test the routing

To see if everything is passing through as expected, we can create a tiny web page that shows the localhost routing in action. If you already have the configuration file pointing at a previously configured Docker container with a self-hosted app, you can skip ahead to the step that uses curl to test the routing. Alternatively, you can simply input the self-hosted app's IP address and port in a web browser, and it should show up.

  1. Create a test page directory with:
    sudo mkdir example_backend && cd example_backend
  2. Make a simple HTML file to serve as a landing page:
    sudo vim index.html
  3. Add some basic code to display:





    It works!



  4. Save and close the file with :wq
  5. Now we run the backend server from the same directory:
    sudo python3 -m http.server 8000
  6. Open a web browser and go to the IP address of the server and port 8000.

As you can see, the reverse proxy intercepts the client request and forwards it to the backend server. Then the backend responds with the web page, and we know the reverse proxy is set up correctly. If you were pointing to an existing self-hosted service, the webpage would be the admin page for that service instead.

👁 Accessing the CasaOS web UI from a PC
I love self-hosting services, and here are 5 reasons why you should, too

Besides being a fun hobby, there are plenty of reasons why you should start self-hosting your essential apps and services

Now you've got a basic reverse proxy set up in Nginx

This is only the start of what Nginx can do, as it can do load balancing, SSL encryption and decryption to keep the load off your services, handle security like access control lists and filtering, and cache data for faster access. But those are all advanced tools, and setting up the reverse proxy to access every self-hosted app from the same IP address is the main goal here for now.