Automatically updates your Docker host's /etc/hosts file with running container hostnames and IPs, including FQDNs
using base.domain as the domain base.
- ✅ Real-time monitoring: Watches Docker events (start, stop, connect, disconnect, etc.)
- ✅ Periodic updates: Fallback periodic sync to catch any missed events
- ✅ FQDN support: Creates entries like
container.base.domain - ✅ Multi-network support: Handles containers connected to multiple networks
- ✅ Thread-safe: Uses asyncio locks to prevent race conditions
- ✅ Graceful shutdown: Handles SIGTERM and SIGINT properly
- ✅ Comprehensive logging: Detailed logging with proper log levels
- ✅ Error handling: Robust error handling throughout
- Python 3.12+
- Docker installed and running
- Root/sudo access (to modify
/etc/hosts)
# Make the script executable
chmod +x docker_hosts_updater.py
# Install as a system service (recommended)
sudo cp docker_hosts_updater.py /usr/local/bin/
sudo cp docker-hosts-updater.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable docker-hosts-updater
sudo systemctl start docker-hosts-updater# Update every 30 seconds (default domain: base.domain)
sudo python docker_hosts_updater.py 30s
# Update every 5 minutes with custom domain
sudo python docker_hosts_updater.py 5m --domain example.com
# Update every 1 hour with custom domain (short form)
sudo python docker_hosts_updater.py 1h -d local.dev
# Show help
python docker_hosts_updater.py --helps- seconds (e.g.,30s)m- minutes (e.g.,5m)h- hours (e.g.,1h)d- days (e.g.,1d)
You can configure the base domain for FQDNs in three ways (in order of priority):
-
Command line argument:
--domainor-dsudo python docker_hosts_updater.py 30s --domain example.com
-
Environment variable:
DOCKER_HOSTS_DOMAINexport DOCKER_HOSTS_DOMAIN=example.com sudo -E python docker_hosts_updater.py 30s -
Default:
base.domain(if neither above is specified)
The script manages a dedicated section in /etc/hosts:
# BEGIN DOCKER CONTAINERS
172.18.0.2 nginx nginx.base.domain web web.base.domain
172.18.0.3 postgres postgres.base.domain db db.base.domain
172.18.0.4 redis redis.base.domain cache cache.base.domain
# END DOCKER CONTAINERS
For each running container, the script adds entries for:
- Container name: The Docker container name
- Hostname: The container's configured hostname (if different)
- Network aliases: Any aliases defined in the network configuration
- FQDNs: All of the above with
.base.domainappended
Container configuration:
services:
web:
image: nginx
hostname: webserver
networks:
frontend:
aliases:
- nginx
- wwwResulting /etc/hosts entries:
172.18.0.2 web web.base.domain webserver webserver.base.domain nginx nginx.base.domain www www.base.domain
Create /etc/systemd/system/docker-hosts-updater.service:
[Unit]
Description=Docker Hosts File Updater
After=docker.service
Requires=docker.service
[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/local/bin/docker_hosts_updater.py 30s
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetService management:
# Start the service
sudo systemctl start docker-hosts-updater
# Check status
sudo systemctl status docker-hosts-updater
# View logs
sudo journalctl -u docker-hosts-updater -f
# Stop the service
sudo systemctl stop docker-hosts-updaterPre-built container images are available at ghcr.io/newdave/docker-hostsfile.
# Using default settings (30s interval, base.domain)
docker run -d \
--name docker-hosts-updater \
--network host \
--cap-add NET_ADMIN \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /etc/hosts:/etc/hosts \
ghcr.io/newdave/docker-hostsfile:latest
# With custom interval and domain
docker run -d \
--name docker-hosts-updater \
--network host \
--cap-add NET_ADMIN \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /etc/hosts:/etc/hosts \
ghcr.io/newdave/docker-hostsfile:latest 5m --domain example.com
# Using environment variable for domain
docker run -d \
--name docker-hosts-updater \
--network host \
--cap-add NET_ADMIN \
-e DOCKER_HOSTS_DOMAIN=local.dev \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /etc/hosts:/etc/hosts \
ghcr.io/newdave/docker-hostsfile:latest 1hversion: '3.8'
services:
hosts-updater:
image: ghcr.io/newdave/docker-hostsfile:latest
container_name: docker-hosts-updater
restart: unless-stopped
network_mode: host
command: ["30s", "--domain", "base.domain"]
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /etc/hosts:/etc/hosts
cap_add:
- NET_ADMIN
environment:
- PYTHONUNBUFFERED=1
- DOCKER_HOSTS_DOMAIN=base.domain
mem_limit: 256m
cpus: 0.5See docker-compose.example.yml for more configuration examples.
latest- Latest build from main branchmain- Latest build from main branchv1.0.0- Specific version (semver)1.0- Major.minor version1- Major versionmain-<sha>- Specific commit SHA
Requirements:
- Access to Docker socket (read-only recommended)
- Mount
/etc/hostsfrom host NET_ADMINcapability or run as privileged- Host network mode recommended
Edit the script and modify:
DOMAIN_BASE = 'base.domain' # Change to your desired domainEdit the monitor_docker_events() function:
events_to_monitor = [
'start', 'stop', 'die', 'kill',
'pause', 'unpause',
'connect', 'disconnect'
]The script needs root privileges to modify /etc/hosts:
sudo python docker_hosts_updater.py 30sCheck Docker is running:
docker infoCheck that containers have:
- Network connections with valid IPs
- At least one of: container name, hostname, or network alias
Debug by checking logs:
# When running as systemd service
sudo journalctl -u docker-hosts-updater -f
# When running manually (set DEBUG level)
# Edit script and change: level=logging.DEBUGThe script automatically deduplicates entries per IP address. If you see duplicates:
- Stop the script
- Manually remove duplicates from
/etc/hosts - Ensure only one instance of the script is running
# Create test containers
docker run -d --name test-nginx --hostname webserver nginx
docker run -d --name test-redis --hostname cache redis
# Check /etc/hosts
grep -A 10 "BEGIN DOCKER CONTAINERS" /etc/hosts
# Should show:
# 172.17.0.2 test-nginx test-nginx.base.domain webserver webserver.base.domain
# 172.17.0.3 test-redis test-redis.base.domain cache cache.base.domain- FQDN Support: Uses
base.domaindomain base as requested - Better Event Monitoring: Monitors all relevant Docker events (start, stop, die, kill, pause, unpause, connect, disconnect)
- Enhanced Error Handling: Comprehensive try/except blocks with proper logging
- Thread Safety: Uses asyncio locks to prevent concurrent writes
- Initial Update: Updates hosts file immediately on startup
- Signal Handling: Properly handles both SIGTERM and SIGINT
- Docker Validation: Checks Docker availability before starting
- Better Logging: Uses Python logging module with timestamps and levels
- Multi-Network Support: Correctly handles containers on multiple networks
- Container Names: Uses actual container names and hostnames, not just aliases
- JSON Event Parsing: Properly parses Docker events as JSON
- Security: Avoids
shell=Truewhere possible - Deduplication: Ensures no duplicate entries per IP
- Type Hints: Added type hints for better code clarity
- Documentation: Comprehensive docstrings and comments
name: Deploy Hosts Updater
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
sudo systemctl stop docker-hosts-updater
sudo cp docker_hosts_updater.py /usr/local/bin/
sudo systemctl start docker-hosts-updater
sudo systemctl status docker-hosts-updaterExport metrics from logs using a sidecar pattern or integrate with:
- Prometheus node_exporter
- Vector.dev for log aggregation
- Grafana Loki for log monitoring
MIT
Pull requests welcome! See CONTRIBUTING.md for detailed guidelines.
# Set up development environment
make dev-setup
# Format code
make format
# Run linting
make lint
# Run pre-commit checks
make pre-commitThis project uses pre-commit hooks to ensure code quality:
- Python: black, isort, flake8, pylint, mypy, bandit
- Docker: hadolint for Dockerfile linting
- GitHub Actions: actionlint for workflow validation
- Markdown: markdownlint
- Shell: shellcheck
Hooks run automatically on git commit. To run manually:
pre-commit run --all-filesPlease ensure:
- Code follows PEP 8 style guidelines (120 char line length)
- All functions have type hints and docstrings
- Error handling is comprehensive
- Changes are tested with multiple Docker network configurations