Changedetection.io Website Monitoring

Changedetection.io is a self-hosted tool that monitors websites and web pages for changes, sending alerts when content updates are detected. With CSS/XPath filtering, JavaScript rendering support, and multiple notification channels, it's ideal for monitoring competitor prices, government pages, job listings, and any site that doesn't offer an RSS feed.

Prerequisites

  • Docker and Docker Compose installed
  • At least 512 MB RAM (more if using browser rendering)
  • A domain name for web access (optional)

Docker Installation

# Create project directory
mkdir -p /opt/changedetection && cd /opt/changedetection

# Create docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3'

services:
  changedetection:
    image: ghcr.io/dgtlmoon/changedetection.io:latest
    container_name: changedetection
    hostname: changedetection
    volumes:
      - changedetection_data:/datastore
    environment:
      PORT: 5000
      PUID: 1000
      PGID: 1000
      BASE_URL: https://cd.yourdomain.com
      # Optional: timezone
      TZ: UTC
    ports:
      - "5000:5000"
    restart: unless-stopped

  # Optional: Playwright/Chromium for JavaScript-heavy pages
  playwright-chrome:
    image: browserless/chrome:latest
    container_name: playwright-chrome
    restart: unless-stopped
    environment:
      SCREEN_WIDTH: 1920
      SCREEN_HEIGHT: 1024
      DEFAULT_LAUNCH_ARGS: '["--window-size=1920,1080"]'
    ports:
      - "3000:3000"

volumes:
  changedetection_data:
EOF

docker compose up -d

# Check it's running
docker compose ps
docker compose logs changedetection --tail 20

Access the UI at http://your-server:5000.

Set a Password

By default, Changedetection has no authentication. Set a password immediately:

# Option 1: Set via environment variable
# Add to docker-compose.yml environment section:
#   USE_X_SETTINGS: "True"

# Option 2: In the web UI:
# Settings → General → Enable "Password" protection

Or add to your compose file:

environment:
  HTTP_X_API_KEY: "your-api-key-here"

Watch Configuration

Add Your First Watch

  1. Enter a URL in the top input field
  2. Click Watch
  3. Configure check interval in Settings → General → Check interval

Key watch settings per URL:

  • Check interval: How often to check (default: 5 minutes)
  • Time between notifications: Prevent repeated alerts
  • Fetch using: Direct HTTP or browser (Playwright)
  • Ignore text: Regex patterns to ignore

Configuring Watches via API

# Add a new watch
curl -X POST "http://localhost:5000/api/v1/watch" \
  -H "x-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/products",
    "tag": "products,monitoring",
    "title": "Product Price Monitor",
    "fetch_backend": "html_requests",
    "time_between_check": {"minutes": 30}
  }'

# List all watches
curl "http://localhost:5000/api/v1/watch" \
  -H "x-api-key: your-api-key"

# Trigger an immediate check
curl -X GET "http://localhost:5000/api/v1/watch/WATCH-UUID/history/latest" \
  -H "x-api-key: your-api-key"

# Delete a watch
curl -X DELETE "http://localhost:5000/api/v1/watch/WATCH-UUID" \
  -H "x-api-key: your-api-key"

CSS and XPath Filters

Filters let you monitor only specific parts of a page, ignoring timestamps, ads, and dynamic content that change constantly:

CSS Selectors

In the watch settings under Filters & Triggers:

/* Monitor only the price element */
.product-price

/* Monitor main content, ignore header and footer */
main.content

/* Multiple elements */
h1, .price, .stock-status

/* Attribute-based selection */
[data-testid="product-title"]

XPath Filters

//div[@class='price']
//table[@id='comparison-table']//tr
//span[contains(@class,'stock')]

Text Trigger Filters

# Trigger alert ONLY when specific text appears:
# Filters & Triggers → "Trigger/wait for text" → "In Stock"

# Trigger alert ONLY when text DISAPPEARS:
# "Trigger if text is NOT present" → "Out of Stock"

# Ignore lines matching these regex patterns:
# "Ignore text" → "Last updated: .*"

JSON Path Filters (for JSON endpoints)

# Monitor a JSON API endpoint, filter to a specific field
# URL: https://api.example.com/product/123
# Filter: $.price  (JSONPath expression)
# OR
# Filter: .stock.available  (Jinja2 expression)

Notification Channels

