Hostinger DNS Sync

A lightweight Docker container that watches Traefik router labels on your Docker host and automatically creates/updates Hostinger DNS records to point your subdomains at your server's public IP.

How it works

Docker event (container start/stop)
        │
        ▼
  Scan all containers for Traefik labels
  e.g. traefik.http.routers.myapp.rule=Host(`app.example.com`)
        │
        ▼
  Extract hostnames → filter by your DOMAIN
        │
        ▼
  Call Hostinger DNS API → upsert A records

On startup it does a full sync, then it listens to Docker events in real-time and re-syncs whenever a container starts or stops.


Prerequisites

  • Docker + Docker Compose
  • A domain on Hostinger with API access enabled
  • Your server's public IP reachable from the internet

Setup

1. Get your Hostinger API key

  1. Log in to hPanel
  2. Go to Profile → API Tokens
  3. Create a new token with DNS read/write permissions

2. Clone & configure

git clone <this-repo>
cd hostinger-dns-sync
cp .env.example .env
nano .env   # fill in HOSTINGER_API_KEY and DOMAIN

3. Deploy

docker compose up -d

Check logs:

docker compose logs -f hostinger-dns-sync

Environment variables

Variable Required Default Description
HOSTINGER_API_KEY API token from hPanel
DOMAIN Root domain, e.g. example.com
PUBLIC_IP auto-detected Override server IP
RECORD_TYPE A A (IPv4) or AAAA (IPv6)
TTL 3600 DNS TTL in seconds
DELETE_ORPHANS false Delete DNS records no longer in Traefik
DRY_RUN false Preview changes without applying

Traefik label format

The service reads all containers with a label matching:

traefik.http.routers.<name>.rule=Host(`subdomain.example.com`)

Multi-host rules are supported:

traefik.http.routers.<name>.rule=Host(`a.example.com`) || Host(`b.example.com`)

Orphan cleanup

By default, the service only adds/updates records and never deletes them. Set DELETE_ORPHANS=true to automatically remove DNS records for subdomains that are no longer referenced by any Traefik router.

⚠️ Use with care if you manage other records manually on the same domain.


Test without making changes

DRY_RUN=true docker compose up hostinger-dns-sync

Security notes

  • The Docker socket is mounted read-only (:ro) — the container cannot start/stop containers.
  • The API key is passed via environment variable, never baked into the image.
  • Add .env to your .gitignore.
Description
No description provided
Readme 39 KiB
Languages
Python 97.2%
Dockerfile 2.8%