Wikielele. DNS · self-hosted DNS analytics
docs for
back to the dashboard

Installing by hand

Docker Compose without the script, for anyone who would rather read the YAML.

operatorAssumes a terminal, Docker, and editing a config file.page 6 of 24

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-dns

The 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:3000
.env — chmod 600 it, because it holds your admin password.
DNS_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:3000
.env — chmod 600 it, because it holds your app password.
sudo chmod 600 .env

2. 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:/data
docker-compose.yml

3. Start it#

sudo docker compose up -d
sudo docker compose logs -f

4. 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.

See also