Changedetection.io Setup: Monitor Webpage Changes and Get Alerts via Docker

Monitor webpage updates, price drops, and structural changes. Deploy changedetection.io via Docker Compose with automated notifications.

Changedetection.io Setup: Monitor Webpage Changes and Get Alerts via Docker

Keeping track of webpage updates, restock notifications, price drops, and visual changes on the modern web is increasingly difficult. Many sites rely heavily on client-side JavaScript rendering, single-page application (SPA) architectures, or implement anti-scraping protections (like Cloudflare) that block basic cURL requests.

Changedetection.io is the premier self-hosted solution for webpage change monitoring. By combining Changedetection.io with a dedicated headless Chrome rendering container (Browserless/Playwright), you can bypass basic scraping limitations, execute custom interactive scripts (like clicking buttons or logging in), filter out noisy layout shifts, and send instant alerts to dozens of destinations.

This guide provides a comprehensive, production-grade guide to deploying Changedetection.io on a virtual private server (VPS) using Docker Compose.


1. Architectural Overview

A robust self-hosted changedetection environment consists of two main decoupled components running inside a Docker network:

  1. Changedetection.io Core: The Flask-based web application. It handles the scheduler, database storage (YAML flat-files or SQLite), difference calculation (visual diffs, text diffs, HTML diffs), and notification dispatching.
  2. Playwright/Browserless (Headless Chrome): A browser-as-a-service container. When Changedetection.io encounters a Javascript-heavy page, it passes the URL to the Browserless container via a WebSocket connection. Browserless launches a Chromium instance, renders the page, executes scripts, and returns the fully rendered HTML or visual screenshot to the core application.
graph TD
    Client[User Browser] -->|Port 5000 / Reverse Proxy| CD[Changedetection.io Core]
    CD -->|WebSocket ws://browserless:3000| BL[Browserless/Playwright Container]
    BL -->|Fetches Page| Web[Target Website]
    CD -->|Apprise/Webhooks| Notify[Notifications: Discord, Telegram, Webhook]
    CD -->|Volume Mount| Disk[(Persistent Storage)]

2. Production Docker Compose Configuration

Create a dedicated application directory on your VPS (e.g., /opt/changedetection) and populate the following docker-compose.yml.

This configuration includes: - Stealth settings for the Playwright engine to reduce detection by bot mitigation frameworks. - Resource limit controls on the Browserless container to prevent memory leaks from long-running Chrome processes. - Named networks and restart policies to ensure high availability.

version: '3.8'

services:
  changedetection:
    image: ghcr.io/dgtlmoon/changedetection.io:latest
    container_name: changedetection
    hostname: changedetection
    restart: unless-stopped
    ports:
      - "127.0.0.1:5000:5000" # Bound to localhost for security behind a reverse proxy
    volumes:
      - changedetection-data:/datastore
    environment:
      # Link to the browserless service with stealth & security arguments
      - PLAYWRIGHT_DRIVER_URL=ws://browserless:3000/?stealth=true&--disable-web-security=true
      # Uncomment to secure with a base URL if needed
      # - BASE_URL=https://monitor.yourdomain.com
    depends_on:
      - browserless
    networks:
      - monitor-network

  browserless:
    image: browserless/chrome:latest
    container_name: browserless-chrome
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000" # Expose only locally
    environment:
      - SCREEN_WIDTH=1920
      - SCREEN_HEIGHT=1080
      - SCREEN_DEPTH=24
      - MAX_CONCURRENT_SESSIONS=10 # Prevents CPU spikes by queuing excessive simultaneous requests
      - MAX_QUEUE_LENGTH=20
      - PREBOOT_CHROME=true # Launches Chrome ahead of time for faster render response
      - CONNECTION_TIMEOUT=60000 # 60 seconds connection timeout
      - KEEP_ALIVE=true
      - DEMO_MODE=false
      - DEBUG=false
    # Limit resources to prevent runaway Chrome processes from locking your VPS
    deploy:
      resources:
        limits:
          memory: 2g
    networks:
      - monitor-network

volumes:
  changedetection-data:
    driver: local

networks:
  monitor-network:
    driver: bridge

Explaining Critical Variables:

  • PLAYWRIGHT_DRIVER_URL: Appending ?stealth=true to the WebSocket connection string instructs Browserless to inject anti-fingerprinting scripts. This hides headless Chrome characteristics (e.g., masking the navigator.webdriver property) to evade detection.
  • MAX_CONCURRENT_SESSIONS: Headless Chrome is notoriously resource-heavy. Setting a session ceiling ensures your VPS doesn't exhaust its CPU and RAM when multiple monitors execute at the same time.
  • 127.0.0.1:5000:5000: Binding ports to localhost prevents public access to your containers before you configure a secure reverse proxy with SSL certificate termination.

