A self-hostable, open-source DuckDNS alternative
Lightweight dynamic DNS service that lets you manage your DNS records without manual overhead.
Features β’ Quick Start β’ Documentation β’ API Reference β’ Contributing
Dyno is a self-hosted dynamic DNS service that eliminates the manual overhead of managing DNS records. Bring your own domain and Cloudflare tokens, and let Dyno handle the rest. Perfect for home labs, development environments, and self-hosted infrastructure.
- π Self-Hosted: Complete control over your DNS data
- π Secure: Token-based authentication for all operations
- π Fast: Built with Go for optimal performance
- π API-First: Comprehensive RESTful API for CLI/script automation
- π¦ Easy Setup: Docker support with minimal configuration
- π Cloudflare Integration: Currently supports Cloudflare DNS (more providers coming soon)
- πΎ Persistent Storage: PostgreSQL database for reliability
π Detailed Documentation: Visit docs.dyno.rndmcode.in for comprehensive guides, tutorials, and API documentation.
- Dynamic DNS Updates: Automatically update your DNS records when your IP changes
- Multi-User Support: User authentication and authorization system
- RESTful API: Full-featured API for programmatic access
- Domain Management: Create, read, update, and delete DNS records
- IPv4 Support: Automatic IP detection and DNS record updates
- CLI-Friendly: Simple GET endpoints for easy curl-based updates
- Dashboard Ready: API designed for web dashboard integration
- Docker Support: Easy deployment with Docker and Docker Compose
- CI/CD Pipeline: Automated builds and deployments with GitHub Actions
- Go 1.24+ (for local development)
- PostgreSQL 12+
- Docker & Docker Compose (recommended)
- Cloudflare Account with API token
- Your own domain managed by Cloudflare
-
Pull the Docker image:
docker pull ghcr.io/rndmcodeguy20/dyno:production
-
Create a
.envfile:# Server Configuration ENV=production HOST=0.0.0.0 PORT=5010 # Database Configuration DB_HOST=postgres DB_PORT=5432 DB_USER=dyno DB_PASSWORD=your_secure_password DB_NAME=dyno_db # Cloudflare Configuration PROVIDER_API_KEY=your_cloudflare_api_token PROVIDER_EMAIL=your_cloudflare_email DOMAIN=yourdomain.com
-
Create a
docker-compose.yml:version: '3.8' services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: dyno_db volumes: - postgres_data:/var/lib/postgresql/data - ./db/migrations:/docker-entrypoint-initdb.d ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 dyno: image: ghcr.io/rndmcodeguy20/dyno:latest env_file: - .env ports: - "5010:5010" depends_on: postgres: condition: service_healthy restart: unless-stopped volumes: postgres_data:
-
Start the services:
docker-compose up -d
-
Clone the repository:
git clone https://github.com/rndmcodeguy20/dyno.git cd dyno -
Install dependencies:
go mod download
-
Set up PostgreSQL:
# Run the migration scripts in db/migrations/ psql -U postgres -f db/migrations/001_seed.sql psql -U postgres -d dyno_db -f db/migrations/002_unique_constraints_domain.sql psql -U postgres -d dyno_db -f db/migrations/003_add_dns_record_ids.sql psql -U postgres -d dyno_db -f db/migrations/004_add_token_hash_users.sql -
Configure environment variables:
cp .env.example .env # Edit .env with your configuration -
Build and run:
go build -o dyno ./cmd/server/main.go ./dyno
| Variable | Description | Required | Default |
|---|---|---|---|
ENV |
Environment (development/staging/production) | Yes | - |
HOST |
Server host address | No | 0.0.0.0 |
PORT |
Server port | No | 5010 |
DB_HOST |
PostgreSQL host | Yes | - |
DB_PORT |
PostgreSQL port | No | 5432 |
DB_USER |
Database username | Yes | - |
DB_PASSWORD |
Database password | Yes | - |
DB_NAME |
Database name | Yes | - |
PROVIDER_API_KEY |
Cloudflare API token | Yes | - |
PROVIDER_EMAIL |
Cloudflare account email | Yes | - |
DOMAIN |
Your domain name | Yes | - |
- Log in to your Cloudflare Dashboard
- Go to My Profile β API Tokens
- Click Create Token
- Use the Edit zone DNS template
- Configure permissions:
- Zone β DNS β Edit
- Zone Resources β Include β Specific zone β your domain
- Copy the generated token to
PROVIDER_API_KEY
http://localhost:5010/api/v1
All protected endpoints require a token in the Authorization header:
Authorization: Bearer <your_token>
POST /api/v1/auth/signup
Content-Type: application/json
{
"username": "johndoe",
"email": "john@example.com",
"password": "securepassword"
}POST /api/v1/auth/login
Content-Type: application/json
{
"username": "johndoe",
"password": "securepassword"
}Response:
{
"success": true,
"data": {
"token": "your_auth_token",
"user": {
"id": 1,
"username": "johndoe",
"email": "john@example.com"
}
}
}GET /api/v1/domains
Authorization: Bearer <token>POST /api/v1/domain
Authorization: Bearer <token>
Content-Type: application/json
{
"domain_name": "home.yourdomain.com",
"current_ip_v4": "203.0.113.10"
}# Automatic IP detection
curl "http://localhost:5010/api/v1/domain/update?domain=home.yourdomain.com&token=your_token"
# Explicit IP
curl "http://localhost:5010/api/v1/domain/update?domain=home.yourdomain.com&ip=203.0.113.10&token=your_token"PUT /api/v1/domain/{domainId}
Authorization: Bearer <token>
Content-Type: application/json
{
"current_ip_v4": "203.0.113.20"
}DELETE /api/v1/domain/{domainId}
Authorization: Bearer <token>GET /api/v1/user/profile
Authorization: Bearer <token>Create a script to update your IP automatically:
#!/bin/bash
# update-ip.sh
TOKEN="your_auth_token"
DOMAIN="home.yourdomain.com"
DYNO_URL="http://localhost:5010"
curl -s "${DYNO_URL}/api/v1/domain/update?domain=${DOMAIN}&token=${TOKEN}"Add to crontab to run every 5 minutes:
*/5 * * * * /path/to/update-ip.shimport requests
DYNO_URL = "http://localhost:5010/api/v1"
TOKEN = "your_auth_token"
headers = {"Authorization": f"Bearer {TOKEN}"}
# List domains
response = requests.get(f"{DYNO_URL}/domains", headers=headers)
print(response.json())
# Update domain
response = requests.get(
f"{DYNO_URL}/domain/update",
params={"domain": "home.yourdomain.com"},
headers=headers
)
print(response.json())$token = "your_auth_token"
$domain = "home.yourdomain.com"
$dynoUrl = "http://localhost:5010"
$headers = @{
"Authorization" = "Bearer $token"
}
Invoke-RestMethod -Uri "$dynoUrl/api/v1/domain/update?domain=$domain" -Headers $headers- Language: Go 1.24
- Web Framework: Chi Router
- Database: PostgreSQL with sqlx
- DNS Provider SDK: Cloudflare Go SDK v6
- Logging: Uber Zap
- Authentication: Token-based (bcrypt hashing)
dyno/
βββ cmd/
β βββ server/ # Application entry point
βββ internal/
β βββ config/ # Configuration management
β βββ database/ # Database connection
β βββ handler/ # HTTP handlers
β βββ middleware/ # Custom middleware
β βββ models/ # Data models
β βββ repository/ # Data access layer
β βββ router/ # Route definitions
β βββ server/ # HTTP server setup
β βββ service/ # Business logic
βββ pkg/
β βββ errors/ # Error handling utilities
β βββ utils/ # Common utilities
βββ db/
β βββ migrations/ # Database migrations
βββ build/ # Compiled binaries
βββ docker-compose.yml # Docker setup
- Password Hashing: Bcrypt with configurable cost
- Token Authentication: Secure token-based auth system
- Input Validation: Request validation on all endpoints
- SQL Injection Protection: Parameterized queries with sqlx
- Rate Limiting: Configurable via middleware
- HTTPS Ready: Deploy behind reverse proxy (Nginx/Traefik)
- Use strong passwords for database and user accounts
- Keep API tokens secure - never commit them to version control
- Run behind a reverse proxy with HTTPS in production
- Regularly update dependencies and Docker images
- Limit database access to the application only
- Use firewall rules to restrict access to sensitive ports
Nginx Configuration Example:
server {
listen 80;
server_name dns.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name dns.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:5010;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}The project supports multiple environments via GitHub Actions:
- staging: Deployed on push to
stagingbranch - production: Deployed on push to
masterbranch
Docker images are tagged with:
- Semantic version (e.g.,
0.0.1) - Commit SHA (e.g.,
0.0.1-abc1234) - Environment tag (
stagingorproduction)
- Go 1.24+
- PostgreSQL 12+
- Make (optional, for taskfile)
-
Clone and enter directory:
git clone https://github.com/rndmcodeguy20/dyno.git cd dyno -
Install dependencies:
go mod download
-
Set up local database:
# Start PostgreSQL (via Docker) docker run -d \ --name dyno-postgres \ -e POSTGRES_PASSWORD=postgres \ -p 5432:5432 \ postgres:16-alpine # Run migrations psql -h localhost -U postgres -f db/migrations/001_seed.sql
-
Create
.envfile:cp .env.example .env # Edit with your local configuration -
Run the application:
go run cmd/server/main.go
# Build for current platform
go build -o dyno ./cmd/server/main.go
# Build for Linux
GOOS=linux GOARCH=amd64 go build -o dyno-linux ./cmd/server/main.go
# Build with version info
go build -ldflags="-X 'main.Version=0.0.1'" -o dyno ./cmd/server/main.go# Run tests
go test ./...
# Run tests with coverage
go test -cover ./...
# Run tests with verbose output
go test -v ./...- β Cloudflare DNS integration
- β User authentication and authorization
- β Domain CRUD operations
- β Automatic IP detection
- β RESTful API
- β Docker support
- β CI/CD pipeline
- Additional DNS Providers
- Route53 (AWS)
- Google Cloud DNS
- DigitalOcean DNS
- Namecheap
- IPv6 Support
- Web Dashboard
- User-friendly interface
- Real-time DNS status
- Domain management UI
- Advanced Features
- Webhook notifications
- Multi-domain support per user
- DNS record history/audit log
- API rate limiting per user
- Email notifications
- Monitoring
- Prometheus metrics
- Health check endpoints
- Status page
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
- Follow Go best practices and conventions
- Write tests for new features
- Update documentation as needed
- Keep commits atomic and well-described
- Ensure CI/CD pipeline passes
This project follows standard Go conventions:
- Use
gofmtfor formatting - Run
go vetfor static analysis - Follow Effective Go guidelines
Please use GitHub Issues to report bugs or request features:
- Bug Report: Describe the issue, steps to reproduce, and expected behavior
- Feature Request: Describe the feature and its use case
This project is licensed under the MIT License - see the LICENSE file for details.
Shantanu Mane
- Email: hi@rndmcode.in
- GitHub: @rndmcodeguy20
- Inspired by DuckDNS
- Built with Go
- Uses Cloudflare Go SDK
- Powered by Chi Router
If you find this project helpful, please consider:
- β Starring the repository
- π Reporting bugs
- π‘ Suggesting new features
- π€ Contributing code
- π’ Sharing with others
For questions and support, please open an issue on GitHub.
Built with β€οΈ by the community
