Skip to main content

A lightweight, JSON-configurable DNS server for internal networks

Project description

NanoDNS

A lightweight, zero-dependency DNS server for internal networks — configured with a single JSON file.

PyPI version Python License: MIT CI Docker Pulls OCI


Features

  • 🚀 Zero dependencies — pure Python standard library only
  • 📝 JSON config — human-readable, hot-reloadable configuration
  • 🔄 Upstream forwarding — forwards unknown queries to public DNS
  • 💾 LRU Cache — configurable in-memory response cache with TTL
  • 🌐 Record types — A, AAAA, CNAME, MX, TXT, PTR, NS, SOA
  • 🃏 Wildcard records*.example.internal support
  • 🚫 Rewrites / blocking — NXDOMAIN any domain instantly
  • ♻️ Hot reload — config changes applied without restart
  • Async — built on Python asyncio
  • 🐳 Docker — multi-platform OCI image (amd64 + arm64)
  • 🔏 Signed — cosign keyless signatures via Sigstore

Installation

pip

pip install nanodns

Docker

# Docker Hub
docker pull iyuangang/nanodns:latest

# GitHub Container Registry
docker pull ghcr.io/iyuangang/nanodns:latest

Quick Start

pip

# 1. Generate an example config
nanodns init

# 2. Edit nanodns.json to your needs

# 3. Validate config
nanodns check nanodns.json

# 4. Start (high port — no admin rights needed)
nanodns start --config nanodns.json --port 5353

# 5. Start on port 53 (requires root / Administrator)
sudo nanodns start --config nanodns.json

Docker

# Generate a config first
nanodns init nanodns.json

# Run with Docker
docker run -d \
  --name nanodns \
  -p 53:53/udp \
  -v $(pwd)/nanodns.json:/etc/nanodns/nanodns.json:ro \
  --cap-add NET_BIND_SERVICE \
  iyuangang/nanodns:latest

# Run with Docker Compose
docker compose up -d

Verify it works

# Linux / macOS
dig @127.0.0.1 -p 5353 web.internal.lan A

# Windows
nslookup web.internal.lan 127.0.0.1

# PowerShell
Resolve-DnsName -Name web.internal.lan -Server 127.0.0.1 -Type A

Configuration

{
  "server": {
    "host": "0.0.0.0",
    "port": 53,
    "upstream": ["8.8.8.8", "1.1.1.1"],
    "upstream_timeout": 3,
    "upstream_port": 53,
    "cache_enabled": true,
    "cache_ttl": 300,
    "cache_size": 1000,
    "log_level": "INFO",
    "log_queries": true,
    "hot_reload": true
  },
  "zones": {
    "internal.lan": {
      "soa": {
        "mname": "ns1.internal.lan",
        "rname": "admin.internal.lan",
        "serial": 2024010101,
        "refresh": 3600,
        "retry": 900,
        "expire": 604800,
        "minimum": 300
      },
      "ns": ["ns1.internal.lan"]
    }
  },
  "records": [
    { "name": "ns1.internal.lan",  "type": "A",     "value": "192.168.1.10",  "ttl": 3600 },
    { "name": "web.internal.lan",  "type": "A",     "value": "192.168.1.100", "ttl": 300  },
    { "name": "db.internal.lan",   "type": "A",     "value": "192.168.1.101", "ttl": 300  },
    { "name": "api.internal.lan",  "type": "CNAME", "value": "web.internal.lan"            },
    { "name": "ipv6.internal.lan", "type": "AAAA",  "value": "fd00::1",       "ttl": 300  },
    { "name": "internal.lan",      "type": "MX",    "value": "mail.internal.lan", "priority": 10 },
    { "name": "internal.lan",      "type": "TXT",   "value": "v=spf1 ip4:192.168.1.0/24 ~all" },
    {
      "name": "app.internal.lan",
      "type": "A",
      "value": "192.168.1.200",
      "wildcard": true,
      "comment": "Matches *.app.internal.lan"
    }
  ],
  "rewrites": [
    { "match": "ads.doubleclick.net", "action": "nxdomain" },
    { "match": "*.tracker.example",   "action": "nxdomain" }
  ]
}