3. Step-by-Step Deployment Guide

Follow these terminal steps on your VPS to spin up the stack.

Step 1: Create the Directory and Write Compose File

sudo mkdir -p /opt/changedetection
cd /opt/changedetection
sudo nano docker-compose.yml

Paste the Docker Compose configuration provided above and save the file (Ctrl+O, Enter, Ctrl+X).

Step 2: Spin Up the Stack

sudo docker compose up -d

Verify that both containers are running properly:

sudo docker compose ps

Step 3: Configure Reverse Proxy (Caddy Example)

For public access over HTTPS, use a lightweight reverse proxy like Caddy. Install Caddy on your host and add the following config to /etc/caddy/Caddyfile:

monitor.yourdomain.com {
    reverse_proxy localhost:5000
}

Reload Caddy to apply changes and automatically request an SSL certificate from Let's Encrypt:

sudo systemctl reload caddy

4. Configuring Playwright Browser Rendering

Once you log into the Changedetection.io dashboard, configure it to route scraper traffic through Playwright:

  1. Navigate to Settings > Fetch Method.
  2. Select Playwright (Chromium/Javascript) as the default fetcher instead of Basic fast Plaintext/HTTP Client.
  3. Click Save.
  4. Test the configuration by adding a dynamic JS page (e.g., a React dashboard or Twitter feed) and hitting Recheck.

When to use basic Fetcher vs. Playwright:

  • Basic (cURL): Use for static blogs, raw RSS feeds, and REST APIs. It uses negligible system resources and executes instantly.
  • Playwright: Use for modern SPAs, e.g., online retail stores (Amazon, BestBuy), dynamic charts, and pages requiring user actions (like cookie acceptance bypass).

5. Advanced Change Monitoring Techniques

Visual Filters (CSS / XPath selectors)

By default, Changedetection.io monitors the text content of the entire document. If a website changes its footer, sidebars, or CSRF tokens on every page load, it will trigger false positives.

To isolate changes: 1. Edit a watch and go to the Filters & Triggers tab. 2. In the CSS/XPath Filter field, input the selector targeting only the relevant content. - Example (Price Container): span.price-amount or //div[@id='product-price'] - Example (Article Body): article.post-entry

Stripping Dynamic Elements (Subtractions)

If you must monitor a large section but want to exclude dynamic elements like timestamp headers: 1. In the CSS/XPath Subtraction field, add the selectors of elements you want to ignore. - Example: .comment-section, .dynamic-timestamp, #ads-banner

Playwright Interactive Steps

Changedetection.io supports multi-step interactions prior to comparing changes. Navigate to the Playwright Steps tab of your watch configuration:

  • Clicking cookie consent: text click: button#accept-cookies wait: 2000
  • Form Submission (Login): text type: input#username, mysecretuser type: input#password, supersecurepwd click: button[type="submit"] wait: 5000

6. Notification Webhooks & Alert Routing

Changedetection.io integrates with Apprise, giving you out-of-the-box support for over 80 notification services.

Apprise Notification Strings

Go to Settings > Notifications to configure global alerts, or override them per watch. Examples of target strings:

  • Discord: discord://[WebhookID]/[WebhookToken]
  • Telegram: tgrid://[BotToken]/[ChatID]
  • Gotify (Self-hosted): gotify://[Host]/[Token]

Custom Webhook Payloads

If you are integrating Changedetection.io into an automated API workflow or CI/CD pipeline, configure a POST webhook:

  1. Use the notification string: json://your-api.com/endpoint
  2. Customize the JSON body in the Notification Body textarea:
{
  "event": "page_changed",
  "title": "{watch_title}",
  "url": "{watch_url}",
  "diff": "{diff}",
  "timestamp": "{current_utc}"
}

7. Maintenance & Backups

All state, screenshot diffs, and watch definitions are stored inside the local volume mapped to /datastore in the changedetection container.

Automated Backups

Run a daily cron job to tarball the datastore directory and push it to secure external storage:

#!/bin/bash
BACKUP_DIR="/backup/changedetection"
mkdir -p "$BACKUP_DIR"
tar -czf "$BACKUP_DIR/changedetection_$(date +%F).tar.gz" -C /var/lib/docker/volumes/changedetection_changedetection-data/_data .

Updating the Stack

To update Changedetection.io and the Browserless container to their latest releases:

cd /opt/changedetection
sudo docker compose pull
sudo docker compose up -d --remove-orphans
sudo docker image prune -f