Skip to content

Commit 48e3c8c

Browse files
committed
docs: update HAProxy configuration using conf.d architecture
1 parent 4615c08 commit 48e3c8c

14 files changed

Lines changed: 95 additions & 121 deletions

.github/instructions/ansible-role-testing.instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ applyTo: "tests/**/*.yml,tests/**/*.sh,roles/**/tasks/*.yml,roles/**/defaults/*.
2121
- `tests/ai_horde/test_ai_horde_render.yml`: Validates Docker mode rendering, security-sensitive compose/env contracts, and fail-fast password/key validation.
2222
- `tests/ai_horde/test_ai_horde_docker_build_toggle.yml`: Canonical owner of the build-on-start toggle contract (both enabled and disabled cases).
2323
- `tests/ai_horde/test_ai_horde_native_render.yml`: Validates native systemd template rendering, multi-instance output, and expected file permissions.
24-
- `tests/ai_horde/test_ai_horde_native_logic.yml`: Validates native HAProxy and instance management logic (stale unit handling, safe edits, ownership normalization gates).
24+
- `tests/ai_horde/test_ai_horde_native_logic.yml`: Validates native HAProxy conf.d architecture and instance management logic (stale unit handling, ownership normalization gates).
2525
- `tests/ai_horde/test_ai_horde_policy_contracts.yml`: Enforces reproducibility and policy contracts (pinned refs/digests, installer verification, readiness semantics).
2626
- `tests/artbot/test_artbot_render.yml`: Validates Artbot rendering and HAProxy/certbot contract output.
2727
- `tests/frontpage/test_frontpage_render.yml`: Validates Frontpage Docker-mode render contracts, env keys, healthcheck wiring, ports, and log rotation.

.github/instructions/repository-navigability.instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ find the right folder before making changes.
4646
| Role path | Purpose |
4747
| --------- | ------- |
4848
| `roles/_uv_bootstrap/` | Shared micro-role: download, verify, and install a pinned uv version |
49+
| `roles/_haproxy_confd_bootstrap/` | Shared micro-role: install HAProxy, create conf.d directory, write systemd override for conf.d loading |
4950
| `roles/geerlingguy.swap/` | Third-party swap configuration role used as an infrastructure dependency |
5051

5152
## Meta folders to know first

docs/ai-horde-haproxy-topology.md

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
# HAProxy Topology Guidance
22

3-
This document covers HAProxy mode selection and topology decisions for the
4-
`ai_horde` role. For variable reference and basic usage, see
3+
This document covers HAProxy topology decisions for the `ai_horde` role.
4+
For variable reference and basic usage, see
55
[roles/ai_horde/README.md](../roles/ai_horde/README.md).
66

7-
## Mode Selection
7+
## conf.d Architecture
88

9-
- **Shared hosts** with existing HAProxy config: use `safe_edit` and a
10-
non-privileged ingress port (for example `8080`).
11-
- **Dedicated ingress hosts**: `standalone` can be appropriate, including
12-
privileged ingress ports.
9+
HAProxy configuration is managed via **conf.d drop-in fragments**. When
10+
`ai_horde_install_haproxy: true`, the role:
11+
12+
1. Includes the shared `_haproxy_confd_bootstrap` micro-role to install
13+
HAProxy, create `/etc/haproxy/conf.d/`, and write a systemd override
14+
that loads all fragments from `conf.d/` alongside the main config.
15+
2. Templates the AI-Horde frontend/backend block to
16+
`/etc/haproxy/conf.d/ai_horde.cfg`.
17+
3. Restarts HAProxy when the fragment changes.
18+
19+
This approach is safe on shared hosts — each role drops its own fragment
20+
into `conf.d/` without touching unrelated configuration.
21+
22+
## Topology Guidance
23+
24+
- **Shared hosts** with existing HAProxy config: use
25+
`ai_horde_install_haproxy: true` with a non-privileged ingress port
26+
(for example `8080`). The conf.d fragment coexists with other services.
27+
- **Dedicated ingress hosts**: the same conf.d approach works; use
28+
privileged ports if appropriate.
1329
- **Managed reverse proxy environments**: prefer upstream ingress and keep
14-
`ai_horde_install_haproxy=false` unless local HAProxy is explicitly needed.
30+
`ai_horde_install_haproxy: false` unless local HAProxy is explicitly needed.
1531

1632
## Privileged Binding
1733

@@ -25,7 +41,6 @@ Copy/paste baseline for running AI-Horde behind an upstream reverse proxy:
2541

2642
```yaml
2743
ai_horde_install_haproxy: true
28-
ai_horde_haproxy_mode: safe_edit
2944
ai_horde_haproxy_port: 8080
3045
ai_horde_listen: "127.0.0.1"
3146
```
@@ -35,9 +50,9 @@ traffic to AI-Horde on `:8080`.
3550