See USAGE.md for the full configuration reference.


CLI Reference

nanodns start  --config FILE  [--host HOST] [--port PORT] [--log-level LEVEL] [--no-cache]
nanodns init   [OUTPUT]       Generate an example config file
nanodns check  CONFIG         Validate a config file and print a summary
nanodns --version

Record Types

Type value Extra fields
A IPv4 address
AAAA IPv6 address
CNAME Target hostname
MX Mail server hostname priority (int)
TXT Text string
PTR Pointer hostname
NS Nameserver hostname

All records support: ttl (default 300), wildcard (bool), comment (string).


Docker

Images

Registry Image
Docker Hub iyuangang/nanodns
GHCR ghcr.io/iyuangang/nanodns

Tags

Tag Description
latest Latest stable release
1.2.3 Exact version
1.2 Minor version
1 Major version
sha-a1b2c3 Specific commit

Platforms

linux/amd64 · linux/arm64 (Raspberry Pi, Apple Silicon)

docker-compose.yml

services:
  nanodns:
    image: iyuangang/nanodns:latest
    container_name: nanodns
    restart: unless-stopped
    ports:
      - "53:53/udp"
    volumes:
      - ./nanodns.json:/etc/nanodns/nanodns.json:ro
    cap_add:
      - NET_BIND_SERVICE
    read_only: true

OCI Compliance

Images follow the OCI Image Spec with standard annotations:

# Inspect OCI annotations
docker inspect iyuangang/nanodns:latest \
  --format '{{json .Config.Labels}}' | python3 -m json.tool

# Verify cosign signature (keyless / Sigstore)
cosign verify \
  --certificate-identity-regexp="https://github.com/iyuangang/nanodns/*" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  iyuangang/nanodns:latest

Deployment

Linux — systemd

# /etc/systemd/system/nanodns.service
[Unit]
Description=NanoDNS Server
After=network.target

[Service]
ExecStart=/usr/local/bin/nanodns start --config /etc/nanodns/nanodns.json
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now nanodns

Windows — NSSM

nssm install NanoDNS "C:\Python\Scripts\nanodns.exe"
nssm set NanoDNS AppParameters "start --config C:\dns\nanodns.json"
nssm start NanoDNS

High Port (No Root Required)

nanodns start --config nanodns.json --port 5353

# Redirect port 53 → 5353 with iptables
sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353

CI / CD

Workflow Trigger Description
release.yml v* tag Run tests → build wheel → GitHub Release → publish PyPI
docker.yml v* tag / main Build multi-platform OCI image → push GHCR + Docker Hub → cosign sign

Release a new version

# 1. Bump version in pyproject.toml
# 2. Commit and tag
git add pyproject.toml
git commit -m "chore: bump version to 0.2.0"
git tag v0.2.0
git push origin main --tags

Both PyPI and Docker Hub are published automatically.


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nanodns-0.3.1.tar.gz (31.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nanodns-0.3.1-py3-none-any.whl (32.3 kB view details)

Uploaded Python 3

File details

Details for the file nanodns-0.3.1.tar.gz.

File metadata

  • Download URL: nanodns-0.3.1.tar.gz
  • Upload date:
  • Size: 31.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for nanodns-0.3.1.tar.gz
Algorithm Hash digest
SHA256 16daef6ad0251374f418eb4a0dbde15ba420726338a4202f5982a55b31ef5376
MD5 d33b3564289a0d0857e487a6c945bb18
BLAKE2b-256 444b8d751a5d9ccb1d73f66928181078120843c11c74ed3d39edec704c604efb

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanodns-0.3.1.tar.gz:

Publisher: release.yml on iyuangang/nanodns

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nanodns-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: nanodns-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for nanodns-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5281e376505cfc09aea34df68e30f71ddf4b9dd6c5484b2b7542be51cb6928fa
MD5 84d14a6aef739f93ad80d252e130ac12
BLAKE2b-256 7d706e05a2e45931f47e3d1b6470adcae5f4f354870a3e23b31cd6948c40fd20

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanodns-0.3.1-py3-none-any.whl:

Publisher: release.yml on iyuangang/nanodns

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page