Watchdoc is a production-ready agent that watches your running containers and keeps them on the freshest image that matches your rules—without brittle per-app configuration. Watchdoc supports Docker Hub, AWS ECR, and Google Container Registry, and the default experience relies on container labels instead of hard-coded compose paths.
- Install Watchdoc on the host.
- Add a couple of labels to any container you want auto-updated.
- Watchdoc discovers, tracks, and refreshes those containers automatically.
No more manual configuration! Just add labels to your containers and Watchdoc takes it from there. Most registry details are optional—Watchdoc infers the provider from the image host (*.amazonaws.com → ECR, gcr.io → GCR, everything else defaults to Docker Hub) and falls back to credentials in /etc/watchdoc/.env or the instance profile. Add labels only when you need overrides:
services:
my-app:
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/example-service:staging-latest
labels:
- "watchdoc.enable=true"✅ No sudo required - Uses Docker API directly
✅ Zero configuration - Automatic container discovery
✅ Works anywhere - No compose file path needed
See the Label-Based Discovery Reference below for more examples.
- 🎯 Label-Based Auto-Discovery: Automatically discover and monitor containers with labels (default behavior)
- Multi-Registry Support: Docker Hub, AWS ECR, Google Container Registry
- Dynamic Tag Patterns: Support for
staging-*,prod-*style tags with automatic latest selection - Semantic Versioning: Intelligent semver parsing and comparison (
v1.2.3,release-2.0.0) - Smart Tag Detection: Auto-identifies semver or prefix-based patterns when no labels are provided and tracks the freshest image automatically
- Docker Compose Aware: Detects compose-managed services via Docker's built-in labels and refreshes them with
docker compose—no file edits or sudo tricks required - Automatic Service Restart: Direct Docker API restart (no sudo needed!)
- State Persistence: Tracks current tags and digests to avoid redundant updates
- Systemd Integration: Native systemd service with automatic startup and logging
- Security: Dedicated user, minimal privileges, secure credential management
- Health Checks: Docker connectivity and service health monitoring
- Production Ready: Robust error handling, comprehensive logging, monitoring support
- Clone and Install:
git clone <repository-url>
cd watchdoc
sudo ./install.shThe systemd service runs as the non-root user that invoked
sudo ./install.sh, so it inherits the same access to compose files,.env, and other resources. Watchdoc rewrites the discovered compose YAML with the new image tag before callingdocker compose. Ensure the install user has write access to the project; otherwise the service falls back to container recreation. The systemd unit disablesProtectHomeso/homeremains writable—compose projects under the install user’s home directory continue to work.
- Label Your Containers:
# docker-compose.yml
services:
watchdoc-demo:
image: nginx:latest
labels:
- "watchdoc.enable=true"
# Optional registry hints, tag patterns, etc.
# - "watchdoc.registry=ecr"
# - "watchdoc.tag-pattern=staging-*"- Start the Service:
sudo systemctl enable watchdoc
sudo systemctl start watchdoc
sudo systemctl status watchdoc- Install Dependencies:
python3 -m venv watchdoc-env
source watchdoc-env/bin/activate
pip install -r requirements.txt- Configure and Run:
cat <<'EOF' > .env
LOG_LEVEL=INFO
AUTO_DISCOVERY=true
# Add registry credentials here if needed
EOF
python watchdoc.py# AWS ECR Configuration
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_DEFAULT_REGION=us-east-2
# Google Container Registry
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
GCP_PROJECT_ID=your-project-id
# Docker Hub
DOCKER_HUB_USERNAME=your_username
DOCKER_HUB_PASSWORD=your_password
# Application Settings
LOG_LEVEL=INFO
CHECK_INTERVAL=30watchdoc_config.json only controls global settings such as check_interval. You no longer need to list individual applications—labels on the containers themselves drive discovery.
# 1. Update volume mounts in docker-compose.yml
# 2. Start the service
docker-compose up -d
# View logs
docker-compose logs -fOnce the containers are running, Watchdoc automatically recognises them via Docker's compose labels and will run
docker compose up -d --no-deps <service>after pulling a newer image. No manual file editing or sudo configuration required.
Watchdoc stores its Docker credentials under /var/lib/watchdoc/docker-config (override with DOCKER_CONFIG) so registry logins succeed even for non-root service accounts.
# 1. Copy files to /opt/watchdoc
sudo cp -r . /opt/watchdoc
### Start/Stop Service
```bash
# Start service
sudo systemctl start watchdoc
# Stop service
sudo systemctl stop watchdoc
# Restart service
sudo systemctl restart watchdoc
# Enable auto-start on boot
sudo systemctl enable watchdoc# Check service status
sudo systemctl status watchdoc
# View real-time logs
sudo journalctl -u watchdoc -f
# View recent logs
sudo journalctl -u watchdoc -n 50# Test Docker access
sudo -u "$USER" docker ps
# Validate configuration
python3 -c "import json; print('Valid JSON' if json.load(open('/etc/watchdoc/watchdoc_config.json')) else 'Invalid')"
# Test registry connectivity
sudo -u "$USER" python3 /opt/watchdoc/watchdoc.py --test# Install dependencies
pip install -r requirements.txt
# Run directly
python3 watchdoc.py- Docker Socket Access: The updater requires access to the Docker socket
- Non-root User: Container runs as non-root user (UID 1000)
- Read-only Mounts: Configuration files are mounted read-only
- Resource Limits: Configure appropriate CPU/memory limits in production
watchdoc.log: Application logswatchdoc_state.json: Persistent state (image digests, last update times)
The Docker container includes health checks that verify Docker connectivity.
The application logs structured information suitable for:
- Prometheus metrics collection
- ELK stack integration
- Grafana dashboards
/etc/watchdoc/watchdoc_config.json
{
"check_interval": 60
}Watchdoc analyses the tags that already exist in your registry and automatically chooses the freshest image when no tag-related labels are set:
- If tags follow semantic versioning, Watchdoc promotes the highest version.
- If tags share a prefix (e.g.
staging-abc123), Watchdoc tracks the newest push that matches the current prefix. - Otherwise, Watchdoc falls back to the image with the most recent push timestamp.
Add watchdoc.tag-pattern or watchdoc.semver-pattern only when you need to override this automatic behaviour.
Watchdoc auto-detects tag strategies (semver, prefix, or latest push) when no label is supplied. Add a label only when you need to pin the behaviour to a specific pattern:
services:
accounting:
image: 285065797661.dkr.ecr.us-east-2.amazonaws.com/accounting:staging-abc123
labels:
- "watchdoc.enable=true"
- "watchdoc.registry=ecr"
- "watchdoc.ecr.region=us-east-2"
- "watchdoc.tag-pattern=staging-*"Watchdoc already detects semver tags automatically. Use the label below if you want to enforce a custom pattern (for example, ignore prerelease tags):
services:
release-api:
image: myregistry/api:v1.2.3
labels:
- "watchdoc.enable=true"
- "watchdoc.registry=ecr"
- "watchdoc.semver-pattern=v*"- Docker Hub: Public and private repositories
- AWS ECR: With IAM role or access key authentication
- Google Container Registry: With service account authentication
Built-in monitoring:
- Docker daemon connectivity
- Registry accessibility
- Service status verification
-
Permission Denied on Docker Socket
# Add user to docker group sudo usermod -aG docker $USER
-
Docker Compose Not Found
# Install docker-compose sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
-
Image Digest Not Found
- Ensure the image exists in the registry
- Check network connectivity to the registry
- Verify image tag is correct
Enable debug logging by modifying the logging level in watchdoc.py:
logging.basicConfig(level=logging.DEBUG, ...)- CPU: 0.1-0.5 cores
- Memory: 128-512 MB
- Storage: 100 MB for logs and state
- Use Docker Compose with restart policies
- Configure log rotation
- Set up monitoring alerts
- Regular backup of state files
- Network isolation using Docker networks
For critical environments:
- Run multiple instances with different check intervals
- Use external state storage (Redis/Database)
- Implement leader election for coordination
The updater can be extended with a REST API for:
- Manual trigger updates
- Status monitoring
- Configuration management
- Webhook integration
MIT License - see LICENSE file for details.
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
For issues and questions:
- Check the troubleshooting section
- Review application logs
- Open an issue with detailed information
Watchdoc supports automatic container discovery using Docker labels. This eliminates the need to manually configure compose paths and avoids editing YAML files during updates.
- Add labels to your services in
docker-compose.ymlorcompose.yml. - Watchdoc automatically discovers and monitors these containers.
- Updates happen via the Docker API or
docker compose(no sudo required). - No static configuration files are needed.
version: '3.8'
services:
my-app:
image: nginx:latest
labels:
- "watchdoc.enable=true"services:
accounting:
image: 285065797661.dkr.ecr.us-east-2.amazonaws.com/accounting:staging-latest
labels:
- "watchdoc.enable=true"
- "watchdoc.registry=ecr"
- "watchdoc.ecr.region=us-east-2"
- "watchdoc.ecr.access-key-id=${AWS_ACCESS_KEY_ID}"
- "watchdoc.ecr.secret-access-key=${AWS_SECRET_ACCESS_KEY}"| Label | Description | Example |
|---|---|---|
watchdoc.enable |
Enable auto-update for this container | true |
watchdoc.registry |
Registry type (auto-detected from image host when omitted) | docker_hub, ecr, gcr |
.env alongside compose files |
Automatically merged for compose restarts | DATABASE_URL=... |
watchdoc.tag-pattern |
Override auto-detected prefixes | staging-* |
watchdoc.semver-pattern |
Override auto-detected semver | v* |
watchdoc.ecr.region |
AWS region | us-east-2 |
watchdoc.ecr.access-key-id |
AWS access key | ${AWS_ACCESS_KEY_ID} |
watchdoc.ecr.secret-access-key |
AWS secret key | ${AWS_SECRET_ACCESS_KEY} |
watchdoc.gcr.project-id |
GCP project ID | my-project |
watchdoc.gcr.service-account-path |
Service account JSON path | /path/to/sa.json |
watchdoc.dockerhub.username |
Docker Hub username | ${DOCKER_HUB_USERNAME} |
watchdoc.dockerhub.password |
Docker Hub password | ${DOCKER_HUB_PASSWORD} |
Leave tag-related labels blank to let Watchdoc auto-detect the smartest strategy. Registry type is inferred from the image host (e.g.,
*.amazonaws.com→ ECR), and credentials fall back to environment variables in/etc/watchdoc/.envor instance metadata if not provided via labels.
- Inspect labels:
docker inspect <container> | grep -A 10 Labels - Verify container is running:
docker ps | grep <container> - Confirm auto-discovery:
grep AUTO_DISCOVERY /etc/watchdoc/.env
Watchdoc analyses the tags already present in your registry and automatically chooses the freshest image.
- Labels enable the container and provide any registry credentials.
- Each scan downloads tag metadata (semver, prefix-based, or push timestamp).
- When a newer tag is detected, Watchdoc pulls the image and restarts the service.
services:
accounting:
image: 285065797661.dkr.ecr.us-east-2.amazonaws.com/accounting:staging-latest
labels:
- "watchdoc.enable=true"
- "watchdoc.registry=ecr"
- "watchdoc.ecr.region=us-east-2"
- "watchdoc.ecr.access-key-id=${AWS_ACCESS_KEY_ID}"
- "watchdoc.ecr.secret-access-key=${AWS_SECRET_ACCESS_KEY}"Need tighter control? Add watchdoc.tag-pattern or watchdoc.semver-pattern to pin the behaviour.
Logging cues
INFO - Auto-detected latest tag for accounting: staging-a1b2c3d (strategy: prefix:staging-)
INFO - Pulling image: 285065797661.dkr.ecr.us-east-2.amazonaws.com/accounting:staging-a1b2c3d
INFO - Restarting compose service accounting (project: accounting)
INFO - Successfully refreshed compose service: accounting
Common hiccups
WARNING - No tags found ...→ verify the repository contains matching tags.ERROR - Failed to authenticate ...→ check credentials/region labels.ERROR - docker compose failed ...→ confirm labels include compose metadata and the files are accessible.
aws ecr create-repository --repository-name my-app --region us-east-1- Grant permissions using
ecr:GetAuthorizationToken,ecr:DescribeImages, etc. - Labels:
labels: - "watchdoc.enable=true" - "watchdoc.registry=ecr" - "watchdoc.ecr.region=us-east-1" - "watchdoc.ecr.access-key-id=${AWS_ACCESS_KEY_ID}" # optional with IAM role - "watchdoc.ecr.secret-access-key=${AWS_SECRET_ACCESS_KEY}" # optional
- Optional env vars: export
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_DEFAULT_REGION.
gcloud services enable containerregistry.googleapis.com- Create a service account and key.
- Labels:
labels: - "watchdoc.enable=true" - "watchdoc.registry=gcr" - "watchdoc.gcr.project-id=YOUR_PROJECT_ID" - "watchdoc.gcr.service-account-path=/secrets/watchdoc-key.json"
- Optional env vars:
GOOGLE_APPLICATION_CREDENTIALS,GOOGLE_CLOUD_PROJECT.
- Create a read-only access token.
- Labels:
labels: - "watchdoc.enable=true" - "watchdoc.registry=docker_hub" - "watchdoc.dockerhub.username=${DOCKER_HUB_USERNAME}" - "watchdoc.dockerhub.password=${DOCKER_HUB_TOKEN}"
- Optional env vars:
DOCKER_HUB_USERNAME,DOCKER_HUB_TOKEN.
| Registry | Label | Env fallback |
|---|---|---|
| ECR | watchdoc.ecr.access-key-id |
AWS_ACCESS_KEY_ID |
| ECR | watchdoc.ecr.secret-access-key |
AWS_SECRET_ACCESS_KEY |
| ECR | watchdoc.ecr.region |
AWS_DEFAULT_REGION |
| GCR | watchdoc.gcr.service-account-path |
GOOGLE_APPLICATION_CREDENTIALS |
| GCR | watchdoc.gcr.project-id |
GOOGLE_CLOUD_PROJECT |
| Docker Hub | watchdoc.dockerhub.username |
DOCKER_HUB_USERNAME |
| Docker Hub | watchdoc.dockerhub.password |
DOCKER_HUB_PASSWORD / DOCKER_HUB_TOKEN |
Problem: sudo: a password is required when updating compose-managed services.
Solution: Add Watchdoc labels, keep compose metadata intact, and let Watchdoc run docker compose up -d --no-deps <service> automatically.
services:
accounting:
image: 285065797661.dkr.ecr.us-east-2.amazonaws.com/accounting:staging-latest
labels:
- "watchdoc.enable=true"
- "watchdoc.registry=ecr"
- "watchdoc.tag-pattern=staging-*" # Optional override
- "watchdoc.ecr.region=us-east-2"
- "watchdoc.ecr.access-key-id=${AWS_ACCESS_KEY_ID}"
- "watchdoc.ecr.secret-access-key=${AWS_SECRET_ACCESS_KEY}"Skip
watchdoc.tag-patternif Watchdoc’s automatic tag detection works for you.
After updating labels:
docker-compose up -d
sudo systemctl restart watchdoc
sudo journalctl -u watchdoc -fYou should see messages about auto-discovery and compose restarts instead of permission errors.
Optional cleanup:
- Remove legacy entries from
/etc/watchdoc/watchdoc_config.json(onlycheck_intervalis required). - Toggle discovery via
/etc/watchdoc/.envif needed (AUTO_DISCOVERY=false).