Taking stock of our homelab, we now have a decent Proxmox node machine up and running with a container to host a WordPress website, and more importantly, a local DNS (Technitium) up and running. We have also set up the Technitium DNS to enable us to go to the website address wordpress.local to access the WordPress website, instead of going to the savage-to-remember 192.168.1.43 (or whatever, I can't remember what the container IP address is any more). But what about accessing our Proxmox server? Currently we still have to go to 192.168.1.2:8006 to access Proxmox (your IP address may be different). How about we change that to something easier to remember, like proxmox.local? Although we can "kinda" do that with a DNS server, notice the port number, 8006, at the end of the address. This means that if we add the record proxmox to our local DNS zone pointing to IP address 192.168.1.2, we are still going to be stuck with the port number. Instead of the address being proxmox.local, it will be proxmox.local:8006. That might be acceptable for some, but we can do better. And that is where a reverse proxy has its use. NGINX (pronounced "engine-x") is an open-source web server software known for its high performance and scalability, used as a reverse proxy, load balancer, and HTTP cache.


Step 1: Download NGINX Container Template

By now you should be fairly well versed in accessing your Proxmox instance, so go to the (soon-to-change) website IP address, in our case https://192.168.1.2:8006, and log in with your credentials (default username is root). Once you have logged in, in the left navigation pane, expand your main Proxmox node. Select the drive local, which is the install directory of Proxmox. In the middle navigation pane, select CT Templates to see the list of all of the previously downloaded LXC Container templates. If you don't have the NGINX template downloaded, click Templates in the main display pane. Search for NGINX and click Download. Once the template download is completed, we can start the creation of a new container.


Step 2: Create a new NGINX Container

In the top right, click the Create CT button. Going through the settings, we need to fill on the following on each screen:

General
In the general screen, fill in the CT ID field, this can be any number normally in the 100 - 999 range, and this is just an ID for the container. We are going to add it just after our DNS container ID, so we use 101. Fill in the hostname, preferably nginx so it is easily identifiable and choose a password. This password is the password for the root user of the Linux container. Template
In the template screen, select the container template. You should only have the option to select local in the Storage dropdown as that is the only place where container templates are installed. In the Template field, select the NGINX container template downloaded in Step 1. Disks
Now we come to the location where the container will be installed. Select any disk of your choosing and disk size as well. The wizard offered an 8 GB default, and our screenshot shows a 4 GiB disk selected for this small lab. The roughly 1.1 GB used by our blank appliance was an observation at publication, not a minimum for every release. Allow room for the operating system, updates and logs as well as NGINX; you can increase the disk later when the underlying storage has space. CPU
This is just the maximum amount of CPU cores the container has access to. This does not mean that those cores are only assigned to this container, it merely states that the container cannot access more than these. We opted for one core as a starting allocation for this small lab. Traffic levels, TLS encryption and enabled proxy features affect the work NGINX performs, so monitor your own workload before deciding whether to increase it. Memory
Same as CPU, this states the maximum. The defaults are fine as is, if we need more we can always increase this. Our idle appliance used around 170 MB of RAM at the time of writing. This is an observation of that setup, not a guaranteed memory requirement for other releases or workloads. Network
Leave as is and just continue to the next screen. We will do a more in-depth dive of the networking (and firewall) in a future article. DNS
Again, leave as Use Host Settings, we might change this in a future container or VM. Confirm
Check that you are happy with all of the settings, and if you are, hit Finish . The installer will run and let you know once done. You are welcome to tick the Start after created option to save you the hassle of starting the newly created container.

Step 3: Configure the NGINX Container

Now we need to configure the TurnKey Linux Template Container, before we can configure NGINX itself. In the left navigation pane, select the newly created nginx container (and start it if it is not running). In the middle navigation pane, select Console to open a console into the container, running in your local browser. You will be greeted with the default TurnKey Linux setup screen. Firstly, a password is needed for the adminer interface. Select a password and confirm it on the following screen. Next up, you will be asked for an API key for your TurnKey Backup and Domain management accounts. For now, just skip this (we will go through the steps in a future article). On the next screen, you can decide if you want to be notified of local system notifications. Either enter your email address and Enable, or just Skip. The following screen will prompt you if you want to install the latest security updates. Actually this updates all packages to their latest versions, so it is a good idea to run this now. Once the update process finished (can take a few minutes), you will be shown all the addresses you can use to access this container. In our case, we see the container is running on IP address 192.168.1.4. Open a new tab (or browser window) and go to the address displayed. You will see a screen where you can either go to the Adminer insterface (for your databases) or the TurnKey Webmin interface to manage the container. You are welcome to have a look at both, but we are not going to use these to set up NGINX. (If you get a certificate security warning for either site, just accept and continue - see flow below).


Step 4: Configure DNS

Before using these management names: The screenshots were accurate for the original lab demonstration. The port-80 routes below use HTTP, so the browser-to-NGINX connection is unencrypted. HTTPS on the Proxmox backend does not encrypt that first part of the journey. Use the HTTP names only to check that routing reaches the expected page, without submitting passwords or other sensitive information.

For real Proxmox administration, retain your direct HTTPS address, shown here as https://192.168.1.2:8006, until a trusted HTTPS proxy endpoint is configured. Confirm the server and certificate identity before signing in. Technitium’s pictured HTTP dashboard on port 5380 is also unencrypted; use a properly configured HTTPS management endpoint for protected login rather than assuming that changing the URL’s scheme enables it. Keep these administration services private. The existing NGINX block and screenshots remain the recorded HTTP example; this note does not introduce a newly tested TLS configuration.

Next up we need to set up our .local DNS names to point to the NGINX instance (NOT the IP's of the services). Open up the Technitium DNS web interface we installed in the previous article. Go to Zones, and select the local zone. We now need to add two Records. Firstly, a record with Name of proxmox and secondly a record with name dns. Both records need to be of type A and must have the IPv4 Address of the NGINX container, in our case 192.168.1.4. Save the records. This means that if we now go to http://dns.local or to http://proxmox.local, we will be "directed" to the NGINX server, and you should see the same interface you saw when going to the IP address directly with the option of going to Adminer or Webmin. This means our DNS is working.

Step 5: Configure NGINX

Back in the Proxmox window, and still in the NGINX container's console window, run the following command:

cd /etc/nginx/sites-available/

This will change the working directory to that where NGINX stores its configuration files. You can run ls -al to see a list of all files in the directory (we will get to that blue one in a minute). Each of these files are a configuration file and can be seen as a group of sites. We want to add a new file, specifically for all our .local domains, so we will run the command

nano local

This creates a new file, local in the directory and opens it up for editing. We need to add the following:

server {
   server_name proxmox.local;
   listen 80;
   location / {
        proxy_pass https://192.168.1.2:8006;
        proxy_ssl_verify off;                # Skip SSL verification (if using a self-signed cert)
        proxy_http_version 1.1;
        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 Upgrade $http_upgrade;      # Needed for WebSocket support
        proxy_set_header Connection "upgrade";
    }
}

server {
   server_name dns.local;
   listen 80;
   location / {
      include proxy_params;
      proxy_pass http://192.168.1.3:5380;
   }
}

Keep the braces, semicolons and directive names as shown. Indentation helps readability; NGINX does not require an exact indentation width.

To break it down, the file contains the following:
- Two server blocks, one for proxmox and one for dns.
- The server_name contains the address that the user entered in their browser to reach this nginx configuration. So one is for proxmox.local and one is for dns.local
- Both listen on port 80, which is http.
- With the location as /, meaning there is nothing after the proxmox.local or dns.local address (ex: http://proxmox.local/ and http://dns.local/), we need to do two things:
- Add the header parameters stating that we are passing this request via proxy
- Pass the request (proxy_pass) to the relevant IP address... and port!

Now that we have a way to pass both the IP address and port, go ahead and save the file with Ctrl+S and exit with Ctrl+X Use the Control key on a Mac too; nano’s ^ notation means Control, not Command. If your terminal intercepts Ctrl+S, use the displayed Write Out shortcut (Ctrl+O in the pictured nano 7.2), confirm the filename with Enter, then exit with Ctrl+X. Next up we need to "activate" this configuration file. We do that by creating a shortcut (a soft link in Linux terms) to the sites-enabled directory. Run the following command:

ln -s /etc/nginx/sites-available/local /etc/nginx/sites-enabled/

To test that it worked, change into the directory /etc/nginx/sites-enabled/ and run ls -al. You will see one of those blue links for local pointing to the file we created.

We can now test our configuration by running the following command:

nginx -t

If we don't have any errors, all we need to do is reload NGINX with the following:

systemctl reload nginx

We can now just enter the addresses http://proxmox.local in our browser to access proxmox (without worrying about the port) and the same for http://dns.local to access our Technitium DNS config.

You can now see your Proxmox with http://proxmox.local and your Technitium with http://dns.local:

promxmox local
dns local

Just a quick note, if it does not work immediately, it might be because your machine has not updated the DNS. Give it some time, or restart the machine to update the configuration

Optional After the Walkthrough: Proxy Validation Without Changing the Backends

The main NGINX block already includes HTTP/1.1 and WebSocket upgrade headers for Proxmox. Keep its demonstrated backend https://192.168.1.2:8006 and the Technitium administration backend http://192.168.1.3:5380. The address belongs to the service being proxied, while the DNS A record for the friendly name points to NGINX. Mixing these two roles can create a loop or send the request to the wrong machine.

First test each service directly from the proxy's network, then through its friendly name. If direct access works but the proxy fails, check the relevant server block and NGINX error log. If neither works, check that the service is running and the address/port has not changed. The Technitium page on port 5380 is its web administration interface, not the DNS resolver port used by clients.

Run nginx -t before a reload. It checks the configuration and referenced resources, but cannot prove the upstream is healthy or that a browser console session works. After reloading, check that the friendly names reach the expected pages without entering real credentials over HTTP. Complete login and interactive-console checks through a trusted HTTPS management route; separately verify the DNS dashboard through its appropriate protected endpoint. A successful HTML response alone does not verify WebSocket traffic.

The photographed proxy_ssl_verify off accommodates the lab's self-signed upstream certificate. It disables verification on the NGINX-to-Proxmox connection and is not a general security improvement. Trusting the appropriate upstream CA and checking its name is an optional later change that needs its own certificate setup. Do not toggle verification on without arranging that trust first.

Keep this stage on the intended private network. Later public routing and HTTPS articles build on it; exposing the Proxmox or DNS administration interface publicly is not required for those examples. If a login fails despite the existing upgrade headers, investigate the browser request, cookies and logs rather than replacing the whole configuration with a different server IP.

Further reading: NGINX WebSocket proxying, NGINX proxy module.

Management and keyboard references: NGINX HTTPS configuration, GNU nano shortcuts, Proxmox HTTPS endpoint.

Conclusion

In conclusion, we've successfully transformed our homelab's accessibility by integrating a reverse proxy with NGINX into our Proxmox environment. By setting up the NGINX container and configuring both our local DNS records and the proxy settings, we’ve replaced cumbersome IP addresses and port numbers with memorable, user-friendly domain names like proxmox.local and dns.local. This not only streamlines our workflow but also provides a scalable foundation for managing multiple services efficiently. As you continue to expand your homelab, remember that this approach can be adapted to a variety of applications, ensuring a cleaner, more organized network setup. Enjoy the newfound simplicity and flexibility that comes with a well-configured reverse proxy!