Go to Settings → Notifications to configure channels:

Email (SMTP)

# In Settings → Notifications → Add notification URL:
# mailto://user:[email protected][email protected]&[email protected]&port=587

# Or with multiple recipients:
# mailto://user:[email protected]:[email protected]&[email protected]

Ntfy

# Notification URL format:
ntfy://ntfy.yourdomain.com/changedetection

# With authentication:
ntfy://user:[email protected]/changedetection

Slack, Discord, Telegram

# Slack (via incoming webhook):
json://hooks.slack.com/services/T.../B.../...

# Discord:
discord://webhook-token@webhook-channel-id

# Telegram:
tgram://bot-token/chat-id

# Gotify:
gotifys://gotify.yourdomain.com/app-token

Webhook (Generic)

# Custom webhook for integration with any service:
# Settings → Notification URL:
json://your-server.com/webhook-endpoint?method=POST

# Custom body template (Jinja2):
{
  "url": "{{watch_url}}",
  "title": "Change detected: {{watch_title}}",
  "diff_url": "{{base_url}}/diff/{{watch_uuid}}"
}

Browser Rendering with Playwright

Some pages require JavaScript execution. Connect Playwright for these:

# In docker-compose.yml - playwright service already added above
# Configure Changedetection to use it:
environment:
  PLAYWRIGHT_DRIVER_URL: ws://playwright-chrome:3000

For individual watches that need browser rendering:

  1. Edit watch → Fetch usingChrome/JS Playwright
  2. Optional: set Page load wait (extra delay) for slow pages
# Verify Playwright is working
docker compose logs playwright-chrome --tail 20

# Test Playwright connection
curl "http://localhost:3000/json"

Playwright Screenshots

Enable screenshots for visual comparison:

# Edit watch → Filters & Triggers → Visual selector
# Draw a box around the element to monitor
# Changedetection will use pixel difference for change detection

API Usage

# Get watch details and current content hash
curl "http://localhost:5000/api/v1/watch/WATCH-UUID" \
  -H "x-api-key: your-api-key"

# Get diff/snapshot history
curl "http://localhost:5000/api/v1/watch/WATCH-UUID/history" \
  -H "x-api-key: your-api-key"

# Get the latest snapshot content
curl "http://localhost:5000/api/v1/watch/WATCH-UUID/history/latest" \
  -H "x-api-key: your-api-key"

# Bulk import watches from a list
cat > watches.txt << 'EOF'
https://example.com/product1
https://example.com/product2
https://competitor.com/pricing
EOF

while IFS= read -r url; do
    curl -s -X POST "http://localhost:5000/api/v1/watch" \
      -H "x-api-key: your-api-key" \
      -H "Content-Type: application/json" \
      -d "{\"url\":\"$url\"}"
    echo "Added: $url"
done < watches.txt

Export and Import

# Export all watches to OPML
curl "http://localhost:5000/api/v1/export?format=opml" \
  -H "x-api-key: your-api-key" > watches-export.opml

# The UI also supports import from OPML files
# Settings → Import

Troubleshooting

Changedetection shows no changes but content clearly changed:

# The change may be in dynamic content (timestamps, ads)
# Add CSS filter to target only stable content
# Check what text the tool is capturing:
# Watch → View Latest Content (eye icon)

Pages using JavaScript not loading correctly:

# Switch watch to use Chrome/Playwright
# Verify Playwright container is running
docker compose ps playwright-chrome

# Check Playwright connection in Changedetection:
# Settings → Fetching → Playwright driver URL
# Should be: ws://playwright-chrome:3000

Too many notifications (noise):

# Configure "Recheck time" per watch to check less frequently
# Enable "Time between notifications" to limit repeat alerts
# Add text trigger to only alert on specific content changes

High memory usage:

# Limit Playwright memory usage
# playwright-chrome container environment:
#   MAX_CONCURRENT_SESSIONS: "2"

# Reduce number of active watches with short check intervals
# Check for memory leaks
docker stats changedetection

Conclusion

Changedetection.io provides a practical solution for monitoring the web content your business cares about — without relying on website-provided feeds or paid monitoring services. Its CSS/XPath filtering prevents false positives from irrelevant page changes, while Playwright support handles modern JavaScript-heavy sites. Running self-hosted, it scales to hundreds of watches on modest server hardware with full control over notification routing.