How to Self-Host Changedetection.io: Website Change Monitoring 2026
TL;DR
Changedetection.io (Apache 2.0, ~19K GitHub stars, Python) monitors websites for changes and alerts you when content changes — a free replacement for VisualPing or Distill.io. Use cases: price tracking (Amazon, Best Buy), stock monitoring, government/regulatory pages, job postings, sports schedules, and any site that changes but doesn't have RSS. Supports JavaScript-rendered pages via Playwright/Puppeteer. Distill.io Pro costs $14/month; Changedetection.io is free to self-host.
Key Takeaways
- Changedetection.io: Apache 2.0, ~19K stars — monitor any website for content changes
- JavaScript support: Optional Playwright/Puppeteer browser for SPAs and dynamic content
- CSS/XPath selectors: Monitor specific page elements (not the whole page)
- Notifications: Email, Slack, Discord, ntfy, Telegram, and 80+ via Apprise
- Filters: Ignore specific text patterns to reduce false positives
- Price tracking: Dedicated price change tracking with JSON XPath
Use Cases
| Use Case | Example |
|---|---|
| Price tracking | Amazon product → alert when price drops below $X |
| Stock availability | "Add to Cart" button appears → notify |
| Government updates | New regulations published |
| Job postings | New jobs at target company |
| Sports schedules | Game dates added or changed |
| Competitor monitoring | New features or pricing changes |
| News keywords | Alert when specific terms appear on a page |
| API monitoring | JSON response changes |
Part 1: Docker Setup
Basic (No JavaScript)
# docker-compose.yml
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- changedetection_data:/datastore
environment:
BASE_URL: "https://changes.yourdomain.com"
HIDE_REFERER: "false"
volumes:
changedetection_data:
With Playwright Browser (JavaScript Support)
For monitoring SPAs, dynamic pages, and JavaScript-rendered content:
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- changedetection_data:/datastore
environment:
BASE_URL: "https://changes.yourdomain.com"
PLAYWRIGHT_DRIVER_URL: ws://sockpuppetbrowser:3000
depends_on:
- sockpuppetbrowser
sockpuppetbrowser:
image: dgtlmoon/sockpuppetbrowser:latest
container_name: sockpuppetbrowser
restart: unless-stopped
cap_add:
- SYS_ADMIN
environment:
SCREEN_WIDTH: 1920
SCREEN_HEIGHT: 1080
volumes:
changedetection_data:
docker compose up -d
Visit http://your-server:5000 — no login required by default. Add password in settings.
Part 2: HTTPS with Caddy
changes.yourdomain.com {
reverse_proxy localhost:5000
}
Part 3: Adding Your First Watch
- + Add Watch
- Paste URL (e.g.,
https://www.amazon.com/dp/B09R9CTNC5) - Set check interval: 1 hour (default), 15 min, or custom
- Save
Changedetection immediately fetches the page and stores a baseline. Next check compares against the baseline — any difference triggers an alert.
Part 4: CSS Selectors (Monitor Specific Elements)
Instead of monitoring the full page (with noisy navigation/ads changes), scope to specific content:
- Edit a watch → Visual Filter
- Click on the element to monitor (e.g., price, availability text)
- Changedetection generates a CSS selector automatically
Or manually set CSS selectors:
/* Monitor only the price: */
#priceblock_dealprice, #priceblock_ourprice, .a-price-whole
/* Monitor availability: */
#availability .a-declarative
/* Monitor a table: */
table.data-table
/* Monitor a specific div: */
div.job-listings-container
Part 5: Price Tracking
Changedetection has dedicated price tracking with threshold alerts:
- Add a watch for a product URL
- Edit → Filters → Price tracking
- Enable Price Change Detection
- Set alert threshold: "Alert when price drops below $50"
For JSON APIs:
URL: https://api.example.com/product/123
Filter: json:data.price # JSONPath to price field
Part 6: Notification Setup
Settings → Notifications → Add Notification
ntfy
URL: ntfy://ntfy.yourdomain.com/changedetection
Slack
URL: slack://your-token@YOUR-WORKSPACE/YOUR-CHANNEL
Discord
URL: discord://webhook-id/webhook-token
URL: smtp://user:password@smtp.yourdomain.com:587?to=you@yourdomain.com&from=cd@yourdomain.com
Changedetection uses Apprise — any Apprise-compatible notification URL works.
Per-Watch Notifications
Each watch can have its own notification settings:
- Edit watch → Notifications tab
- Override global settings
- Useful for routing: "Price changes → SMS, government pages → email"
Part 7: Filters and Ignore Rules
Reduce false positives from dynamic content (ads, timestamps, visitor counts):
Edit watch → Filters & Triggers:
# Ignore lines containing these strings (regex supported):
visitor counter
last updated
Advertisement
# Trigger only when page contains this text:
Trigger text contains: "In Stock"
# Trigger only when text is removed:
Trigger text removed: "Out of Stock"
Ignore regions (visual selector):
- Edit watch → Visual Filter
- Toggle to "Ignore" mode
- Click regions to exclude (e.g., the ad sidebar)
Part 8: Browser Steps (Login-Protected Pages)
Monitor pages that require login using Playwright:
- Edit watch → Browser Steps
- Add steps:
Navigate to https://example.com/loginFill input[name=email] with you@example.comFill input[name=password] with your-passwordClick button[type=submit]Wait for element .dashboard
- Save — Changedetection now logs in before checking
Part 9: Tags and Groups
Organize watches with tags:
Tags: amazon-prices, job-boards, government
Filter dashboard by tag. Bulk-apply notification settings to a tag group.
Part 10: API
# List all watches:
curl https://changes.yourdomain.com/api/v1/watch \
-H "x-api-key: your-api-key"
# Add a watch:
curl -X POST https://changes.yourdomain.com/api/v1/watch \
-H "x-api-key: your-api-key" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com","tag":"monitoring","title":"Example Site"}'
# Get change history:
curl "https://changes.yourdomain.com/api/v1/watch/UUID/history" \
-H "x-api-key: your-api-key"
# Generate API key: Settings → API Access Token
Maintenance
# Update:
docker compose pull
docker compose up -d
# Logs:
docker compose logs -f changedetection
# Backup:
tar -czf changedetection-backup-$(date +%Y%m%d).tar.gz \
$(docker volume inspect changedetection_changedetection_data --format '{{.Mountpoint}}')
# Purge old history (saves disk space):
# Settings → Data → Purge old history
See all open source monitoring and automation tools at OSSAlt.com/categories/monitoring.