changedetection.io Deployment and Usage Guide
1. Prerequisites
Runtime Requirements:
- Python 3.8 or higher
- Node.js (for optional Playwright support)
- Modern web browser for accessing the web interface
Database/Storage:
- Local filesystem access for datastore (default:
/datastore) - Optional: External database for scaling (though current implementation uses JSON files)
Optional Dependencies:
- Docker and Docker Compose (recommended for production)
- Playwright for advanced browser automation features
- Redis for queue management (if scaling beyond single instance)
Accounts/Services (Optional):
- Notification service accounts (Discord, Slack, Telegram, Email SMTP, etc.)
- Proxy service accounts if monitoring rate-limited sites
2. Installation
Option A: Docker (Recommended)
# Create a directory for persistent storage
mkdir datastore
# Run with Docker
docker run -d \
--name changedetection \
-p 5000:5000 \
-v $(pwd)/datastore:/datastore \
dgtlmoon/changedetection.io
Option B: Docker Compose
version: '3'
services:
changedetection:
image: dgtlmoon/changedetection.io
ports:
- "5000:5000"
volumes:
- ./datastore:/datastore
environment:
- PLAYWRIGHT_DRIVER_URL=ws://playwright:3000
- BASE_URL=http://localhost:5000
depends_on:
- playwright
playwright:
image: browserless/chrome:latest
ports:
- "3000:3000"
docker-compose up -d
Option C: Python Source Installation
# Clone the repository
git clone https://github.com/dgtlmoon/changedetection.io.git
cd changedetection.io
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Install Playwright browsers (optional)
playwright install chromium
# Install Node.js dependencies for frontend (if needed)
npm install # or yarn install
3. Configuration
Environment Variables
Core Settings:
# Base URL for notification links (required for proper notifications)
BASE_URL=http://your-server.com:5000
# Datastore location
DATASTORE_PATH=/datastore
# Minimum recheck time (seconds)
MINIMUM_SECONDS_RECHECK_TIME=3
# Playwright integration
PLAYWRIGHT_DRIVER_URL=ws://localhost:3000
PLAYWRIGHT_TIMEOUT=30000
# Security
SECRET_KEY=your-secret-key-here
BLOCK_SIMPLEHOSTS=False # Set to True to block localhost/private IP monitoring
Notification Configuration:
# Email notifications
NOTIFICATION_URLS=smtp://user:pass@smtp.server:587/?to=recipient@example.com
# Webhook notifications
NOTIFICATION_URLS=https://discord.com/api/webhooks/your-webhook-url
NOTIFICATION_URLS=https://hooks.slack.com/services/your-webhook-url
# Multiple notifications (comma separated)
NOTIFICATION_URLS=mailto:admin@example.com,https://webhook.url
Performance Tuning:
# Worker settings
WORKER_MAX_JOBS=10
WORKER_MAX_RUNTIME=3600
# Snapshot compression
SNAPSHOT_BROTLI_COMPRESSION_THRESHOLD=20480 # 20KB threshold
# Request settings
HTTP_PROXY=http://your-proxy:8080
HTTPS_PROXY=http://your-proxy:8080
NO_PROXY=localhost,127.0.0.1
Configuration Files
The application stores configuration in JSON files within the datastore:
changedetection.json- Global settings{watch_uuid}/watch.json- Individual watch configurations{tag_uuid}/tag.json- Tag configurations
Watch Configuration Examples
Basic Watch:
{
"url": "https://example.com/product",
"tag": "monitoring",
"time_between_check": {"minutes": 30},
"fetch_backend": "system"
}
Advanced Watch with Filters:
{
"url": "https://api.example.com/data",
"method": "GET",
"ignore_text": ["ADVERTISEMENT", "Sponsored"],
"css_filter": ".product-price",
"notification_urls": ["https://discord.com/api/webhooks/..."],
"restock": {
"track": true,
"notification_body": "Price changed to {{price}}"
}
}
4. Build & Run
Development Mode
# From source directory
python changedetection.py
# Or using Flask directly
export FLASK_APP=changedetection.py
export FLASK_ENV=development
flask run --host=0.0.0.0 --port=5000
Production Mode
# Using Gunicorn (recommended for production)
pip install gunicorn
gunicorn --bind 0.0.0.0:5000 --workers 4 changedetection:app
# With environment variables
DATASTORE_PATH=/datastore \
BASE_URL=https://your-domain.com \
gunicorn --bind 0.0.0.0:5000 changedetection:app
Building Docker Image
# Build from source
docker build -t changedetection-custom .
# Run custom build
docker run -d \
-p 5000:5000 \
-v /path/to/datastore:/datastore \
changedetection-custom
5. Deployment
Platform Recommendations
1. Docker-based Platforms:
- Docker Swarm/Kubernetes: For high availability
- Portainer: Easy Docker management
- Cloud Run/ECS/Fargate: Managed container services
2. Traditional VPS:
- Ubuntu/Debian with systemd:
# Create systemd service file: /etc/systemd/system/changedetection.service
[Unit]
Description=changedetection.io
After=network.target
[Service]
Type=simple
User=changedetection
WorkingDirectory=/opt/changedetection.io
Environment="DATASTORE_PATH=/var/lib/changedetection"
Environment="BASE_URL=https://your-domain.com"
ExecStart=/opt/changedetection.io/venv/bin/gunicorn --bind 0.0.0.0:5000 changedetection:app
Restart=always
[Install]
WantedBy=multi-user.target
3. Reverse Proxy Setup (Nginx):
server {
listen 80;
server_name changedetection.your-domain.com;
location / {
proxy_pass http://localhost:5000;
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;
}
}
4. Cloud Platforms:
- Railway.app: One-click deployment with persistent storage
- Fly.io: Global edge deployment
- Hetzner/OVH: Affordable VPS with good storage options
Scaling Considerations
Single Instance:
- Suitable for up to 1000 watches
- Uses in-memory queue system
- File-based JSON storage
Multi-Instance:
- Requires external queue (Redis)
- Shared filesystem or database for datastore
- Load balancer with sticky sessions
# Example with Redis queue
docker run -d \
--name changedetection \
-e REDIS_URL=redis://redis-server:6379 \
-e DATASTORE_PATH=/datastore \
-v shared-storage:/datastore \
dgtlmoon/changedetection.io
6. Troubleshooting
Common Issues
1. "Base URL not set" in notifications:
# Set the BASE_URL environment variable
export BASE_URL=https://your-actual-domain.com
# Or in Docker:
docker run -e BASE_URL=https://your-domain.com ...
2. Playwright not working:
# Ensure Playwright service is running
docker run -d -p 3000:3000 browserless/chrome:latest
# Set the connection URL
export PLAYWRIGHT_DRIVER_URL=ws://localhost:3000
3. Watch not detecting changes:
- Check if page requires JavaScript (enable Playwright)
- Verify CSS/XPATH filters are correct
- Check for anti-bot protections (may need proxies)
- Enable "Treat text as text" for JSON/API responses
4. High memory usage:
# Reduce snapshot retention
export SNAPSHOT_BROTLI_COMPRESSION_THRESHOLD=10240
# Limit number of snapshots per watch in settings
# Increase check intervals for less frequent monitoring
5. Database/schema migration issues:
# The application includes automatic schema migrations
# Check logs for update messages
# Backups are automatically created in /datastore as .tar.gz files
# To restore: tar -xzf before-update-{N}-{timestamp}.tar.gz -C /datastore
6. Permission errors (Docker):
# Set proper permissions on host directory
chown -R 1000:1000 /path/to/datastore
# Or use named volumes
docker volume create changedetection-data
7. Notifications not sending:
- Verify notification URLs are correct
- Check SMTP credentials for email
- Test webhook URLs with curl
- Check application logs for notification errors
Logging and Debugging
# Enable verbose logging
export LOG_LEVEL=DEBUG
# Check application logs
docker logs changedetection
# Monitor specific watch
# In UI: Watch → History → View processing details
# Test fetch manually
curl -X POST http://localhost:5000/api/v1/watch/{uuid}/check
Performance Optimization
For large deployments (>500 watches):
# Increase worker count
export WORKER_COUNT=4
# Use external Redis for queue
export REDIS_URL=redis://localhost:6379
# Adjust check intervals
# Use global default settings for consistency
# Consider dedicated instance for Playwright-heavy watches
Storage optimization:
- Enable Brotli compression (default for snapshots >20KB)
- Regularly prune old snapshots via settings
- Consider mounting
/datastoreon SSD storage