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 onproxy-lan. - From the internet, as
grafana.example.com, through Traefik onproxy-dmz.
This is the inventory you start with, recorded as in Devices:
| Device | Address | Notes |
|---|---|---|
pve1 | The hypervisor | |
grafana-vm | 192.168.10.21 | Virtual machine under pve1 |
grafana-vm2 | 192.168.10.22 | Virtual machine under pve1 |
proxy-lan | 192.168.10.5 | Runs Nginx |
proxy-dmz | 192.168.10.6 | Runs 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-lanandtraefik-publicare 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.
- Open Inventory → Applications and choose Create application.
- Fill in:
- Runs on: Device, then
grafana-vm. - Name:
Grafana. - Protocol:
http. - Port:
3000, with TCP.
- Runs on: Device, then
- Choose Create.
- 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
- Open Inventory → Services and choose Create service.
- Fill in:
- Name:
Grafana. - Protocol:
http. - Category:
monitoring. - Main address: A gateway route, because people use the proxy hostnames, not the VM addresses.
- Name:
- Under Served by, choose Add application and pick
grafana-vm - Grafana:3000/TCP. The first one becomes primary. - Choose Add application again and pick
grafana-vm2 - Grafana:3000/TCP. Set its Role toreplica. - 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:
- Open Inventory → Gateways and choose Create gateway.
- Name
nginx-lan, and under runs on addproxy-lan. Create it. - Do the same for
traefik-publiconproxy-dmz.
Then add the routes. Edit nginx-lan and, under routes to, choose Add route for each:
| Host | Path | Service | Priority |
|---|---|---|---|
grafana.home.lan | (blank) | Grafana | 100 |
grafana.home.lan | /api | Grafana | 100 |
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:
| Host | Path | Service | Priority |
|---|---|---|---|
grafana.example.com | (blank) | Grafana | 100 |
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
-
Open Templates & checks → Templates, choose New template, and pick Nginx reverse proxy.
-
Near the top of the source, find the line marked
EDIT THISand 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:
# 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' %}
# 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.