A lightweight, self-hosted Dynamic DNS (DDNS) client for updating DNS records on Hostinger domains. Perfect for self-hosting enthusiasts who want to keep their domain's DNS pointing to a machine with a dynamic IP address.
Features:
- ✅ Automatic IP detection and DNS updates
- ✅ Prevents unnecessary API calls by caching last known IP
- ✅ Comprehensive logging for troubleshooting
- ✅ Easy configuration via
.envfile - ✅ Lightweight and battery-friendly (runs in seconds)
- ✅ Ideal for Raspberry Pi, NAS, and other always-on devices
- Prerequisites
- Installation
- Configuration
- Usage
- Setting Up Cron Job
- Troubleshooting
- How It Works
- Contributing
- License
- Python 3.9+ installed on your system
- Hostinger account with API access enabled
- Hostinger API token (see setup instructions below)
- Internet connection to access your Hostinger API
- A domain name hosted on Hostinger
git clone https://github.com/your-username/hostinger-ddns.git
cd hostinger-ddnsOr download the latest release:
wget https://github.com/your-username/hostinger-ddns/releases/download/v1.0.0/hostinger-ddns.tar.gz
tar xzf hostinger-ddns.tar.gz
cd hostinger-ddnspython3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activatepip install --upgrade pip
pip install -r requirements.txtAlternatively, install packages individually:
pip install requests hostinger-api python-dotenv- Log in to your hPanel at https://hpanel.hostinger.com
- Click on Profile (top right)
- Navigate to Account Information → API
- Generate a new API token
- Copy the token (you won't see it again!)
Copy the example configuration:
cp .env.example .envEdit .env with your details:
# Your Hostinger API token (keep this secret!)
API_TOKEN=your_token_here
# Your domain name on Hostinger
DOMAIN=example.com
# The subdomain (A record) to update (use @ for root domain)
SUBDOMAIN=vpn
# TTL in seconds (lower = faster propagation, higher = fewer API calls)
TTL=60
# Where to store logs (ensure directory is writable)
LOG_FILE=/home/username/hostinger-ddns-python.log
# Temporary file to cache last known IP
LAST_IP_FILE=/tmp/hostinger-ddns-python-last-ip.txt.env is in .gitignore and will never be committed to version control. Always keep your tokens private!
API_TOKEN=your_api_token_here
DOMAIN=myserver.com
SUBDOMAIN=raspberry
TTL=300
LOG_FILE=/var/log/hostinger-ddns/ddns.log
LAST_IP_FILE=/tmp/hostinger-ddns-last-ip.txtCreate separate clones of the repository:
hostinger-ddns/
├── hostinger-ddns-vpn/
│ ├── .env (SUBDOMAIN=vpn)
│ └── hostinger_ddns.py
└── hostinger-ddns-web/
├── .env (SUBDOMAIN=web)
└── hostinger_ddns.py
Run the script directly:
source venv/bin/activate
python hostinger_ddns.pyOutput: Check the log file specified in your .env for results.
tail -f /home/username/hostinger-ddns-python.logExample successful log:
[2025-10-27 22:00:01] INFO: Script started.
[2025-10-27 22:00:02] INFO: IP change detected. Current IP: 49.47.218.86, Last IP: None.
[2025-10-27 22:00:02] ACTION: Sending update request to Hostinger API for vpn.example.com -> 49.47.218.86
[2025-10-27 22:00:03] SUCCESS: Hostinger API accepted update for vpn.example.com to 49.47.218.86
[2025-10-27 22:00:03] INFO: Script finished. Update successful.
Edit your crontab:
crontab -eAdd one of these lines (choose based on your needs):
Every 5 minutes (most common):
*/5 * * * * cd /home/ashwath/hostinger_ddns && source venv/bin/activate && python hostinger_ddns.pyEvery 10 minutes:
*/10 * * * * cd /home/ashwath/hostinger_ddns && source venv/bin/activate && python hostinger_ddns.pyEvery hour:
0 * * * * cd /home/ashwath/hostinger_ddns && source venv/bin/activate && python hostinger_ddns.pyOn boot (after 2 minute delay):
@reboot sleep 120 && cd /home/ashwath/hostinger_ddns && source venv/bin/activate && python hostinger_ddns.pyConsult your NAS's documentation for task scheduling. Examples:
- Synology DSM: Control Panel → Task Scheduler
- QNAP NAS: System Settings → System Tools → Scheduled Tasks
- Unraid: Scheduled Jobs plugin
Create a simple cron container to run the script periodically.
Solution: Ensure dependencies are installed:
source venv/bin/activate
pip install -r requirements.txtSolution:
- Check that
.envfile exists in the script directory - Verify
API_TOKENis set with your real token (not placeholder) - Ensure
.envis readable by your user
ls -la .env
cat .env | grep API_TOKENSolution:
- Verify your API token is correct in
.env - Check if your token has expired (generate a new one)
- Ensure your Hostinger account has API access enabled
Solution:
- Verify
DOMAINexists on your Hostinger account - Check that
SUBDOMAINis correct (use@for root domain) - Ensure domain and subdomain names are lowercase
Solution:
- Create the log directory manually:
mkdir -p /var/log/hostinger-ddns sudo chown $USER:$USER /var/log/hostinger-ddns chmod 755 /var/log/hostinger-ddns
- Or change
LOG_FILEto a user-writable directory:LOG_FILE=/home/username/hostinger-ddns.log
Solution:
- Check if your IP is actually changing:
curl https://ifconfig.me
- If IP is stable, the issue is likely with
LAST_IP_FILEpermissions:rm /tmp/hostinger-ddns-python-last-ip.txt python hostinger_ddns.py
Add more verbose output by checking the full log:
tail -50 /home/username/hostinger-ddns-python.log | grep -i errorTo test that your cron job is working correctly, you can manually change the cached IP to force an update:
cat /tmp/hostinger-ddns-python-last-ip.txtrm /tmp/hostinger-ddns-python-last-ip.txtThis removes the cached IP file, forcing the script to update DNS on the next execution (even if IP hasn't changed).
echo "192.168.1.100" > /tmp/hostinger-ddns-python-last-ip.txtThis makes the script think the last IP was 192.168.1.100. When the cron job runs, it will detect a "change" and update your DNS.
# 1. Check current IP
curl https://ifconfig.me
# 2. Clear cache to force next update
rm /tmp/hostinger-ddns-python-last-ip.txt
# 3. Run script manually to see it update
source venv/bin/activate
python hostinger_ddns.py
# 4. Check the log to confirm success
tail -5 /home/username/hostinger-ddns-python.log
# 5. Verify the new IP is cached
cat /tmp/hostinger-ddns-python-last-ip.txtExpected Log Output on Update:
[2025-10-27 22:30:01] INFO: Script started.
[2025-10-27 22:30:02] INFO: IP change detected. Current IP: 49.47.218.86, Last IP: None.
[2025-10-27 22:30:02] ACTION: Sending update request to Hostinger API for vpn.example.com -> 49.47.218.86
[2025-10-27 22:30:03] SUCCESS: Hostinger API accepted update for vpn.example.com to 49.47.218.86
[2025-10-27 22:30:03] INFO: Script finished. Update successful.
┌─────────────────────────────────────────────────────┐
│ Cron Job Executes (every 5 minutes) │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 1. Fetch current public IP from ifconfig.me │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 2. Read last known IP from cache file │
└─────────────────────────────────────────────────────┘
↓
┌───────────────────────────────┐
│ IP Changed? │
└───────────────────────────────┘
↙ ↘
Yes No
↓ ↓
┌──────────────────┐ ┌──────────────────┐
│ Call Hostinger │ │ Log: No update │
│ API to update │ │ needed. Exit. │
│ DNS record │ └──────────────────┘
└──────────────────┘
↓
┌──────────────────┐
│ Success? │
└──────────────────┘
↙ ↘
Yes No
↓ ↓
┌──────┐ ┌──────────┐
│Save │ │Log error,│
│new IP│ │don't save│
│Exit 0│ │Exit 1 │
└──────┘ └──────────┘
Key Benefits:
- Efficient: Only calls API when IP changes
- Reliable: Comprehensive error logging
- Safe: Secret tokens kept in
.env, never committed - Lightweight: Runs in seconds, minimal resource usage
We welcome contributions from the self-hosting community!
- Fork the repository on GitHub
- Create a feature branch:
git checkout -b feature/your-feature-name
- Make your changes and test thoroughly
- Commit with clear messages:
git commit -m "Add feature: description of changes" - Push to your fork:
git push origin feature/your-feature-name
- Create a Pull Request with a description of changes
- Support for other DNS providers (Cloudflare, Linode, etc.)
- IPv6 support
- Systemd service file for automatic startup
- Docker image
- Configuration wizard for first-time setup
- Web UI for monitoring
- Support for multiple A records in one run
- Webhook notifications on IP change
This project is licensed under the MIT License - see LICENSE file for details.
MIT License
Copyright (c) 2025
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
- Issues: GitHub Issues for bug reports and feature requests
- Discussions: GitHub Discussions for questions and ideas
- Email: For private security concerns
- ✅ Initial release with Hostinger API v1 support
- ✅ Environment variable configuration via
.env - ✅ IP caching to reduce API calls
- ✅ Comprehensive logging
- ✅ Proper error handling and validation
- awesome-selfhosted - A list of Free Software network services and web applications
- r/selfhosted - Community for self-hosting enthusiasts
- Hostinger API Documentation
Happy self-hosting! 🚀