Conclusion

The monitoring layer for the local dev platform is centralized on the Storage Server (Mac mini, Ubuntu 24.04), which runs 24/7. Metrics collection and log aggregation continue on the Storage Server alone even when Compute Server and Desktop PC are powered off.

Service management evolved from the initial compose + systemd template approach to Quadlet (.container → systemd auto-generation), with rootful and rootless scopes separated by access requirements.

Collection TargetPathStorage
Host metrics (3 hosts)node_exporter → PrometheusStorage Server
Disk health (2 hosts)smartctl_exporter → PrometheusStorage Server
RouterOS metricsMKTXP (API) → PrometheusStorage Server
MinIO metrics/minio/v2/metrics/cluster → PrometheusStorage Server
RouterOS / Netgear syslogUDP 1514 (rfc3164) → Promtail → LokiStorage Server
Linux logsjournal / files → Promtail → LokiStorage Server
VisualizationGrafana (Desktop PC) → Prometheus + LokiDesktop PC

Hardware and Storage

ItemDetails
ChassisMac mini late 2018 (x86)
CPUCore i3
RAMDDR4 SO-DIMM 8GB
StorageSSD 2TB (OS, LVM), ext M.2 SSD 4TB (LUKS → /mnt/data)
NIC1GbE + ext 10GbE (RTL8159, 10.10.10.3)
  nvme0n1 (1.8T)  → LVM → / (100G)
nvme1n1 (3.6T)  → LUKS → /mnt/data (archive, backups, object storage)
  

OS is Ubuntu 24.04.3 LTS. NTP and DNS point to the router, timezone is UTC.


Quadlet Service Layout

The initial deployment used compose files with a [email protected] systemd template, with dedicated users (prometheus, loki, promtail) for rootless operation. The current setup uses Quadlet with rootful/rootless scope separation.

  /opt/containers/systemd/
├── rootful/
│   ├── node-exporter.container
│   ├── promtail.container
│   └── smartctl-exporter.container
└── rootless/
    ├── loki.container
    ├── minio.container
    ├── mktxp.container
    └── prometheus.container
  

Rootful / Rootless Split Criteria

ScopeServicesReason
rootfulnode-exporter, promtail, smartctlNeed access to host /, /var/log/journal, or raw disk devices
rootlessprometheus, loki, mktxp, minioNetwork-only data collection and storage. No direct host device access needed

node_exporter requires Network=host and Volume=/:/host:ro,rslave for full host metrics. Promtail needs /var/log/journal access and UDP 1514 listening. smartctl_exporter needs raw disk device access.

For Quadlet verification details and UID mapping, see Building a Container Platform with Rootless Podman and Quadlet.


Prometheus Configuration

Seven jobs covering 3 hosts, RouterOS, and MinIO.

  global:
  scrape_interval: 30s
  evaluation_interval: 30s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets:
          - storage.home.arpa:9090

  - job_name: node-exporter
    static_configs:
      - targets:
          - storage.home.arpa:9100
          - desktop.home.arpa:9100
          - compute.home.arpa:9100

  - job_name: loki
    static_configs:
      - targets:
          - storage.home.arpa:3100

  - job_name: promtail
    static_configs:
      - targets:
          - storage.home.arpa:9080

  - job_name: smartctl
    scrape_interval: 5m
    static_configs:
      - targets:
          - storage.home.arpa:9633
          - compute.home.arpa:9633

  - job_name: mikrotik
    static_configs:
      - targets:
          - storage.home.arpa:49090

  - job_name: minio
    metrics_path: /minio/v2/metrics/cluster
    static_configs:
      - targets:
          - storage.home.arpa:9000
  

Port Map

PortServiceHost(s)
9090Prometheusstorage
9100node_exporterstorage, desktop, compute
3100Lokistorage
9080Promtailstorage
9633smartctl_exporterstorage, compute
49090MKTXP (RouterOS)storage
9000MinIOstorage

node_exporter Across 3 Hosts

Storage Server and Compute Server run node_exporter via Quadlet (rootful). Desktop PC (Mac) uses Homebrew with an explicit listen address change for remote scraping.

  # Mac (Homebrew)
brew install node_exporter
sudo brew services start node_exporter
# Change listen address for external scraping
sudo sed -i '' 's/--web.listen-address=127\.0\.0\.1:9100/--web.listen-address=0.0.0.0:9100/' \
  /Library/LaunchDaemons/homebrew.mxcl.node_exporter.plist
  

Promtail Configuration (Log Collection)

Promtail runs as a rootful Quadlet service and collects both Linux logs and network device syslog, pushing everything to Loki.

  server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /promtail/positions.yaml

clients:
  - url: http://storage.home.arpa:3100/loki/api/v1/push

