Service that manages name servers
  • Go 70.1%
  • HTML 28.3%
  • Makefile 1.1%
  • Dockerfile 0.5%
Find a file
Adam Štrauch 381a351912
All checks were successful
Dev pipeline / build-and-push (push) Successful in 40s
feat: add release workflow and update host variables in Taskfile
2026-05-16 20:13:19 +02:00
.gitea/workflows feat: add release workflow and update host variables in Taskfile 2026-05-16 20:13:19 +02:00
docs feat: add PATCH /zones/:zone_id/owner endpoint for dedicated owner change 2026-05-13 11:50:06 +02:00
pkg/dnsapi feat: add link to open zone owner in admin panel if applicable 2026-05-16 19:43:59 +02:00
.gitignore README and Makefile update 2021-09-19 12:54:49 +02:00
AGENTS.md feat: add DNS Admin UI and zone status API 2026-05-09 21:03:39 +02:00
db.sqlite feat: add expandRelativeHost function for hostname normalization and update MX/CNAME/NS handling in DNSCheckRecord 2026-05-10 16:07:35 +02:00
docker-compose.yml feat: Add DNSSEC support for zones 2026-05-10 20:00:42 +02:00
Dockerfile feat: add DNS Admin UI and zone status API 2026-05-09 21:03:39 +02:00
go.mod feat: add expandRelativeHost function for hostname normalization and update MX/CNAME/NS handling in DNSCheckRecord 2026-05-10 16:07:35 +02:00
go.mod.backup feat: add DNS Admin UI and zone status API 2026-05-09 21:03:39 +02:00
go.sum fix: update DNS query implementation to use miekg/dns library for direct queries 2026-05-10 01:28:52 +02:00
go.sum.backup feat: add DNS Admin UI and zone status API 2026-05-09 21:03:39 +02:00
LICENSE Initial commit 2018-05-17 00:00:33 +02:00
main.go feat: add DNS Admin UI and zone status API 2026-05-09 21:03:39 +02:00
Makefile feat: add DNS Admin UI and zone status API 2026-05-09 21:03:39 +02:00
pipeline.yml Add go vet into Makefile 2018-05-22 14:25:45 +02:00
README.md feat: add DNS Admin UI and zone status API 2026-05-09 21:03:39 +02:00
requests.http Domain validation, basic http client 2018-08-15 23:35:54 +02:00
Taskfile.yml feat: add release workflow and update host variables in Taskfile 2026-05-16 20:13:19 +02:00

DNS API

Lightweight REST API for managing Bind9 zones and records across a primary/secondary nameserver setup.

The service runs in two modes:

  • Primary — exposes the full zone/record CRUD API, persists data in SQLite, writes zone files and bind config to the local filesystem, and pushes updates to secondary instances via HTTP.
  • Secondary — exposes only the bind-sync endpoints used by the primary to deliver zone files and named.conf. It writes received files to disk and reloads bind9.

Both modes are provided by the same binary; the runtime mode is controlled by DNSAPI_MODE.

Quick start (Docker Compose)

cp docker-compose.yml.example docker-compose.yml   # adjust tokens and nameserver FQDNs
docker compose up -d

See docker-compose.yml for a full example with comments explaining the rndc setup.

Modes

Primary mode

The primary instance:

  • Serves the REST API (zones, records, commit, export/import).
  • On commit: writes the zone file locally, pushes it to each secondary via PUT /bind/zones/:domain, regenerates and writes named.conf.rosti locally, and pushes it to each secondary via PUT /bind/config.
  • Reloads bind9 via the configured reload command after updating local config/zones.
  • 10 seconds after commit, forces an rndc refresh on itself and all secondaries.

Secondary mode

The secondary instance:

  • Exposes only the bind-sync endpoints (protected by the same API token as primary).
  • On receiving a zone file or bind config via HTTP, writes it to disk and reloads bind9.

Configuration

All settings are environment variables with the DNSAPI_ prefix.

Variable Default Description
DNSAPI_MODE primary Runtime mode: primary or secondary
DNSAPI_API_TOKEN (required) Bearer token for all API calls
DNSAPI_PORT 1323 HTTP listen port (ignored when HTTPS is enabled)
DNSAPI_PRIMARY_NAME_SERVER (required in primary) FQDN of the primary nameserver
DNSAPI_NAME_SERVERS (required in primary) Comma-separated list of all nameservers (≥2)
DNSAPI_PRIMARY_NAME_SERVER_IP (auto-resolved) Override for the primary NS IP — set this in containers/environments where the FQDN is not resolvable
DNSAPI_SECONDARY_NAME_SERVER_IPS (auto-resolved) Override for the secondary NS IPs (zone allow-transfer / masters) — set this alongside the primary IP override
DNSAPI_SECONDARY_INSTANCES (empty) Comma-separated HTTP URLs of secondary dnsapi instances
DNSAPI_ABUSE_EMAIL (required in primary) SOA abuse email
DNSAPI_DATABASE_PATH gorm.sqlite SQLite database path (primary only)
DNSAPI_BIND_RELOAD_COMMAND systemctl reload bind9 Shell command used to reload bind9
DNSAPI_BIND_REFRESH_COMMAND rndc refresh Command prefix for zone refresh (zone name appended)
DNSAPI_HTTPS false Enable HTTPS with automatic Let's Encrypt certificate
DNSAPI_PUBLIC_DOMAIN (required when HTTPS) Domain for the TLS certificate
DNSAPI_ACME_EMAIL (required when HTTPS) Email for Let's Encrypt registration
DNSAPI_SENTRY_DSN (empty) Sentry DSN for error reporting
DNSAPI_SENTRY_ENV dev Sentry environment tag
DNSAPI_TIME_TO_REFRESH 300 SOA refresh interval
DNSAPI_TIME_TO_RETRY 180 SOA retry interval
DNSAPI_TIME_TO_EXPIRE 604800 SOA expire value
DNSAPI_MINIMAL_TTL 30 SOA minimum TTL
DNSAPI_TTL 3600 Default record TTL

Installation (systemd)

Primary (runs on the same host as bind9):

[Unit]
Description=dnsapi — primary
After=network.target

[Service]
WorkingDirectory=/etc/bind
Environment=DNSAPI_MODE=primary
Environment=DNSAPI_API_TOKEN=<strong-secret>
Environment=DNSAPI_PRIMARY_NAME_SERVER=ns1.example.com
Environment=DNSAPI_NAME_SERVERS=ns1.example.com,ns2.example.com
Environment=DNSAPI_SECONDARY_INSTANCES=https://ns2.example.com:1323
Environment=DNSAPI_ABUSE_EMAIL=admin@example.com
Environment=DNSAPI_DATABASE_PATH=/var/lib/dnsapi/db.sqlite
Environment=DNSAPI_PORT=1323
ExecStart=/usr/local/bin/dnsapi
Restart=on-failure

[Install]
WantedBy=multi-user.target

Secondary (runs on the same host as the secondary bind9):

[Unit]
Description=dnsapi — secondary
After=network.target

[Service]
WorkingDirectory=/etc/bind
Environment=DNSAPI_MODE=secondary
Environment=DNSAPI_API_TOKEN=<same-strong-secret>
Environment=DNSAPI_PORT=1323
ExecStart=/usr/local/bin/dnsapi
Restart=on-failure

[Install]
WantedBy=multi-user.target

HTTPS / Let's Encrypt

Set DNSAPI_HTTPS=true, DNSAPI_PUBLIC_DOMAIN=ns1.example.com, and DNSAPI_ACME_EMAIL=admin@example.com.

The service starts on port 443 (TLS) and opens an HTTP-01 challenge listener on port 80.
Certificates are cached in /var/cache/dnsapi/autocert.

When HTTPS is enabled, DNSAPI_PORT is ignored.

API

Swagger UI is served at /swagger/index.html (primary mode only).

Zones

Method Path Description
GET /zones/ List all zones
GET /zones/:zone_id Get a single zone
POST /zones/ Create a zone
PUT /zones/:zone_id Update zone tags / abuse email
DELETE /zones/:zone_id Delete a zone
PUT /zones/:zone_id/commit Write zone to bind and sync to secondaries

Records

Method Path Description
GET /zones/:zone_id/records/ List records
GET /zones/:zone_id/records/:record_id Get a record
POST /zones/:zone_id/records/ Create a record
PUT /zones/:zone_id/records/:record_id Update a record
DELETE /zones/:zone_id/records/:record_id Delete a record

Secondary sync endpoints (secondary mode only)

Method Path Description
PUT /bind/config Write named.conf include file and reload bind9
PUT /bind/zones/:domain Write a zone file
DELETE /bind/zones/:domain Delete a zone file
POST /bind/reload Reload bind9
POST /bind/refresh/:domain Force zone refresh

All endpoints require Authorization: <token> header.

Database migrations

Versioned SQL migrations are embedded in the binary (pkg/dnsapi/migrations/*.sql) and applied automatically on startup. Migration state is tracked in the schema_migrations table.

Development

go build ./...
go test ./...
make docs   # regenerate Swagger docs