How to Watch Any Web Page for Changes with changedetection.io (Prices, Stock, Appointments)

Tell me when this page changes: changedetection.io watches prices, stock, slots and notices — and messages you.

The GPU you want keeps selling out. The council publishes bin-collection changes somewhere on a page. The GP's booking page opens slots at random times. changedetection.io checks pages on a schedule, compares them to last time, and sends a notification when the bit you care about changes — with a visual selector so "the bit you care about" is the price, not the rotating banner. One container; a browser container alongside for sites that need JavaScript.

Quick reference

TaskWhat
Folder~/docker/changedetection/compose.yaml
Webhttp://server-ip:5000 (or another port if Frigate has it)
Add a watchpaste a URL → Edit → Visual selector → click the element
JavaScript sitesadd the browser-chrome service and pick Playwright in the watch
AlertsNotification URLs in Apprise format: ntfy, Telegram, email
Check intervalper watch; be polite — 30 minutes or more for most

1. The compose file

mkdir -p ~/docker/changedetection/data && cd ~/docker/changedetection
nano compose.yaml

services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
ports:
- "5001:5000"
environment:
PLAYWRIGHT_DRIVER_URL: ws://browser-chrome:3000/
BASE_URL: http://192.168.1.50:5001
TZ: Europe/London
volumes:
- ./data:/datastore
depends_on:
- browser-chrome
restart: unless-stopped

browser-chrome:
image: dgtlmoon/sockpuppetbrowser:latest
container_name: browser-chrome
environment:
SCREEN_WIDTH: 1920
SCREEN_HEIGHT: 1080
MAX_CONCURRENT_CHROME_PROCESSES: 5
restart: unless-stopped

docker compose up -d
sudo ufw allow from 192.168.1.0/24 to any port 5001

2. A first watch

Open http://192.168.1.50:5001, paste a product page's URL, Watch. It fetches the page and shows the text it found. Click Edit → Visual Filter Selector: the page renders as a picture; click the price, and the selector (span.price) is filled in. Now only that element is compared. Recheck once, and the watch is live.

Settings → Time between check: 30 minutes for a shop, daily for a council page. Checking every minute will get your IP blocked and is unkind.

3. JavaScript-only pages

If the fetched text is empty or "enable JavaScript", switch the watch's Fetch method to Playwright Chromium — the browser container renders it properly. It can also click a button or fill a form first (Browser Steps), for pages behind a "check availability" click.

4. Alerts

Settings → Notifications (global) or per watch, in Apprise URL form:

ntfys://TOKEN@ntfy.example.com/server
tgram://BOT_TOKEN/CHAT_ID
mailto://myserver@gmail.com:APP_PASSWORD@gmail.com?to=you@example.com

The message body can include {{diff}} — the changed text — so the notification says "£799 → £649" rather than "something changed". Send test proves it.

5. Useful tricks

Things it's good for

GPU and console stock, used-car listings, a flat on Rightmove with a price filter, a concert's tickets page, software release pages, your own site's homepage (as a cheap "did it get defaced" check), a competitor's prices, the school term dates page.

Troubleshooting

Every check reports a change. Something dynamic is inside the selector — narrow it, or add the changing text to Ignore text.

"Fetch failed" / 403. The site blocks bots. Use Playwright, set a browser user-agent in the watch's Request tab, lengthen the interval, or go through a proxy.

Port 5000 clash. Frigate (91) uses it; the compose above maps to 5001.

Browser container eats memory. Lower MAX_CONCURRENT_CHROME_PROCESSES, or use plain fetch for sites that don't need JavaScript.

Wrapping up

One container (two with the browser), a URL, a click on the element you care about, an ntfy URL. The page that you'd otherwise refresh every evening now tells you — the moment the price drops, the stock returns, or the slot appears.

Call us