Installing by hand
Docker Compose without the script, for anyone who would rather read the YAML.
The script does nothing you cannot do yourself in four commands. This is the whole of it.
1. A directory and a configuration#
sudo mkdir -p /opt/elele-dns/data
sudo chown -R 1001:1001 /opt/elele-dns/data
cd /opt/elele-dnsThe chown is the step that is easy to miss. The container runs as uid 1001 rather than as root, and a bind mount hands it whatever the host directory already is — a root-owned directory it cannot write its database into.
Write .env. The AdGuard Home credentials are the same ones you use to log into its admin interface.
Write .env. Pi-hole v6 has no username — the password alone is exchanged for a session — and it should be an application password from Settings → Web interface / API rather than the one you type into the login box.
DNS_PROVIDER=adguard
ADGUARD_URL=http://192.168.1.10:3000
ADGUARD_USERNAME=admin
ADGUARD_PASSWORD=your-adguard-password
DATABASE_PATH=/data/queries.db
RETENTION_DAYS=90
PUBLIC_URL=http://192.168.1.10:3000DNS_PROVIDER=pihole
PIHOLE_URL=http://192.168.1.10
PIHOLE_PASSWORD=your-pihole-app-password
DATABASE_PATH=/data/queries.db
RETENTION_DAYS=90
PUBLIC_URL=http://192.168.1.10:3000sudo chmod 600 .env2. A compose file#
services:
elele-dns:
image: hunterelele/elele-dns:latest
container_name: elele-dns
restart: unless-stopped
env_file: .env
ports:
# The image listens on 3001. The left-hand side is the port you visit.
- "3000:3001"
volumes:
- ./data:/data3. Start it#
sudo docker compose up -d
sudo docker compose logs -f4. Check it#
curl -s http://127.0.0.1:3000/api/health | jq
curl -s http://127.0.0.1:3000/api/ingest/status | jq/api/health reports process health and resolver reachability separately: a container that cannot reach the resolver is degraded, not dead, and restarting it will not help. /api/ingest/status is the one to watch, and progress.lagMs is the number that matters.
Podman#
Works, with the usual caveats. Use podman-compose or generate a systemd unit with podman generate systemd. Rootless Podman cannot bind , which matters for the resolver rather than for this.
Building from source#
You cannot, and it would not help you if you could. The source is not public. What ships is the image, built for linux/arm64 and linux/amd64 from the same commit every time, so there is nothing a local build would give you that docker compose pull does not.
If you are here because you want to know what it does before you run it, that is a fair thing to want: Architecture describes the schema, the ingest loop and the rollups, and the demo is the real interface running real queries against invented data.