┌─┐┌─┐┬ ┬┌─┐┌─┐┌─┐
├┤ │ │ │├─┘└─┐│ │
└─┘└─┘┴─┘┴┴ └─┘└─┘
v2.0.0
Fast, lightweight authoritative DNS server built for infrastructure you own.
Eclipso is a production-grade DNS server written in Go that gives you full control over your DNS infrastructure — no third-party API, no vendor dashboard, no per-query pricing. Define your zones in simple TOML files, store them on disk or in any S3-compatible object store, and let Eclipso handle the rest.
It speaks UDP, TCP, and DNS-over-TLS (DoT), responds in ~160 microseconds, and fits in a single static binary. Whether you're running a handful of domains on a VPS or powering service discovery across a distributed cluster, Eclipso is designed to stay out of your way and just work.
- Self-hosted DNS done right — Run your own authoritative nameserver without the operational complexity of BIND or PowerDNS. Zone files are human-readable TOML, configuration is environment variables, and the whole thing deploys as a single container.
- S3-native zone management — Store zone files in AWS S3, Predastore, MinIO, or any S3-compatible backend. Eclipso syncs automatically, so you can manage DNS records through the same object storage pipeline as the rest of your infrastructure.
- Built for MulgaOS Hive — Eclipso serves as the DNS backbone for Hive, an open-source AWS alternative. It handles both internal service discovery (SRV records for NATS, gateways, and other cluster services) and public-facing authoritative DNS, all from the same instance.
- Plays nice with public resolvers — Full RFC compliance means Cloudflare (1.1.1.1), Google (8.8.8.8), and every other recursive resolver can properly resolve your domains. TCP fallback, EDNS0, correct NXDOMAIN/NODATA semantics, proper authority sections — the things that matter when your DNS needs to actually work on the real internet.
- UDP + TCP + DNS-over-TLS on configurable ports
- EDNS0 support for modern resolver compatibility
- 10 record types — A, AAAA, CNAME, MX, NS, TXT, SOA, SRV, CAA, PTR
- Wildcard records with exact-match priority
- In-memory hashmap for O(1) lookups (~160µs per query)
- Zone files in TOML format, loaded from local filesystem or S3
- Live reload — filesystem watch (fsnotify) or periodic S3 sync
- S3-compatible backends — works with AWS S3, Predastore, MinIO, etc.
- Correct RFC semantics — NXDOMAIN, NODATA, REFUSED, NS authority section, zone-based SOA serial
- Configurable upstream resolvers with TLS and failover for CNAME chasing
- Graceful shutdown on SIGTERM/SIGINT
- Container-first — multi-arch Docker images, single binary
git clone https://github.com/benduncan/eclipso
cd eclipso
make build
ZONE_DIR="./config/domains" ./bin/eclipsoVerify it works:
dig @127.0.0.1 hello_a.net A
dig @127.0.0.1 hello_a.net MX
dig @127.0.0.1 hello_a.net TXT
dig @127.0.0.1 hello_a.net A +tcp # TCP query
dig @127.0.0.1 hello_a.net A +edns=0 # EDNS0 queryAll configuration is via environment variables.
| Variable | Default | Description |
|---|---|---|
ZONE_DIR |
config/domains/ |
Path to zone files or s3://bucket-name |
HOST |
0.0.0.0 |
Listen address |
PORT |
53 |
Listen port (UDP + TCP) |
ECLIPSO_LOG_IGNORE |
Suppress all logging | |
ECLIPSO_LOG_DEBUG |
Enable debug logging |
| Variable | Default | Description |
|---|---|---|
ECLIPSO_TLS_CERT |
Path to TLS certificate (PEM) | |
ECLIPSO_TLS_KEY |
Path to TLS private key | |
DOT_PORT |
853 |
DoT listener port |
| Variable | Default | Description |
|---|---|---|
AWS_ACCESS_KEY |
AWS access key ID | |
AWS_SECRET_ACCESS_KEY |
AWS secret access key | |
AWS_REGION |
AWS region | |
ECLIPSO_S3_ENDPOINT |
Custom S3 endpoint URL (for Predastore, MinIO, etc.) | |
ECLIPSO_S3_INSECURE |
Skip TLS verification for self-signed certs | |
S3_SYNC_RETRY |
60 |
S3 sync interval in seconds |
| Variable | Default | Description |
|---|---|---|
ECLIPSO_UPSTREAM |
tls://1.1.1.1:853,tls://8.8.8.8:853,1.1.1.1:53 |
Comma-separated upstream servers for CNAME chasing. Prefix with tls:// for DNS-over-TLS. |
Zone files use TOML. Each file represents one zone and is named <domain>.toml.
version = 1.0
[domain]
domain = "example.com"
soa = "ns1.example.com."
created = 2024-01-01T00:00:00Z
modified = 2024-06-15T12:00:00Z
verified = true
active = true
ownerid = 1
[defaults]
ttl = 3600
type = 1 # A record
class = 1 # IN
# A records
[[records]]
domain = ""
address = "203.100.1.1"
[[records]]
domain = "www."
address = "203.100.1.1"
# Wildcard — matches any subdomain without an explicit record
[[records]]
domain = "*."
address = "203.100.1.99"
# NS records
[[records]]
domain = ""
type = 2
address = "ns1.example.com."
[[records]]
domain = ""
type = 2
address = "ns2.example.com."
# MX records
[[records]]
domain = ""
type = 15
preference = 10
address = "mail.example.com."
# TXT records (SPF, DKIM, verification, etc.)
[[records]]
domain = ""
type = 16
address = "v=spf1 mx a -all"
# AAAA record
[[records]]
domain = ""
type = 28
address = "2001:db8::1"
# SRV record (service discovery)
[[records]]
domain = "_nats._tcp."
type = 33
priority = 10
weight = 0
port = 4222
address = "node1.example.com."
# CAA record (certificate authority authorization)
[[records]]
domain = ""
type = 257
caa_flag = 0
caa_tag = "issue"
address = "letsencrypt.org"
# PTR record (reverse DNS — in a separate zone file for in-addr.arpa)
# [[records]]
# domain = "1."
# type = 12
# address = "host-1.example.com."| Type | Code | Fields |
|---|---|---|
| A | 1 | address (IPv4) |
| NS | 2 | address (nameserver FQDN) |
| CNAME | 5 | address (target FQDN) |
| SOA | 6 | Auto-generated from [domain] section |
| PTR | 12 | address (target FQDN) |
| MX | 15 | address (mail server FQDN), preference |
| TXT | 16 | address (text value) |
| AAAA | 28 | address (IPv6) |
| SRV | 33 | address (target FQDN), priority, weight, port |
| CAA | 257 | address (CA domain), caa_flag, caa_tag |
Eclipso serves as the DNS layer for MulgaOS Hive, providing both internal service discovery and public authoritative DNS.
Service discovery with SRV records:
# _nats._tcp.hive.phasegrid.net → node1.hive.phasegrid.net:4222
[[records]]
domain = "_nats._tcp.hive."
type = 33
priority = 10
weight = 0
port = 4222
address = "node1.hive.phasegrid.net."
# _awsgw._tcp.hive.phasegrid.net → node1.hive.phasegrid.net:9999
[[records]]
domain = "_awsgw._tcp.hive."
type = 33
priority = 10
weight = 0
port = 9999
address = "node1.hive.phasegrid.net."Using Predastore as the zone file backend:
Hive's S3-compatible storage (Predastore) can serve as the zone file backend, keeping DNS configuration alongside the rest of the Hive infrastructure:
ZONE_DIR="s3://dns-zones" \
ECLIPSO_S3_ENDPOINT="https://predastore.hive.phasegrid.net:8443" \
ECLIPSO_S3_INSECURE=1 \
AWS_ACCESS_KEY="..." \
AWS_SECRET_ACCESS_KEY="..." \
AWS_REGION="us-west-1" \
./bin/eclipsoDocker Compose (S3):
AWS_ACCESS_KEY="X" AWS_SECRET_ACCESS_KEY="Y" ZONE_DIR="s3://my-bucket" AWS_REGION="us-west-1" docker compose up -dStandalone (filesystem):
docker run \
--mount src=./config/domains,target=/config/domains,type=bind \
-e ZONE_DIR="/config/domains" \
-p 53:53/udp -p 53:53/tcp \
calacode/eclipso-dnsWith DNS-over-TLS:
docker run \
--mount src=./config/domains,target=/config/domains,type=bind \
--mount src=./certs,target=/certs,type=bind \
-e ZONE_DIR="/config/domains" \
-e ECLIPSO_TLS_CERT="/certs/server.pem" \
-e ECLIPSO_TLS_KEY="/certs/server.key" \
-p 53:53/udp -p 53:53/tcp -p 853:853/tcp \
calacode/eclipso-dnsmake test # Unit tests (31 tests)
make race # Race condition detection
make bench # Benchmarks with benchstat
make e2e # E2E tests via Docker (Predastore + Eclipso)
make test-all # Unit tests + race detectionmake benchSimulates 26 domains with ~255 subdomains each:
name time/op
DNSQueryA-8 160µs ±12%
DNSQueryTXT-8 172µs ±19%
DNSQueryMX-8 162µs ±12%
name alloc/op
DNSQueryA-8 3.09kB ± 0%
DNSQueryTXT-8 3.68kB ± 0%
DNSQueryMX-8 4.05kB ± 0%
See DEV.md for the full development plan.
- DNS-over-HTTPS (DoH)
- DNSSEC signing
- Prometheus metrics endpoint
- Rate limiting / DDoS protection
- Dynamic record API (HTTP)
- Split-horizon DNS (internal vs external views)
- Health-aware DNS responses
- Response caching
MIT