scrape_configs:
  # --- Linux file logs ---
  - job_name: security-files
    static_configs:
      - labels:
          job: security
          host: storage.home.arpa
          __path__: /var/log/alternatives.log*
      - labels:
          job: security
          host: storage.home.arpa
          __path__: /var/log/apport.log*

  - job_name: apt-files
    static_configs:
      - labels:
          job: apt
          host: storage.home.arpa
          __path__: /var/log/apt/history.log
      - labels:
          job: apt
          host: storage.home.arpa
          __path__: /var/log/apt/term.log
      - labels:
          job: apt
          host: storage.home.arpa
          __path__: /var/log/dpkg.log

  # --- systemd journal ---
  - job_name: kernel-journal
    journal:
      path: /var/log/journal
      max_age: 168h
      labels:
        job: kernel
        host: storage.home.arpa
    relabel_configs:
      - source_labels: ["__journal__hostname"]
        target_label: host
      - source_labels: ["__journal__syslog_identifier"]
        target_label: ident
      - source_labels: ["__journal_priority"]
        target_label: priority

  - job_name: systemd-units
    journal:
      path: /var/log/journal
      max_age: 168h
      labels:
        job: systemd
        host: storage.home.arpa
    relabel_configs:
      - source_labels: ["__journal__systemd_unit"]
        target_label: unit
      - source_labels: ["__journal__hostname"]
        target_label: host
      - source_labels: ["__journal__uid"]
        target_label: uid

  # --- Network device syslog (RouterOS / Netgear WiFi) ---
  - job_name: network-syslog
    syslog:
      listen_address: 0.0.0.0:1514
      listen_protocol: udp
      syslog_format: rfc3164
      use_incoming_timestamp: false
      label_structured_data: true
    relabel_configs:
      - source_labels: ["__syslog_message_hostname"]
        target_label: host
      - source_labels: ["__syslog_message_app_name"]
        target_label: app
      - source_labels: ["__syslog_severity"]
        target_label: severity
      - source_labels: ["__syslog_facility"]
        target_label: facility
  

Network Device Syslog Reception

RouterOS and Netgear WiFi AP send syslog over UDP 1514 in rfc3164 format.

RouterOS configuration:

  /system logging action add name=promtail target=remote remote=10.10.10.3 remote-port=1514 bsd-syslog=yes
/system logging add topics=firewall action=promtail
/system logging add topics=system,info action=promtail
  

Bulk firewall logging:

  /ip firewall filter set [find builtin=no && dynamic=no] log=yes log-prefix="FW: "
  

MKTXP: RouterOS API Metrics

RouterOS metrics use MKTXP Exporter (API-based) instead of SNMP. The API path is simpler to validate and avoids SNMP Exporter network-mode complications.

  [edge.home.arpa]
hostname = edge.home.arpa
username = metrics
password = (API user password)
port = 8728
use_ssl = False
verify_ssl = False
timeout = 5
allow_duplicates = False
  

Metrics are exposed at http://storage.home.arpa:49090/metrics — CPU, memory, temperature, interface stats, and DHCP data. Grafana visualizes these using the “Mikrotik MKTXP Exporter” dashboard.


Grafana Integration (Desktop PC)

Visualization runs on the Desktop PC (Mac Studio) via Grafana, pointing to the Storage Server’s data sources. This separates collection/storage from visualization.

Data Sources

  • Prometheus → http://storage.home.arpa:9090
  • Loki → http://storage.home.arpa:3100

RouterOS Log Table Panel

ItemSetting
Data sourceLoki
Query{job="network-syslog"}
TransformationsExtract fields → Organize fields
Columns keptTime, host, app, severity, line

LogQL query example:

  {job="network-syslog"} | line_format "{{.time}} | {{.host}} | {{.severity}} | {{.line}}"
  

Having metrics and logs in the same Grafana makes it possible to correlate a RouterOS load spike with firewall events at the same timestamp.


Vector Migration (In Progress)

Promtail reached EOL in March 2026, so migration to Vector (Datadog Telemetry) is underway. Vector is Rust-based and handles log collection, metric transformation, and routing in a single binary.

Within the Go + NATS + Dagster AI orchestration platform, the TELEMETRY stream (telemetry.>) is already designed to be consumed by Vector and forwarded to Prometheus and Loki. Promtail’s responsibilities will transfer directly to Vector.


Caveats

  • Verify CRS304 L3 / filter rules do not block monitoring ports (9100, 9633, etc.)
  • Mac’s node_exporter defaults to localhost bind — plist modification needed for external scraping
  • Restrict monitoring ports to internal segments only
  • smartctl_exporter at 5-minute intervals is sufficient; over-scraping adds disk load
  • Promtail’s journal collection requires /var/log/journal access, which necessitates rootful Quadlet