Run changedetection.io for Website Change Alerts With Docker Compose
Watch a page for meaningful changes without hand-refreshing it all day, then keep the first deployment private and modest instead of promising that every JavaScript-heavy site will work out of the box.
How to run changedetection.io locally, store watches persistently, and test notifications before you trust the service.
Operators who need simple page-change alerts for status pages, product docs, pricing pages, or lightweight monitoring tasks.
If you overcomplicate the first install with browser automation and too many watches, you create noise before you create value.
Before you begin
- A Docker host with Compose v2.
- A private admin path or reverse proxy plan.
- One or two specific pages you genuinely want to monitor.
- A notification destination you can test, such as email, ntfy, or another supported webhook target.
changedetection.io's current docs still treat Docker as the easiest deployment path and keep the application state inside
/datastore. That makes it a good fit for a small self-hosting stack as long as you keep the first rollout focused.
Step 1: Create the Compose stack
Create a working directory and save this as compose.yaml:
mkdir -p /opt/stacks/changedetection
cd /opt/stacks/changedetection
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io:latest
container_name: changedetection
restart: unless-stopped
ports:
- 127.0.0.1:5000:5000
volumes:
- changedetection_data:/datastore
volumes:
changedetection_data:
Binding to localhost only is the safer default. You can always put it behind a private reverse proxy later. Starting with a simple container also keeps the first troubleshooting loop short.
Step 2: Start and secure the service
Bring up the stack and check the logs:
docker compose up -d
docker compose ps
docker compose logs --tail 50 changedetection
Reach the app privately by SSH port forwarding or a protected reverse proxy. changedetection.io supports authentication and access-control features, but private network reachability is still the cleaner baseline than exposing another unauthenticated web UI to the public internet.
Step 3: Add the first watch and notification
Add one stable URL first. A documentation page, status page, or product page is a better first candidate than a chaotic home page with ads, rotating timestamps, or personalized content.
Then configure one notification destination and test it immediately. changedetection.io supports many notification backends, but the valuable part is proving one real end-to-end alert instead of collecting ten half-configured outputs.
Step 4: Add browser fetching only if needed
Some modern pages render important content only in the browser. changedetection.io can work with a browser-fetching sidecar, but you should add that only when the basic fetch path fails on a specific site.
A common pattern is to add a Playwright-based sidecar and point only the affected watches at it. Keep that as an exception path. Do not assume every site needs headless browser rendering.
services:
browserless:
image: browserless/chrome:latest
restart: unless-stopped
Exact sidecar settings evolve upstream. When you add this layer, recheck the current changedetection.io documentation and test one difficult page intentionally before widening the rollout.
Step 5: Operate and update safely
Routine checks:
docker compose ps
docker compose logs --tail 50 changedetection
docker volume ls | grep changedetection
Update the stack:
cd /opt/stacks/changedetection
docker compose pull
docker compose up -d
Review the first few alert cycles after any upgrade. A watcher that silently breaks is worse than one that fails loudly during testing.
Rollback notes
The persistent state lives in /datastore, so protect that volume before major changes. If an update misbehaves, revert the image tag,
recreate the container, and confirm that your watches and notifications still exist.
If browser-side rendering caused the trouble, remove that extra layer first and prove the base watcher still works on simpler URLs.
Troubleshooting
The watch fires constantly on a mostly unchanged page.
Filter noisy sections, remove timestamps or rotating fragments, and validate the watch preview before blaming the app.
The page shows blank or incomplete content.
That usually means the site depends on client-side rendering. Test a browser-fetcher path for that specific watch instead of converting the whole stack immediately.
You never receive notifications.
Test the notification target directly, then send a watch test event before waiting for a real page change.
The UI is reachable publicly.
Move the bind address back to localhost or put the service behind an authenticated private path right away.