Files
ntbops-ansible/docs/topology.md
T

3.4 KiB

Topology

Every VM is a Docker host. Proxmox and Cloud-Init only create the machine and bootstrap Ansible; service placement is controlled by inventory.

Edge VM

The edge VM runs:

  • Docker
  • Traefik
  • authentik IAM

It publishes Traefik entrypoints on host ports 80 and 443. authentik can run behind the same local Traefik instance by setting:

nbops_service_routing_mode: traefik
authentik_domain: auth.example.invalid

Downstream VMs

Downstream VMs also run Docker and normally run an internal Traefik instance. Application containers route through that local Traefik using Docker labels and the shared Docker proxy network.

nbops_roles:
  - base
  - ssh
  - ufw
  - updates
  - docker
  - traefik
  - gitea
  - immich

nbops_service_routing_mode: traefik
nbops_traefik_entrypoint: web
nbops_traefik_tls: false

In this mode application roles do not publish their HTTP ports. They render Traefik labels and join nbops_proxy_network, which defaults to proxy.

Only Traefik entrypoints should be exposed on the downstream host firewall. Use traefik_bind_address to bind those entrypoints to the VM's internal address.

For central edge routing, the edge Traefik uses the file provider to route machine-to-machine traffic to each downstream VM's internal Traefik entrypoint. The internal Traefik then routes to applications using Docker labels. This keeps container discovery local to the VM and gives the edge-to-internal leg a stable place for future mTLS.

Edge VM:

nbops_roles:
  - base
  - ssh
  - ufw
  - updates
  - docker
  - traefik

traefik_docker_provider_enabled: false
traefik_file_provider_enabled: true
traefik_dynamic_inventory_group: nbops

Downstream VM:

nbops_service_routing_mode: traefik
nbops_traefik_entrypoint: web
nbops_traefik_tls: false

traefik_bind_address: "10.0.10.25"
traefik_publish_entrypoints: true

nbops_traefik_file_routes:
  - name: jellyfin
    domain: jellyfin.example.invalid
    scheme: http
    port: 80
  - name: gitea
    domain: git.example.invalid
    scheme: http
    port: 80

When the play runs against the edge VM, Traefik renders dynamic/http-services.yml from nbops_traefik_file_routes declared across the inventory group. Each route targets ansible_host for that inventory host by default. Set target_host for a different VM address, or set url to provide the complete upstream URL.

For future mTLS between edge Traefik and internal Traefik, routes can reference a Traefik serversTransport:

nbops_traefik_file_routes:
  - name: jellyfin
    domain: jellyfin.example.invalid
    scheme: https
    port: 443
    servers_transport: internal-mtls

traefik_dynamic_servers_transports:
  - name: internal-mtls
    server_name: apps-01.example.internal
    root_cas:
      - /etc/pki/internal/certs/ca.crt
    certificates:
      - cert_file: /etc/pki/internal/certs/edge.crt
        key_file: /etc/pki/internal/private/edge.key

Routing Modes

Application roles support:

  • host: publish application ports directly and open those ports in UFW.
  • traefik: do not publish HTTP ports; attach to Traefik and render labels.
  • traefik_file: publish a service port without labels. This is available for exceptional direct edge-to-app routes, but the preferred multi-VM topology is edge Traefik -> internal Traefik -> application.

Gitea is special: HTTP can be routed through Traefik while SSH remains published with gitea_publish_ssh: true.