Skip to content

long2ice/dns-failover

Repository files navigation

DNS Failover

Automatic DNS failover driven by health checks. When the primary IP becomes unreachable, the DNS record is swapped to a backup IP — and swapped back automatically when the primary recovers.

Features

  • Multiple DNS providers: Cloudflare, Huawei Cloud DNS (extensible)
  • Multiple check methods: ICMP ping, TCP ping, HTTP(S)
  • Failure / recovery thresholds: only flip after N consecutive failures and recover after M consecutive successes
  • Primary / backup failover: each record carries a primary and an optional backup IP; a state machine drives the switch
  • DNS change audit: every add / switch / remove is persisted and viewable in the UI
  • Realtime updates: SSE pushes status changes to the browser, plus optional Telegram notifications
  • Full management UI: Vite + React + shadcn/ui, embedded into a single binary
  • Auth: JWT in an httpOnly cookie; the first visit prompts you to create the admin account

Stack

  • Backend: Go + Gin + ent ORM + PostgreSQL
  • Frontend: Vite + React + TypeScript + Tailwind + shadcn/ui
  • Realtime: Server-Sent Events
  • Deployment: single binary (frontend embedded via go:embed)

Quick Start

Requires Go 1.22+, Node 18+, PostgreSQL.

# 1. Create the database
createdb dns_failover

# 2. Configure
cp .env.example .env
# Edit .env: DATABASE_URL, and (optionally) TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID

# 3. Build the frontend
cd web && npm install && npm run build && cd ..

# 4. Run
go run ./cmd/server
# Open http://localhost:8080 — the first visit walks you through admin setup.

Production build:

make build      # Produces a single ./dns-failover binary with the frontend embedded
./dns-failover

Docker

An image tagged latest is published to Docker Hub on every push to main: long2ice/dns-failover.

# Easiest: docker compose brings up the app and a PostgreSQL service together
cp .env.example .env
docker compose up -d
# Open http://localhost:8080

# Or run the app container alone, pointing at your own Postgres:
docker run -d \
  --name dns-failover \
  -p 8080:8080 \
  --sysctl net.ipv4.ping_group_range="0 2147483647" \
  -e DATABASE_URL="postgres://user:pass@host:5432/dns_failover?sslmode=disable" \
  long2ice/dns-failover:latest

The ping_group_range sysctl lets the container open unprivileged ICMP sockets — required for the ping checker. Drop it if you only use tcping / http.

Configuration

Configure via a .env file or real environment variables (real env vars take precedence):

Variable Default Description
DATABASE_URL postgres://postgres:postgres@localhost:5432/dns_failover?sslmode=disable PostgreSQL connection string
ADDR :8080 HTTP listen address
GIN_MODE release Gin mode (debug / release / test)
TELEGRAM_BOT_TOKEN Telegram bot token (optional)
TELEGRAM_CHAT_ID Telegram target chat ID (optional)

Architecture

┌─────────────────┐
│  React (Vite)   │  ← SSE / REST
└────────┬────────┘
         │
┌────────▼────────────────────────────────────┐
│  Gin HTTP server                            │
│  ├── /api/*        REST handlers            │
│  └── /api/events   SSE                      │
└────────┬───────────────┬────────────────────┘
         │               │
   ┌─────▼──────┐  ┌─────▼────────┐
   │  Monitor   │  │  Notifier    │
   │  Manager   │  │  (Telegram)  │
   │  (workers) │  │              │
   └─────┬──────┘  └─────▲────────┘
         │               │
         │   ┌───────────┴─────────┐
         │   │  Event Hub (in-mem) │ ← pub/sub
         │   └─────────────────────┘
         │
   ┌─────▼──────────────┐    ┌──────────────┐
   │  Checkers          │    │ DNS Provider │
   │  ping/tcping/http  │    │ Cloudflare   │
   │                    │    │ Huawei       │
   └────────────────────┘    └──────────────┘
                ↕                    ↕
         ┌──────▼────────────────────▼──────┐
         │      PostgreSQL (via ent)        │
         └──────────────────────────────────┘

Every enabled monitor runs in its own goroutine with its own ticker. On a status change the worker calls applyRecordState, which diff-syncs each bound DNS record against the target provider.

Roadmap

v0.3 — Production-readiness

  • Multi-vantage checks: a single check point produces false positives when its own network blips. Add lightweight agent nodes that report check results, and only flip when M of N agents agree
  • Encrypted credentials at rest: providers.credentials is currently plaintext JSON in Postgres; encrypt it with AES using an ENCRYPTION_KEY env var
  • Generic webhook notifications: ship a webhook channel alongside Telegram so users can wire Slack, WeCom, Feishu, DingTalk, Bark, etc.

v0.5 — Ecosystem

  • More DNS providers: Aliyun, Route53, DNSPod, Google Cloud DNS
  • More check types: DNS resolution check, TLS certificate expiry, custom shell scripts
  • Tags / grouping and search filters on monitors and records

v0.6 — Multi-user

  • API tokens (for CI/CD and scripts)
  • Audit log: who changed what and when
  • Role-based access (read-only / operator / admin)

Project Structure

.
├── cmd/server/          # entry point
├── internal/
│   ├── api/             # HTTP handlers (Gin)
│   ├── checker/         # ping / tcping / http
│   ├── config/          # .env / env vars
│   ├── dns/             # provider adapters (Cloudflare, Huawei)
│   ├── model/           # API/store shared model
│   ├── monitor/         # background worker + event hub
│   ├── notifier/        # Telegram push
│   └── store/           # ent wrapper + converters
├── ent/
│   └── schema/          # ent schemas (run `go generate ./ent` to regenerate)
└── web/                 # Vite + React frontend
    ├── src/             # source
    └── dist/            # build output (go:embed)

License

MIT

About

DNS failover with health checks (Cloudflare, Huawei Cloud)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors