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

Uploaded Python 3

File details

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

File metadata

  • Download URL: nanodns-0.2.11.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.11.tar.gz
Algorithm Hash digest
SHA256 18f61929d133bbf6b62eafb00e59eaf42a51cd557910fa76e9731e03c7a89fe4
MD5 fd9d9814704d1d8fa19a4ede794911e1
BLAKE2b-256 6794950b3075534fe2333e19fdbe07b090d5fb007d1b868de6cc2e4ddcfa1e09

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: nanodns-0.2.11-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.11-py3-none-any.whl
Algorithm Hash digest
SHA256 6e1ad5345cfa151178ec4db5d7780eed0ad69c1e4018e26690100639e2154ced
MD5 5998c9c01b3ae8cc52605549a29a203e
BLAKE2b-256 56bb41c4c06f0e8c8710b1d8f313e100d4330df4477f9dd959b3c5e41ff04095

See more details on using hashes here.

Provenance

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