Skip to main content

Example: a web app behind a reverse proxy

This page models one real setup from start to finish. Read Applications, services, and gateways for every field and rule.

The setup​

You run Grafana in Docker on two virtual machines on the hypervisor pve1. Each container publishes port 3000 on its VM. People reach Grafana in two ways:

  • At home, as grafana.home.lan, through Nginx on proxy-lan.
  • From the internet, as grafana.example.com, through Traefik on proxy-dmz.

This is the inventory you start with, recorded as in Devices:

DeviceAddressNotes
pve1The hypervisor
grafana-vm192.168.10.21Virtual machine under pve1
grafana-vm2192.168.10.22Virtual machine under pve1
proxy-lan192.168.10.5Runs Nginx
proxy-dmz192.168.10.6Runs Traefik

The addresses are on the devices' interfaces. That is where MiniPAM looks up an application's address.

In MiniPAM terms:

  • Grafana on each VM is an application instance: something listening on port 3000.
  • Grafana is one service, served by both instances.
  • nginx-lan and traefik-public are gateways, and each has a route to the service.

1. Record where Grafana listens​

Record the application on the VM, not on the container and not on pve1. The container publishes its port on the VM, so the VM's address and port 3000 are what Nginx and Traefik connect to. Choose IP address under Runs on only when the container has an address of its own, for example on a macvlan network.

  1. Open Inventory → Applications and choose Create application.
  2. Fill in:
    • Runs on: Device, then grafana-vm.
    • Name: Grafana.
    • Protocol: http.
    • Port: 3000, with TCP.
  3. Choose Create.
  4. Repeat for grafana-vm2, with the same name, protocol, and port.

The Applications page now shows one Grafana row with "2 instances". Expand it to see both VMs. The row is only a view: the two instances stay separate records.

Leave Listens on empty. Docker publishes the port on every address of the VM, and MiniPAM then uses the VM's interface address.

2. Create the Grafana service​

  1. Open Inventory → Services and choose Create service.
  2. Fill in:
    • Name: Grafana.
    • Protocol: http.
    • Category: monitoring.
    • Main address: A gateway route, because people use the proxy hostnames, not the VM addresses.
  3. Under Served by, choose Add application and pick grafana-vm - Grafana:3000/TCP. The first one becomes primary.
  4. Choose Add application again and pick grafana-vm2 - Grafana:3000/TCP. Set its Role to replica.
  5. Save the service.

The primary flag and the replica role are notes for people. Both instances are equal backends to MiniPAM and to templates, and MiniPAM does not fail over between them.

3. Add the gateways and routes​

Create one gateway per proxy, because each generates its own configuration file:

  1. Open Inventory → Gateways and choose Create gateway.
  2. Name nginx-lan, and under runs on add proxy-lan. Create it.
  3. Do the same for traefik-public on proxy-dmz.

Then add the routes. Edit nginx-lan and, under routes to, choose Add route for each:

HostPathServicePriority
grafana.home.lan(blank)Grafana100
grafana.home.lan/apiGrafana100

The /api route is only there to show how a path comes out in the generated file. Leave it out if you have no reason to treat that path differently.

Edit traefik-public and add one route:

HostPathServicePriority
grafana.example.com(blank)Grafana100

You can add the same routes from the service instead: under Reached via, choose Add route.

The service's details now show both routes under Reached via and both VMs under Served by.

4. Generate the proxy configuration​

Templates need the Templates workspace feature.

Nginx for nginx-lan​

  1. Open Templates & checks → Templates, choose New template, and pick Nginx reverse proxy.

  2. Near the top of the source, find the line marked EDIT THIS and set the gateway name:

    {%- set gateway_name = 'nginx-lan' %}

    The starter already says nginx-lan. Change it whenever your gateway has another name.

The preview renders the gateway's routes:

nginx.conf
# Generated by MiniPAM for gateway "nginx-lan".

upstream grafana_home_lan {
server 192.168.10.21:3000;
server 192.168.10.22:3000;
}

upstream grafana_home_lan_api {
server 192.168.10.21:3000;
server 192.168.10.22:3000;
}

server {
listen 80;
server_name grafana.home.lan;

location / {
proxy_pass http://grafana_home_lan;
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;
}

location /api {
proxy_pass http://grafana_home_lan_api;
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;
}
}

Each route becomes an upstream with both Grafana instances, and each FQDN one server block with a location per path. Include the file inside the http {} block, for example as /etc/nginx/conf.d/minipam.conf, and run nginx -t before you reload.

Traefik for traefik-public​

Create a second template from Traefik dynamic configuration. Its gateway_name line already says traefik-public:

{%- set gateway_name = 'traefik-public' %}
traefik.yml
# Generated by MiniPAM for gateway "traefik-public".
http:
routers:
grafana-example-com:
rule: "Host(`grafana.example.com`)"
service: grafana-example-com
services:
grafana-example-com:
loadBalancer:
servers:
- url: "http://192.168.10.21:3000"
- url: "http://192.168.10.22:3000"

Point Traefik's file provider at the downloaded copy.

Each file only covers its own gateway, so the public Traefik never learns about grafana.home.lan. To publish the files and let the proxies fetch them, follow Templates.

Common modeling questions​

The application listens on several addresses​

When it runs on a Device, MiniPAM uses the device's interface addresses, and the starters take the first one. If the application listens on one specific address, enter it under Listens on: the starters then use that address. Choose All addresses when it really listens everywhere. If the address belongs to no recorded device, choose IP address under Runs on instead.

The service has several backends​

Add each instance under Served by on the one service. Templates get them all as equal backends, as in the output above. Keep primary, Role, and Priority for notes; templates can't read them.

The service is not behind a gateway​

Add no routes and set Main address to The primary application. People connect to the applications' addresses directly, and the proxy starters leave the service out.

One service is internal and public​

Create one gateway per entry point, such as nginx-lan and traefik-public, and add a route on each. Routes have no internal or public flag. The gateway a route belongs to is what tells them apart.

DNS and other UDP services​

Record the instances with udp, such as port 53, and a service with Protocol Family dns. They usually have no route: HTTP reverse proxies don't carry them, and the starters only write HTTP configuration.

TLS and certificates​

Routes don't record TLS, certificates, or the port the proxy listens on. Handle them in the proxy, or add them to your copy of the template. The Nginx starter writes listen 80, and both starters connect to the backends over http://.

A container with its own address​

When a container has its own address, for example on a macvlan network, record that address under Inventory → IP addresses and create the instance with an IP address target. The proxy then connects to the container's address instead of its host.