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

Uploaded Python 3

File details

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

File metadata

  • Download URL: nanodns-0.2.10.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.2.10.tar.gz
Algorithm Hash digest
SHA256 f933acecd355826c6ac260c2e88348474b91826122c95400ee7a337d17da1206
MD5 057804bdeae3a742a01841f0a55c80f4
BLAKE2b-256 c2075ba42fecef8a059f4c09b51b9d60fce849e1eb3975b34b0ea01ed085393d

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: nanodns-0.2.10-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.2.10-py3-none-any.whl
Algorithm Hash digest
SHA256 84b08380940bf4f08b6f2a5a76e146ea087ca70a2d873ac71789873132a12967
MD5 1e90eda3fbe21a7ab37f160c310014de
BLAKE2b-256 151b9beb25ff78b15e22df4c3825480d117e6be5a30c7711f47d17cbb0db448d

See more details on using hashes here.

Provenance

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