Skip to content

sholdee/adguard-exporter

Repository files navigation

AdGuard Exporter

This exporter is primarily intended to run as a sidecar container for AdGuard Home in Kubernetes. It enables metrics visibility across multiple replica instances by mounting AdGuard Home's work directory and reading lines from querylog.json.

The exporter is written in Go and uses a distroless image running as nonroot.

AdGuard Home Grafana dashboard

Images And Releases

Images are published to Docker Hub and GHCR:

sholdee/adguardexporter
ghcr.io/sholdee/adguard-exporter

Published images support linux/amd64 and linux/arm64. Releases publish these tags:

vX.Y.Z
X.Y.Z
X.Y
latest

The latest tag points to the latest release, not the latest commit on master.

Available Metrics

agh_dns_queries_total: Total number of DNS queries
agh_blocked_dns_queries_total: Total number of AdGuard blocked queries
agh_dns_query_types_total: DNS query types and respective counts
agh_dns_query_hosts_total: Top 100 DNS query hosts
agh_blocked_dns_query_hosts_total: Top 100 blocked DNS query hosts
agh_safe_search_enforced_hosts_total: Safe search enforced hosts
agh_dns_filtering_reason_total: DNS query filtering reasons
agh_dns_filtering_reason_hosts_total: Top 100 filtered hosts by reason
agh_querylog_entries_skipped_total: Query log entries skipped by reason
agh_dns_average_response_time: Average response time of all queries in ms
agh_dns_average_upstream_response_time: Query processing time by upstream in ms

The upstream timing metric keeps its historical name, but it is based on querylog Elapsed grouped by Upstream; AdGuard Home querylog records do not include pure upstream round-trip duration.

Grafana Dashboard

The dashboard JSON is versioned in dashboards/adguard-exporter.v2.json. It uses Grafana's V2 JSON model, range-based counter queries for dashboard totals, filtering reason and skipped querylog record metrics, and upstream timing labeled as query processing time rather than pure upstream round-trip duration.

How To Use This Container

Configure AdGuard Home to dump query logs to disk at a regular interval by using a low size_memory setting. This example causes queries to be logged every five lines.

secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: adguard-secret
  namespace: adguard
type: Opaque
stringData:
  AdGuardHome.yaml: |
    # ... [earlier configuration omitted]

    querylog:
      dir_path: ""
      ignored:
        - localhost
      interval: 24h
      size_memory: 5
      enabled: true
      file_enabled: true

    # ... [remaining configuration omitted]

Add the sholdee/adguardexporter sidecar container to your existing AdGuard deployment manifest.

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: &app adguard
  namespace: *app
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: *app
  template:
    metadata:
      labels:
        app: *app
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 65532
        runAsGroup: 65532
        fsGroup: 65532
        seccompProfile:
          type: RuntimeDefault
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: *app
      initContainers:
      - name: adguard-init
        image: busybox:1.36.1
        securityContext:
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 65532
          runAsGroup: 65532
          allowPrivilegeEscalation: false
        imagePullPolicy: IfNotPresent
        command:
          - sh
          - -c
          - |
            cp /home/AdGuardHome.yaml /config/AdGuardHome.yaml
            chmod 644 /config/AdGuardHome.yaml
        volumeMounts:
          - mountPath: /home
            name: adguard-secret
          - mountPath: /config
            name: adguard-conf
      containers:
      - name: adguard-home
        image: adguard/adguardhome:v0.107.52
        securityContext:
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 65532
          runAsGroup: 65532
          allowPrivilegeEscalation: false
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dnstcp
          protocol: TCP
        - containerPort: 3000
          name: http-initial
          protocol: TCP
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - name: adguard-data
          mountPath: /opt/adguardhome/work
        - name: adguard-conf
          mountPath: /opt/adguardhome/conf
        resources:
          requests:
            memory: 150Mi
            cpu: "15m"
          limits:
            memory: 400Mi
        livenessProbe: &probe
          exec:
            command:
            - /bin/sh
            - -c
            - nslookup localhost 127.0.0.1
        readinessProbe: *probe
      - name: adguard-exporter
        image: ghcr.io/sholdee/adguard-exporter:v2.0.2
        securityContext:
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 65532
          runAsGroup: 65532
          allowPrivilegeEscalation: false
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8000
          name: metrics
          protocol: TCP
        volumeMounts:
        - name: adguard-data
          mountPath: /opt/adguardhome/work
        livenessProbe:
          httpGet:
            path: /livez
            port: metrics
        readinessProbe:
          httpGet:
            path: /readyz
            port: metrics
      volumes:
      - emptyDir: {}
        name: adguard-data
      - emptyDir: {}
        name: adguard-conf
      - name: adguard-secret
        secret:
          secretName: adguard-secret

Add the metrics port to the Service definition.

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: adguard-http
  namespace: adguard
  labels:
    app: adguard
spec:
  selector:
    app: adguard
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    name: http
  - port: 8000
    protocol: TCP
    targetPort: 8000
    name: metrics
  type: ClusterIP

Create a ServiceMonitor for Prometheus to start scraping metrics.

servicemonitor.yaml

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: adguard-metrics
  namespace: adguard
  labels:
    app: adguard
spec:
  selector:
    matchLabels:
      app: adguard
  namespaceSelector:
    matchNames:
    - adguard
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

About

Export querylog metrics from adguard

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors