Skip to content

robotnikz/sentinel-dns

Sentinel-DNS

Sentinel-DNS

The self-hosted DNS blocker appliance (Pi-hole/AdGuard-style) with an honest Web UI, API, and an embedded DNS stack.

Stars Issues Last Commit CI/CD Pipeline GitHub Release License Docker Compose

Quickstart · Screenshots · Security · Troubleshooting


⚡ What is Sentinel-DNS?

Sentinel-DNS is a self-hosted DNS blocker (Pi-hole/AdGuard-style) that runs on your own server. You point your router (or your devices) to Sentinel-DNS as the DNS server and it will block ads/trackers/malware domains before they reach your apps.

If you’re new to DNS blockers: don’t worry — the setup is basically “start the container → open the web UI → set router DNS”.

⭐ Why people choose Sentinel-DNS (vs. Pi-hole / AdGuard Home)

  • Sync + HA + Failover (2 nodes): a floating VIP (keepalived/VRRP) + automatic cluster sync so DNS keeps working even if one node dies.
  • Built-in Tailscale integration: use Sentinel-DNS as your DNS blocker on-the-go (remote devices can use the same policies and filtering via Tailscale).
  • Per-client policies (device + subnet): client-specific rules + domain/category/app blocklists (kids, guests, TVs, work devices, etc.)
  • Honest UI: health/cluster indicators are backed by real checks (not just a green badge).

✨ Key Features (short)

  • Fast setup: one container, persistent data volume
  • DNS filtering: blocklists + allow/deny rules + local DNS rewrites
  • Client policies: per device + per subnet
  • Blocklists: domain lists + Category: lists + App: lists
  • Visibility: query logs, metrics, DNS Activity Map
  • Encrypted upstream DNS: UDP / DoT / DoH
  • Optional remote access: Tailscale (docs/REMOTE_ACCESS_TAILSCALE.md)
  • Optional HA: Sync + VIP failover (docs/CLUSTER_HA.md)

Note

AI features are optional and opt-in. They require you to add a Gemini or ChatGPT API key in the UI. Sentinel-DNS will never contact an AI provider “in the background” — AI requests only happen on an explicit user action (e.g. clicking an analyze button).

📸 Screenshots

Dashboard Query Logs
Dashboard Query Logs
Filtering Client Policies
Filtering Clients
DNS Settings Cluster / HA
DNS Settings Cluster / HA

🧭 Quickstart

Sentinel-DNS is shipped as a Docker image. You can run it on a Raspberry Pi, NAS, mini PC, or server.

If you want the “just tell me what to do” version:

  1. Start the container (commands below)
  2. Open the Web UI: http://<server-ip>:8080
  3. Create your admin account
  4. Set your router’s DNS to the Sentinel IP (or VIP if you use HA)
  5. Done ✅

More docs (advanced / deeper): start here: docs/README.md

Not sure which Compose file to use? See deploy/compose/README.md

🧰 Docker Prerequisites

  1. Docker Engine + Docker Compose installed
  2. Your firewall allows LAN access to: 53/udp, 53/tcp, 8080/tcp

Note

HA/VIP failover requires Linux hosts (keepalived uses host networking). Single-node works anywhere Docker works.

🧩 Method 1: Docker Compose (Recommended)

Tip

For production, pin a version tag (instead of latest) so upgrades/rollbacks are explicit.

Fastest path: use the repo’s compose file. It runs Sentinel only by default.

  • Want Tailscale? Uncomment the marked block under sentinel.
  • Want HA/VIP failover (keepalived)? Uncomment the keepalived service.

The Web UI adapts automatically:

  • If Tailscale isn’t enabled in your deployment, the Tailscale/Remote UI is hidden.
  • If keepalived/HA isn’t deployed, the “Cluster / HA” navigation entry is hidden.
# deploy/compose/docker-compose.yml
services:
  sentinel:
    image: ghcr.io/robotnikz/sentinel-dns:latest
    container_name: sentinel-dns
    ports:
      # DNS service (UDP/TCP). If port 53 is already in use (e.g. systemd-resolved),
      # adjust the host-side port mapping or disable the stub resolver.
      # By default this is published on all host interfaces (0.0.0.0).
      # To keep it reachable for all devices in your LAN while avoiding WAN exposure,
      # bind explicitly to your LAN interface IP (example 192.168.1.10):
      # - "192.168.1.10:53:53/udp"
      # - "192.168.1.10:53:53/tcp"
      - "53:53/udp"
      - "53:53/tcp"
      # Web UI + API
      # Same idea for the UI/API:
      # - "192.168.1.10:8080:8080"
      - "8080:8080"
    environment:
      # Timezone used for logs/UI timestamps.
      - TZ=Europe/Berlin
      # Ensure the Web UI/API is reachable via Docker port publishing.
      - HOST=0.0.0.0
      - PORT=8080
      # Optional: HA/VIP role override file (written by keepalived sidecar).
      # If you never configure HA in the UI, this file won't exist and Sentinel runs normally.
      - CLUSTER_ROLE_FILE=/data/sentinel/cluster_role
    volumes:
      # Persistent storage for Postgres data, settings, secrets, and the GeoIP database.
      - sentinel-data:/data

    # --------------------------------------------------------------------------
    # Optional feature: Tailscale remote access (embedded tailscaled)
    # --------------------------------------------------------------------------
    # Uncomment the block below to enable Tailscale.
    # cap_add:
    #   - NET_ADMIN
    # devices:
    #   - /dev/net/tun:/dev/net/tun
    #
    # Optional: Exit Node / subnet routes (uncomment as needed)
    # sysctls:
    #   net.ipv4.ip_forward: "1"
    #   net.ipv6.conf.all.forwarding: "1"
    restart: unless-stopped

  # --------------------------------------------------------------------------
  # Optional feature: HA / VIP failover (keepalived sidecar)
  # --------------------------------------------------------------------------
  # Uncomment the whole service below to enable keepalived/HA.
  # keepalived:
  #   image: ghcr.io/robotnikz/sentinel-dns-keepalived:latest
  #   container_name: sentinel-keepalived
  #   network_mode: host
  #   restart: unless-stopped
  #   cap_add:
  #     - NET_ADMIN
  #     - NET_RAW
  #     - NET_BROADCAST
  #   environment:
  #     - DATA_DIR=/data
  #     - HA_CONFIG_FILE=/data/sentinel/ha/config.json
  #     - HA_ROLE_FILE=/data/sentinel/cluster_role
  #     - HA_NETINFO_FILE=/data/sentinel/ha/netinfo.json
  #     - HA_READY_URL=http://127.0.0.1:8080/api/cluster/ready
  #   volumes:
  #     - sentinel-data:/data

