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.3.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.3-py3-none-any.whl (32.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nanodns-0.3.3.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.3.tar.gz
Algorithm Hash digest
SHA256 90b81bc010977197198a65133260351978cc56ea78de70d4103384af32cb9756
MD5 56801c9bf5087d2a692542ac2dd3b021
BLAKE2b-256 ab35fcfeea4208c536c42d00daa234ef45d1e6bfa368ed85cc9b2616a2da5850

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanodns-0.3.3.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.3-py3-none-any.whl.

File metadata

  • Download URL: nanodns-0.3.3-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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 820528ee09e0956755ae611d946912b87e4acdd1eb345edb1ea1d45bada0a472
MD5 58929708c0e85f0f0e990558068b17af
BLAKE2b-256 8594cae53b209201e537d9f598bbcd44619ddb59b1d68314b183402c0807fd86

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanodns-0.3.3-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