3651
## Operator Decision Matrix
3752

38-
| Topology | Recommended HAProxy mode | Baseline ingress port | Notes |
53+
| Topology | HAProxy setup | Baseline ingress port | Notes |
3954
| ---- | ---- | ---- | ---- |
40-
| Single-host lab | `safe_edit` | `8080` | Lowest-conflict local baseline; easy to place another proxy in front later. |
41-
| Shared host with existing HAProxy | `safe_edit` | `8080` | Preserves unrelated HAProxy configuration via marker-bounded edits. |
42-
| Dedicated ingress host | `standalone` | `80` (and/or `443` via external TLS strategy) | Full config ownership is acceptable when no other stack owns HAProxy. |
43-
| Managed reverse proxy upstream | `disabled` (`ai_horde_install_haproxy=false`) | Upstream-owned | Keep AI-Horde behind upstream ingress; avoid duplicate proxy ownership. |
55+
| Single-host lab | `install_haproxy: true` | `8080` | Lowest-conflict local baseline; easy to place another proxy in front later. |
56+
| Shared host with existing HAProxy | `install_haproxy: true` | `8080` | conf.d fragment coexists with other service configs. |
57+
| Dedicated ingress host | `install_haproxy: true` | `80` (and/or `443` via external TLS strategy) | Full config ownership is acceptable when no other stack owns HAProxy. |
58+
| Managed reverse proxy upstream | `install_haproxy: false` | Upstream-owned | Keep AI-Horde behind upstream ingress; avoid duplicate proxy ownership. |

docs/monitoring/BACKUP.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ journalctl -u mimir-backup --since "24 hours ago" --no-pager
139139

140140
(1500 minutes ≈ 25 hours, appropriate for a daily schedule)
141141

142-
3. **node_exporter systemd collector:** If node_exporter is configured
143-
with `--collector.systemd`, it exposes
142+
3. **Host metrics systemd collector:** If host metrics collection is configured
143+
with a systemd collector (Alloy `prometheus.exporter.unix` or legacy
144+
node_exporter with `--collector.systemd`), it exposes
144145
`node_systemd_unit_state{name="mimir-backup.service",state="failed"}`.
145146
An alert on this metric provides Prometheus-native backup monitoring.
146147

docs/monitoring/OBSERVABILITY.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ For deployment order and baseline setup, see [MONITORING.md](../../MONITORING.md
4949
| `horde_monitoring` | Mimir, S3-compatible storage, Memcached, Grafana, Loki by default, optional Tempo/Pyroscope, Grafana datasource provisioning, monitoring alert rules, optional HAProxy backend insertion |
5050
| `horde_stats_exporter` | `horde-exporter` systemd service and optional downsampling timer |
5151
| `horde_alloy` | App-host telemetry collection and forwarding (metrics/logs/traces) |
52-
| `prometheus.prometheus.*` (playbook) | Prometheus, Alertmanager, and optional node_exporter (not managed by `horde_monitoring`) |
52+
| `prometheus.prometheus.*` (playbook) | Prometheus, Alertmanager (not managed by `horde_monitoring`; configured at the playbook level) |
5353

5454
## Full Stack Components
5555

@@ -192,15 +192,19 @@ Complete example: [examples/alloy_app_host.yml](../../examples/alloy_app_host.ym
192192

193193
### Interaction With node_exporter Deployment
194194

195-
In [examples/horde_monitoring_stack.yml](../../examples/horde_monitoring_stack.yml),
196-
node_exporter installation is decided per host using
197-
`horde_host_metrics_source`:
195+
**Grafana Alloy is the preferred host metrics source.** It collects host
196+
metrics natively via `prometheus.exporter.unix` and pushes them to Mimir,
197+
eliminating the need for a separate node_exporter binary and HAProxy
198+
basic-auth fronting.
199+
200+
For legacy deployments that still run node_exporter, the monitoring stack
201+
example supports per-host selection via `horde_host_metrics_source`:
198202

199203
- `auto` (recommended): skip node_exporter when the host has
200204
`horde_alloy_enabled: true` or `horde_alloy_collect_metrics: true`;
201205
otherwise install node_exporter.
202-
- `node_exporter`: force install on all hosts.
203-
- `alloy`: force skip on all hosts.
206+
- `node_exporter`: force install on all hosts (legacy).
207+
- `alloy`: force skip on all hosts (new deployments).
204208

205209
### Alloy Pipeline Toggles
206210

@@ -217,7 +221,7 @@ Role validation fails fast if enabled pipelines do not have endpoints, or if
217221
## End-To-End Data Flows
218222

219223
- Exporter metrics: `horde-exporter` -> Prometheus scrape -> Mimir
220-
- Host metrics: node_exporter -> Prometheus scrape or Alloy `prometheus.exporter.unix` -> Mimir remote_write
224+
- Host metrics: Alloy `prometheus.exporter.unix` -> Mimir remote_write (preferred), or node_exporter -> Prometheus scrape (legacy)
221225
- Logs: Alloy journal/file/docker sources -> Loki push API
222226
- Traces: app OTLP -> Alloy OTLP receiver -> Tempo OTLP HTTP exporter
223227
- Trace-derived metrics: Tempo metrics generator -> Mimir remote_write

examples/ai_horde.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
# ai_horde_listen: "127.0.0.1"
5353
# ai_horde_admins: ["admin_user#1"]
5454
# ai_horde_install_haproxy: true
55-
# ai_horde_haproxy_mode: safe_edit
5655
# ai_horde_haproxy_port: 8080
5756

5857
# Production ingress variant (80/443) for dedicated ingress hosts only:

examples/alloy_app_host.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@
3131
horde_alloy_tenant_id: "infrastructure"
3232

3333
# --- Host metrics source ---
34-
# "node_exporter" keeps host metrics in Prometheus scrape path.
35-
# "alloy" enables host metrics from Alloy prometheus.exporter.unix.
34+
# "alloy" enables host metrics from Alloy prometheus.exporter.unix
35+
# (preferred — no separate node_exporter binary needed).
36+
# "node_exporter" keeps host metrics in Prometheus scrape path (legacy).
3637
# When using examples/horde_monitoring_stack.yml with
3738
# horde_host_metrics_source: auto, set horde_alloy_enabled: true
3839
# in inventory for these hosts so node_exporter is skipped implicitly.
39-
horde_host_metrics_source: "node_exporter"
40+
horde_host_metrics_source: "alloy"
4041

4142
# --- Authentication ---
4243
horde_alloy_basic_auth_username: "alloy"

examples/full_horde_stack.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,8 @@
5252
ai_horde_backend_host: "127.0.0.1"
5353
ai_horde_admins: ["admin_user#1"]
5454
ai_horde_install_haproxy: true
55-
ai_horde_haproxy_mode: safe_edit
5655
ai_horde_haproxy_port: "{{ horde_api_ingress_port }}"
5756
# Production ingress override for dedicated ingress hosts:
58-
# ai_horde_haproxy_mode: standalone
5957
# ai_horde_haproxy_port: 80
6058
# Preconditions:
6159
# - No conflicting listeners on 80/443.

examples/horde_monitoring_stack.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
# Usage: ansible-playbook -i inventory.yml horde_monitoring_stack.yml
44
#
55
# Architecture:
6-
# Play 1: Deploy node_exporter only on hosts that resolve to node_exporter source
7-
# Play 2: Deploy Mimir + Grafana + Prometheus + stats exporter on the monitoring host
6+
# Deploys Mimir + Grafana + Prometheus + stats exporter on the monitoring host.
7+
# Host metrics are collected by Grafana Alloy (preferred) or legacy
8+
# node_exporter. See examples/alloy_app_host.yml for Alloy deployment.
89
#
910
# Prometheus (native) --remote_write--> Mimir (Docker) <-- Grafana (Docker)
1011
# All services bind to 127.0.0.1. HAProxy fronts Mimir externally with basic_auth.
1112
#
1213
# Prerequisites:
1314
# ansible-galaxy collection install -r requirements.yml
14-
# HAProxy is installed on each monitored host to front node_exporter
15-
# with basic auth. node_exporter listens on localhost only.
15+
# For legacy node_exporter hosts: HAProxy is installed on each monitored host
16+
# to front node_exporter with basic auth. node_exporter listens on localhost only.
1617

1718
# ---------------------------------------------------------------------------
1819
# Play 1: node_exporter on hosts using node_exporter as host metrics source

examples/inventory_monitoring.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ all:
5151
ansible_user: admin
5252
ansible_become: true
5353
# Optional: override the address Prometheus should use for
54-
# node_exporter scraping if ansible_host is not routable from
55-
# the monitoring host.
54+
# node_exporter scraping (legacy) if ansible_host is not routable
55+
# from the monitoring host.
5656
# horde_node_exporter_scrape_address: "10.0.0.101"
57-
# If this host is managed by examples/alloy_app_host.yml for host metrics,
58-
# set horde_alloy_enabled: true so monitoring stack auto mode skips
57+
# If this host uses Alloy for host metrics (preferred), set
58+
# horde_alloy_enabled: true so monitoring stack auto mode skips
5959
# node_exporter on this host.
6060
# horde_alloy_enabled: true
6161

0 commit comments

Comments
 (0)