volumes:
  sentinel-data:

Or download the compose file directly and run it:

# Linux/macOS
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/robotnikz/sentinel-dns/main/deploy/compose/docker-compose.yml
docker compose up -d

# Windows PowerShell
curl.exe -fsSL -o docker-compose.yml https://raw.githubusercontent.com/robotnikz/sentinel-dns/main/deploy/compose/docker-compose.yml
docker compose up -d

If you want HA/VIP failover, uncomment keepalived in your compose file and then configure it in the UI (“Cluster / HA”) after startup.

If you want Tailscale remote access, uncomment the marked cap_add/devices block under sentinel and then configure it in the UI.

The source of truth is always deploy/compose/docker-compose.yml.

Important

Upgrade safety (data/history): keep your sentinel-data volume. If you delete or change this mount, Sentinel will start fresh.

Important

Port 53 conflicts are common on Linux (e.g. systemd-resolved). If Sentinel fails to start, change the host port mapping in your compose file or disable the stub resolver.

🖥️ Method 2: Docker CLI

docker run -d \
  -p 8080:8080 \
  -p 53:53/udp \
  -p 53:53/tcp \
  -v sentinel-data:/data \
  -e TZ=Europe/Berlin \
  --restart unless-stopped \
  --name sentinel-dns \
  ghcr.io/robotnikz/sentinel-dns:latest

# Note: for remote access (Tailscale) / Exit Node, extra privileges are required.
# See: docs/REMOTE_ACCESS_TAILSCALE.md

Once running:

  • Web UI + API: http://<server-ip>:8080
  • DNS service: <server-ip>:53 (UDP/TCP)

🧠 Important concept (Leader / Follower)

If you enable Sync/HA:

  • You make changes on the Leader (settings, clients, blocklists, rules…)
  • The Follower automatically syncs and is read-only
  • Your network uses the VIP (virtual IP) as DNS, so failover is transparent

✅ First run

On first start, create an admin user directly in the Web UI:

  1. Open http://<server-ip>:8080
  2. Create username + password (min 8 chars)
  3. Log in (session cookie)

Optional AI features (Gemini / ChatGPT) can be enabled from the UI by adding an API key. Keys are stored encrypted server-side.

Sentinel-DNS will only send an AI request when you explicitly trigger it (for example: “analyze this domain”).

🧩 Client policies (what makes Sentinel powerful)

In Clients / Client Policies you can create policies for:

  • Individual devices (by IP/MAC)
  • Networks & Subnets (e.g. guest Wi‑Fi)

Each client policy can have different filtering, for example:

  • stricter blocking for kids devices
  • relaxed rules for a Smart TV
  • a “work” profile that blocks distractions

Blocklists: domain vs category vs app

Sentinel supports multiple kinds of lists:

  • Domain blocklists (classic ad/tracker lists)
  • Category blocklists (e.g. "Category: adult", "Category: gambling")
  • App blocklists (e.g. "App: tiktok", "App: youtube")

You can enable these globally and/or per client.

🗺️ GeoIP database

The dashboard world map uses a local MaxMind GeoLite2 database.

Recommended setup (no manual file copying):

  1. In the Web UI: Settings -> GeoIP / World Map
  2. Enter your MaxMind license key
  3. Click update/download

Sentinel will download and refresh the GeoLite2 City database inside the persistent /data volume.

🔒 Security & Hardening

Sentinel-DNS is intended for self-hosting on a trusted network.

  • If you expose the UI beyond your LAN, put it behind a reverse proxy with TLS.
  • Do not expose port 53 to the internet.
  • Prefer LAN/VPN access (Tailscale) instead of public DNS.

🛠️ Troubleshooting

docker compose ps
docker compose logs -f
curl -fsS http://<server-ip>:8080/api/health

More self-hosting/ops notes (upgrades, backups, port 53 conflicts):

🧾 Notes on DNSSEC

With public upstream resolvers (Google/Cloudflare/Quad9), DNSSEC is typically validated by the upstream resolver. If you want DNSSEC validation locally inside the appliance, use Unbound (Local).

📜 Project policies

⚠️ Limitations

  • Some upstream endpoints require HTTP/2 for DoH. If an upstream DoH endpoint is not compatible with the current client implementation, use DoT instead.

About

The self-hosted DNS blocker appliance (Pi-hole/AdGuard-style) with an honest Web UI, API, and an embedded DNS stack.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors