# ๐Ÿ“ˆ Observability > [!NOTE] > Every fleet host runs the same collection agent natively (not as a container), pushing logs and metrics outward rather than waiting to be scraped โ€” a deliberate choice that keeps monitoring off the firewall's critical path. | | | |---|---| | **Agent** | Native systemd service on every host, not Docker | | **Logs** | journald โ†’ Loki | | **Metrics** | Push-based `remote_write`, not pull-based scraping | | **Coverage** | All fleet hosts, confirmed live | ## Pipeline ```mermaid flowchart LR subgraph Sources["Per-host sources"] J["journald
persistent, 14-day retention"] D["Docker container
stdout/stderr"] N["node metrics
CPU/mem/disk/net"] App["Native app metrics
5 apps expose endpoints"] end Agent["Collection Agent
native systemd service"] J --> Agent D -->|docker_hosts only| Agent N --> Agent App --> Agent Agent -->|logs| Loki["Loki"] Agent -->|remote_write, 30s| Prom["Prometheus"] ``` ## Agent install: deliberate, not default > [!TIP] > The agent binary is downloaded directly from upstream releases and run under a hand-written systemd unit โ€” not installed from a package repo or the vendor's own packaged service file. Before install, a preflight check fails the run if the host doesn't run systemd, or if the root filesystem has too little free space for journald's own headroom, and configures/waits for time-sync confirmation (skipped on container-based hosts, where the clock can't be locally adjusted anyway). ## Logs: journald only, no double-handling The system's built-in syslog daemon is explicitly disabled once journald takes over โ€” avoids the same log stream getting handled twice. journald itself is switched from the default in-memory ring buffer to persistent on-disk storage, sized larger on the one host with much higher container-log volume. ```mermaid flowchart LR A["journald
system logs"] --> L1["Loki
job=system"] B["Docker API
container logs
survives recreation"] --> L2["Loki
job=docker"] C["DNS query log
daily rotation"] --> L3["Loki
job=dns ยท real event time"] ``` Every host's logs land in the same log store under its own host label, filterable in the dashboard by host or job. Docker logs are scraped via the container runtime's own API (not by tailing log files on disk) specifically so log collection survives a container being recreated. The DNS query log pipeline is kept deliberately separate from general system/container error tracking, with its timestamps extracted from the log line itself rather than ingestion time. ## Metrics: pushed, not pulled > [!IMPORTANT] > **Why push instead of pull**: Prometheus doesn't need network access to every host โ€” only the monitoring host needs to accept inbound writes. This matters directly for hosts sitting behind the tighter firewall rules described in [`firewall-and-network.md`](firewall-and-network.md) โ€” a pull model would mean punching a scrape-target hole through that segmentation for every single host. | App | What's exposed | |---|---| | Secrets platform | Dedicated read-only metrics token, separate from its normal agent token | | Orchestration engine | Built-in metrics endpoint | | Git server | Built-in metrics endpoint | | Workflow automation | Built-in metrics endpoint | | Identity provider | Built-in metrics endpoint, localhost-only | Five apps expose their own metrics endpoints directly, scraped by that host's own agent and pushed through the same path as host-level metrics โ€” no separate pipeline needed for app-level visibility. --- Related stacks: [`stacks/platform/grafana-stack/`](../stacks/platform/grafana-stack/)