Skip to content

AdGuard Home

Deploy AdGuard Home on Kubernetes — a network-wide DNS-level ad and tracker blocker. Supports DNS over HTTPS, DNS over TLS, custom rewrite rules, client-level settings, and optional multi-instance synchronization.

Two deployment modes: wizard (default) vs pre-configured — each uses a different web UI port
  • Wizard mode (config.adGuardHome: {}): AdGuard Home starts on port 3000 for the initial setup wizard. After completing setup, the UI moves to port 80.
  • Pre-configured mode (config.adGuardHome populated): Setup wizard is skipped entirely. The UI starts directly on port 80, ready for production use.

Choose pre-configured mode for GitOps or reproducible deployments. Wizard mode is suitable for first-time manual setup.

Key Features

  • Two deployment modes — interactive wizard or full pre-configured via values
  • Dual PVC — separate conf (config + TLS certs) and work (query log + stats + filters) volumes
  • Dedicated DNS service — separate LoadBalancer for UDP/TCP port 53
  • adguardhome-sync — multi-instance config synchronization (primary → replicas)
  • Full-volume backuptar archives both PVCs to S3 (no database involved)

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install adguard-home helmforge/adguard-home -f values.yaml

OCI registry:

helm install adguard-home oci://ghcr.io/helmforgedev/helm/adguard-home -f values.yaml

Deployment Examples

# values.yaml — AdGuard Home in wizard mode (default)
# Web UI starts on port 3000 for first-time setup
# After wizard completion, UI moves to port 80

service:
  dns:
    type: LoadBalancer
    loadBalancerIP: '192.168.1.53' # static IP for DNS (optional, MetalLB/cloud LB)

persistence:
  conf:
    enabled: true
    size: 256Mi
  work:
    enabled: true
    size: 5Gi # query log + filter lists + statistics

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: adguard.example.com
      paths:
        - path: /
          pathType: Prefix
# Access wizard for first-time setup:
kubectl port-forward svc/<release>-adguard-home-web 3000:80
# Visit http://localhost:3000
# values.yaml — AdGuard Home pre-configured (wizard skipped, UI on port 80)
config:
  adGuardHome:
    http:
      address: 0.0.0.0:80
      session_ttl: 720h
    users:
      - name: admin
        # Generate: htpasswd -bnBC 10 "" 'mypassword' | cut -d: -f2
        password: '$2y$10$...'
    dns:
      bind_hosts:
        - 0.0.0.0
      port: 53
      upstream_dns:
        - 'https://dns.cloudflare.com/dns-query'
        - 'https://dns.google/dns-query'
      bootstrap_dns:
        - 1.1.1.1
        - 8.8.8.8
      protection_enabled: true
      filtering_enabled: true
      cache_size: 4194304
    filters:
      - enabled: true
        url: 'https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt'
        name: AdGuard DNS filter
        id: 1
      - enabled: true
        url: 'https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt'
        name: AdAway Default Blocklist
        id: 2
    querylog:
      enabled: true
      interval: 720h
    statistics:
      enabled: true
      interval: 168h
    schema_version: 29

service:
  dns:
    type: LoadBalancer
    loadBalancerIP: '192.168.1.53'

persistence:
  conf:
    enabled: true
    size: 256Mi
  work:
    enabled: true
    size: 5Gi

ingress:
  enabled: true
  ingressClassName: traefik
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: adguard.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: adguard-home-tls
      hosts:
        - adguard.example.com
# values.yaml — Primary instance with adguardhome-sync to replica
# Deploy a second instance as the replica (separate helm release)
config:
  adGuardHome:
    http:
      address: 0.0.0.0:80
    users:
      - name: admin
        password: '$2y$10$...'
    schema_version: 29

sync:
  enabled: true
  origin:
    url: 'http://adguard-home.adguard.svc.cluster.local:80'
    username: admin
    password: 'admin-password' # use existingSecret in production
  replicas:
    - url: 'http://adguard-home-replica.adguard.svc.cluster.local:80'
      username: admin
      password: 'admin-password'
  cron: '*/10 * * * *' # sync every 10 minutes; empty = continuous daemon
  runOnStart: true
  features:
    dns:
      serverConfig: true
      accessLists: true
      rewrites: true
    general:
      settings: true
      protection: true
      clients: true
      filters: true

service:
  dns:
    type: LoadBalancer
    loadBalancerIP: '192.168.1.53'
