The self-hosted DNS blocker appliance (Pi-hole/AdGuard-style) with an honest Web UI, API, and an embedded DNS stack.
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”.
- 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).
- 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).
| Dashboard | Query Logs |
|---|---|
![]() |
![]() |
| Filtering | Client Policies |
![]() |
![]() |
| DNS Settings | Cluster / HA |
![]() |
![]() |
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:
- Start the container (commands below)
- Open the Web UI:
http://<server-ip>:8080 - Create your admin account
- Set your router’s DNS to the Sentinel IP (or VIP if you use HA)
- Done ✅
More docs (advanced / deeper): start here: docs/README.md
Not sure which Compose file to use? See deploy/compose/README.md
- Docker Engine + Docker Compose installed
- 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.
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
keepalivedservice.
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 -dIf 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.
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.mdOnce running:
- Web UI + API:
http://<server-ip>:8080 - DNS service:
<server-ip>:53(UDP/TCP)
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
On first start, create an admin user directly in the Web UI:
- Open
http://<server-ip>:8080 - Create username + password (min 8 chars)
- 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”).
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
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.
The dashboard world map uses a local MaxMind GeoLite2 database.
Recommended setup (no manual file copying):
- In the Web UI: Settings -> GeoIP / World Map
- Enter your MaxMind license key
- Click update/download
Sentinel will download and refresh the GeoLite2 City database inside the persistent /data volume.
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
53to the internet. - Prefer LAN/VPN access (Tailscale) instead of public DNS.
docker compose ps
docker compose logs -f
curl -fsS http://<server-ip>:8080/api/healthMore self-hosting/ops notes (upgrades, backups, port 53 conflicts):
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).
- License: LICENSE
- Contributing: CONTRIBUTING.md
- Security: SECURITY.md
- Code of Conduct: CODE_OF_CONDUCT.md
- Support: SUPPORT.md
- Some upstream endpoints require HTTP/2 for DoH. If an upstream DoH endpoint is not compatible with the current client implementation, use DoT instead.





