TetherDNS is a self-hosted, enterprise-ready web application for managing Cloudflare DNS records and automating Dynamic DNS (DDNS) updates. It turns complex network configurations into effortless control, all wrapped in a premium Ocean Deep Tech UI.
- ✨ Features
- 🏗️ Architecture
- ⚡ Quick Start
- ⚙️ Configuration
- 🚀 First-Time Setup
- 📖 Usage Guide
- 🔗 Webhook API
- 🛠️ Scripts Reference
- 🔒 Security
- 📜 License
| Feature | Description |
|---|---|
| Secure Setup Wizard | A one-time initialization flow to create your admin credentials on first launch |
| TOTP 2FA | Industry-standard Time-based One-Time Password (via otplib) protects the admin console with QR-code scan setup |
| bcrypt Password Hashing | All passwords are stored using battle-tested bcryptjs hashing — never in plaintext |
| Encrypted Sessions | HTTP-only, server-side sessions encrypted with a 32+ character secret key |
| Configurable Cookie Security | Toggle SESSION_SECURE to enforce HTTPS-only cookie transport in production |
| Auth Middleware | All protected routes are guarded server-side via Nitro middleware |
| Feature | Description |
|---|---|
| Multi-Account Vault | Add and manage multiple Cloudflare API accounts from a single dashboard |
| Zone Explorer | Browse all DNS zones across accounts with instant search and pagination |
| Full Record CRUD | Create, read, update, and delete DNS records: A, AAAA, CNAME, TXT, MX, SRV |
| Proxy Toggle | Enable or disable Cloudflare's proxy (Orange Cloud ☁️) per record with one click |
| TTL Control | Adjust Time-To-Live per record; supports 1 (Auto) and all standard TTL values |
| Feature | Description |
|---|---|
| Auto IP Detection | Built-in cron job checks and updates your public IP at a configurable interval (default: every 5 minutes) |
| Per-Record DDNS | Enable DDNS on any A or AAAA record individually |
| Webhook Endpoints | Generate unique, signed webhook URLs to trigger DDNS updates from routers, scripts, or automation tools |
| IPv4 & IPv6 Support | Detects and updates both IPv4 (A) and IPv6 (AAAA) records independently |
| Feature | Description |
|---|---|
| IP History Charts | Interactive ApexCharts visualizations tracking your IP change history over time |
| Real-Time Audit Log | Immutable, timestamped log of every login, logout, config change, and DDNS update |
| DDNS Event Log | Dedicated chronological view of all DDNS update events with IP before/after values |
| Feature | Description |
|---|---|
| Ocean Deep Tech Theme | Glassmorphism-inspired dark UI with deep indigo tones, designed to minimize eye strain |
| Responsive Design | Fully responsive from 4K desktop down to mobile screens |
| i18n Support | Full internationalization with English 🇬🇧 and Thai 🇹🇭 language support (@nuxtjs/i18n) |
graph TD
subgraph "Client Layer"
Browser["🌐 Browser (Vue 3 + Nuxt UI)"]
end
subgraph "Server Layer (Nitro)"
Middleware["🛡️ Auth Middleware"]
API["📡 REST API Endpoints"]
Session["🔑 Session Manager"]
Cron["⏱️ DDNS Cron Job"]
end
subgraph "Service Layer"
CFService["☁️ Cloudflare SDK"]
IPService["📍 IP Detection Service"]
end
subgraph "Data Layer"
Prisma["🔷 Prisma ORM"]
SQLite[("🗄️ SQLite / LibSQL")]
end
Browser -->|"HTTP Request"| Middleware
Middleware -->|"Authenticated"| API
API --> Session
API --> CFService
API --> Prisma
Cron --> IPService
Cron --> CFService
Cron --> Prisma
CFService -->|"Cloudflare REST API v4"| Cloudflare["☁️ Cloudflare"]
Prisma --> SQLite
| Layer | Technology |
|---|---|
| Frontend Framework | Nuxt 4 + Vue 3 (Composition API) |
| UI Component Library | @nuxt/ui v4 + Tailwind CSS |
| Server Engine | Nitro (built into Nuxt) |
| Database ORM | Prisma v7 with @prisma/adapter-libsql |
| Database | SQLite (via LibSQL) — zero-dependency, file-based |
| Cloudflare Client | Official cloudflare TypeScript SDK v5 |
| Authentication | nuxt-auth-utils session + bcryptjs + otplib (TOTP 2FA) |
| Charts | ApexCharts + vue3-apexcharts |
| i18n | @nuxtjs/i18n v10 |
| Icons | heroicons + lucide via @nuxt/icon |
The fastest way to get TetherDNS running in production. Requires Docker and Docker Compose.
Step 1 — Clone the repository
git clone https://github.com/riiixch/TetherDNS.git
cd TetherDNSStep 2 — Configure your environment
# Copy the example file
cp .env.example .env
# Edit the file and set your own SESSION_PASSWORD (must be 32+ characters!)
# See the Configuration section below for full details
⚠️ Important: TheSESSION_PASSWORDvariable must be at least 32 characters. A short password will cause a500error on every request.
Step 3 — Build and start the container
docker compose up -d --buildStep 4 — Access the app
Open your browser and navigate to: http://localhost:3000
You will be redirected to the Setup Wizard on first launch.
To update to a new version:
git pull
docker compose down
docker compose up -d --buildTo view live logs:
docker compose logs -f tetherdnsTo stop:
docker compose downFor contributors and developers who want to extend or customize TetherDNS.
Prerequisites:
- Node.js v20 or higher
- npm v10 or higher
Step 1 — Clone and install dependencies
git clone https://github.com/riiixch/TetherDNS.git
cd TetherDNS
npm installStep 2 — Configure environment variables
cp .env.example .env
# Edit .env with your configurationStep 3 — Initialize the database
npx prisma db pushStep 4 — Start the development server
npm run devThe dev server is available at: http://localhost:3000 with hot module replacement (HMR) enabled.
All configuration is done through environment variables. For Docker deployments, set them in docker-compose.yml. For local development, use a .env file.
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
✅ Yes | file:./tetherdns.db |
Path to the SQLite database file. For Docker, use an absolute path like file:/app/data/tetherdns.db to persist data via a volume mount. |
SESSION_PASSWORD |
✅ Yes | (none) | Must be 32+ characters. A strong, random secret used to encrypt session cookies. Generate one with openssl rand -base64 48. |
SESSION_SECURE |
✅ Yes | false |
Set to true to enforce HTTPS-only cookies. Always true in production with HTTPS. Set to false for HTTP or local development. |
NODE_ENV |
✅ Yes | development |
Set to production for deployed instances. |
TZ |
No | System default | Timezone for log timestamps (e.g., Asia/Bangkok, America/New_York). |
PORT |
No | 3000 |
The port the server listens on. |
HOST |
No | 0.0.0.0 |
The host address to bind to. |
# Using openssl (Linux/macOS/WSL)
openssl rand -base64 48
# Using Node.js
node -e "console.log(require('crypto').randomBytes(48).toString('base64'))"
# Using PowerShell (Windows)
[System.Convert]::ToBase64String((1..48 | ForEach-Object { [byte](Get-Random -Max 256) }))services:
tetherdns:
image: tetherdns:latest
container_name: tetherdns
restart: always
volumes:
- ./data:/app/data # Persists the database outside the container
environment:
- DATABASE_URL=file:/app/data/tetherdns.db
- SESSION_PASSWORD=your-super-secret-key-min-32-characters-long
- SESSION_SECURE=false # Set to true if using HTTPS
- NODE_ENV=production
- TZ=Asia/Bangkok
ports:
- "3000:3000"💡 Tip: The
./datavolume mount ensures your database file survives container restarts and upgrades.
When you launch TetherDNS for the first time with an empty database, you will automatically be redirected to the Setup Wizard at /setup.
1. Create Admin Account
- Enter a username and a strong password for your admin account.
- This is the primary account for accessing the TetherDNS dashboard.
2. (Optional) Enable Two-Factor Authentication
- After setup, navigate to Settings (⚙️) → Security.
- Click Enable 2FA to generate a QR code.
- Scan the QR code with your authenticator app (e.g., Google Authenticator, Authy).
- Enter the 6-digit TOTP code to confirm and activate 2FA.
Once setup is complete, you will be redirected to the Login page.
The Accounts page is where you manage your Cloudflare API credentials.
Adding a Cloudflare Account:
- Navigate to the Accounts tab.
- Click + Add Account.
- Enter a friendly Name for the account (e.g., "Personal Cloudflare").
- Enter your Cloudflare API Token.
- Go to Cloudflare Dashboard → My Profile → API Tokens.
- Create a token with Zone:DNS:Edit and Zone:Zone:Read permissions.
- Click Save. TetherDNS will validate the token against the Cloudflare API.
Managing Accounts:
- Edit: Update the account name or API token at any time.
- Delete: Remove an account. This will also remove all associated zone and DDNS configurations from TetherDNS (but will not delete anything from Cloudflare).
The Zones page lists all DNS zones (domains) found across all your configured Cloudflare accounts.
- Search: Use the search bar to filter zones by domain name.
- Pagination: Navigate through large lists of zones.
- Select a Zone: Click on any zone to open its DNS record management page.
Inside a zone, you can view and manage all its DNS records.
Supported Record Types: A, AAAA, CNAME, TXT, MX, SRV
Adding a Record:
- Click the + Add Record button.
- Select the Type (A, AAAA, CNAME, etc.).
- Fill in the Name (e.g.,
@for root,www,mail). - Fill in the Content (e.g., IP address for
Arecords, hostname forCNAME). - Set the TTL (Time To Live). Use
1for "Auto". - Toggle Proxied (☁️) if you want Cloudflare's proxy.
- Click Save.
Editing a Record:
- Click the ✏️ Edit icon on any record row.
- Modify the fields and click Update.
Deleting a Record:
- Click the 🗑️ Delete icon on a record row.
- Confirm the deletion in the dialog.
⚠️ Warning: Deletions are sent immediately to the Cloudflare API and cannot be undone through TetherDNS.
DDNS automatically keeps your DNS records updated with your current public IP address — essential for home servers or any device with a dynamic IP.
How It Works:
- A background cron job runs every 5 minutes (configurable).
- It detects your current public IPv4 and IPv6 addresses.
- Any
AorAAAArecord with DDNS enabled is automatically updated in Cloudflare if the IP has changed.
Enabling DDNS on a Record:
- Open a zone and find an
AorAAAArecord. - Toggle the DDNS switch on that record.
- TetherDNS will track and update this record automatically.
Webhook-triggered DDNS: For instant updates without waiting for the cron interval, use the generated Webhook URL. See the Webhook API section below.
Audit Log (/audit)
A comprehensive, real-time log of all user and system actions:
- Admin logins and logouts
- Account additions, edits, and deletions
- DNS record changes (create, update, delete)
- DDNS cron job events and IP change notifications
DDNS Log (/logs)
A focused view specifically for DDNS-related events, showing the timestamp, record affected, previous IP, and new IP for every automated update.
Accessing Settings: Click the ⚙️ icon in the navigation bar.
Profile Settings:
- Change your admin username and password.
Two-Factor Authentication (2FA):
| State | Action |
|---|---|
| Disabled | Click Enable 2FA → scan the QR code with your authenticator app → enter the 6-digit code to confirm |
| Enabled | Click Disable 2FA → enter your current 2FA code to confirm deactivation |
🔑 Backup: When 2FA is enabled, save your setup key in a secure location. If you lose access to your authenticator app, you will need direct database access to recover.
Language:
- Use the 🌐 language switcher in the navigation bar to toggle between English and Thai (ภาษาไทย).
TetherDNS exposes a webhook endpoint that triggers an immediate DDNS update for all enabled records. This is ideal for routers that support custom DDNS scripts.
Endpoint:
GET /api/webhook/ddns?token=<YOUR_WEBHOOK_TOKEN>
How to get your token:
- Navigate to Settings → Webhook.
- Copy the generated token (or regenerate it if needed).
Usage Examples:
# Trigger an update using curl
curl "http://your-server:3000/api/webhook/ddns?token=YOUR_TOKEN_HERE"# Use in a cron job on another server
*/10 * * * * /usr/bin/curl -s "http://your-server:3000/api/webhook/ddns?token=YOUR_TOKEN_HERE"Router Configuration (DD-WRT / OpnSense / pfSense):
- Set the custom DDNS provider URL to:
http://your-server:3000/api/webhook/ddns?token=YOUR_TOKEN_HERE
Response:
{ "success": true, "updated": 2 }All scripts are run with npm run <script>.
| Script | Command | Description |
|---|---|---|
dev |
nuxt dev |
Start the development server with HMR |
build |
nuxt build |
Build the application for production |
preview |
nuxt preview |
Preview the production build locally |
generate |
nuxt generate |
Generate a static version of the app |
prisma:gen |
npx prisma generate |
Regenerate the Prisma client after schema changes |
prisma:push |
npx prisma db push |
Push the Prisma schema to the database (auto-creates tables) |
prisma:reset |
npx prisma migrate reset |
|
update:check |
npx npm-check-updates |
Check for available dependency updates |
update:install |
npx npm-check-updates -u && npm install |
Apply all dependency updates |
TetherDNS is designed with security as a first-class concern:
- No credentials stored in plaintext. All passwords are hashed with
bcryptjs. - Session encryption. Sessions are encrypted with a user-provided secret key (min. 32 chars) using
iron-webcrypto. A weak or missing key causes a hard failure on startup. - HTTP-only cookies. Session cookies are inaccessible to client-side JavaScript, mitigating XSS attacks.
- Server-side auth middleware. Every protected API route and page is validated server-side before any data is returned.
- API token isolation. Your Cloudflare API tokens are stored in the local database and are never exposed to the frontend.
- TOTP 2FA. An optional but highly recommended second factor protects against credential theft.
Security Recommendations for Production:
- Always use
SESSION_SECURE=truebehind an HTTPS reverse proxy (e.g., Nginx, Caddy, Traefik). - Use a randomly generated
SESSION_PASSWORDof at least 48 characters. - Enable TOTP 2FA immediately after setup.
- Restrict access to port
3000via your firewall or reverse proxy — do not expose it directly to the public internet. - Use a Cloudflare API Token with the minimum required permissions, not your Global API Key.
Distributed under the MIT License. See LICENSE for full details.
Built with uncompromising passion by RIIIXCH