# values.yaml — AdGuard Home with daily S3 backup
# Backup archives both conf and work PVCs (tar — no database)
config:
  adGuardHome:
    http:
      address: 0.0.0.0:80
    users:
      - name: admin
        password: '$2y$10$...'
    schema_version: 29

service:
  dns:
    type: LoadBalancer
    loadBalancerIP: '192.168.1.53'

persistence:
  conf:
    enabled: true
    size: 256Mi
  work:
    enabled: true
    size: 5Gi

backup:
  enabled: true
  schedule: '0 2 * * *'
  s3:
    endpoint: https://s3.amazonaws.com
    bucket: adguard-backups
    prefix: adguard-home
    existingSecret: adguard-s3-credentials # keys: access-key, secret-key

ingress:
  enabled: true
  ingressClassName: traefik
  hosts:
    - host: adguard.example.com
      paths:
        - path: /
          pathType: Prefix

Configuration Reference

Image

ParameterTypeDefaultDescription
image.repositorystringdocker.io/adguard/adguardhomeAdGuard Home image.
image.tagstring"v0.107.73"Image tag.

Configuration

ParameterTypeDefaultDescription
config.adGuardHomeobject{}Pre-seed config rendered as AdGuardHome.yaml. Empty = wizard mode.
config.existingSecretstring""Existing secret with AdGuardHome.yaml content (key: AdGuardHome.yaml).
Generate bcrypt password hashes for config.adGuardHome.users

AdGuard Home requires bcrypt-hashed passwords in the configuration file. Generate with:

htpasswd -bnBC 10 "" 'mypassword' | cut -d: -f2

Use the output (starting with $2y$10$...) as the password value.

Persistence — Dual PVC

ParameterTypeDefaultDescription
persistence.conf.enabledbooleantrueEnable PVC for /opt/adguardhome/conf (config + TLS certs).
persistence.conf.sizestring256MiConf PVC size.
persistence.conf.storageClassstring""StorageClass for conf PVC.
persistence.conf.existingClaimstring""Use an existing PVC for conf.
persistence.work.enabledbooleantrueEnable PVC for /opt/adguardhome/work (query log + stats + filter lists).
persistence.work.sizestring2GiWork PVC size. Increase for longer query log retention.
persistence.work.storageClassstring""StorageClass for work PVC.
persistence.work.existingClaimstring""Use an existing PVC for work.

Services

ParameterTypeDefaultDescription
service.web.typestringClusterIPWeb UI service type.
service.web.portinteger80Web UI port.
service.dns.typestringLoadBalancerDNS service type. Use NodePort for bare-metal without MetalLB.
service.dns.portinteger53DNS service port (UDP + TCP).
service.dns.loadBalancerIPstring""Static IP for the DNS LoadBalancer.

Ingress

ParameterTypeDefaultDescription
ingress.enabledbooleanfalseEnable Ingress for the web UI.
ingress.ingressClassNamestringtraefikIngress class name.
ingress.hostsarray[]Host and path rules.
ingress.tlsarray[]TLS configuration.

Sync (adguardhome-sync)

ParameterTypeDefaultDescription
sync.enabledbooleanfalseDeploy the adguardhome-sync sidecar.
sync.origin.urlstring""URL of the origin AdGuard Home instance.
sync.origin.usernamestring""Admin username on the origin.
sync.origin.passwordstring""Admin password on the origin. Use existingSecret.
sync.replicasarray[]List of replica instances (url, username, password).
sync.cronstring*/10 * * * *Sync schedule. Empty string = continuous daemon mode.
sync.runOnStartbooleantrueRun sync immediately on pod startup.
sync.existingSecretstring""Existing secret with sync credentials.
sync.features.dns.serverConfigbooleantrueSync DNS server configuration.
sync.features.general.filtersbooleantrueSync filter lists.
sync.features.general.clientsbooleantrueSync client settings.

Backup

Backup archives both PVCs (conf + work) as tar archives. No database is involved.

ParameterTypeDefaultDescription
backup.enabledbooleanfalseEnable scheduled S3 backup CronJob.
backup.schedulestring"0 2 * * *"Cron schedule.
backup.archivePrefixstringadguard-homePrefix for backup archive filenames.
backup.s3.endpointstring""S3-compatible endpoint URL.
backup.s3.bucketstring""Target bucket name.
backup.s3.existingSecretstring""Existing secret with S3 credentials.
extraManifestsarray[]Extra Kubernetes manifests.

More Information