Skip to content

changedetection.io

changedetection.io watches URLs for content changes, stores snapshots and diff history, and sends notifications through Apprise-backed channels such as email, webhooks, Slack, Telegram, Discord, Gotify, and many others. The HelmForge chart packages the official ghcr.io/dgtlmoon/changedetection.io:0.55.7 image with persistent /datastore storage and an optional Playwright browser sidecar for JavaScript-rendered pages.

Architecture

The chart deploys a single Deployment with the Recreate strategy. The application stores its SQLite database, watch configuration, rendered snapshots, and optional Python user packages under /datastore, which is backed by a PVC by default.

Component Purpose
changedetection container Runs the changedetection.io web UI, scheduler, diff engine, and notification flow.
Optional browser sidecar Runs Browserless Chromium on localhost for Playwright-backed JavaScript rendering.
PVC Stores SQLite data, snapshots, watch history, and Python user package installs.
Service Exposes the HTTP application port inside the cluster.
Ingress or HTTPRoute Optional external routing, disabled by default.
ExternalSecret Optional environment Secret materialization for notification credentials or runtime tuning.
Plan the browser mode before production

The most important sizing decision is whether browser.enabled is required. Static pages and APIs work without the sidecar. SPAs, modern commerce pages, and client-rendered dashboards usually need Playwright, which materially increases CPU and memory usage.

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install changedetection helmforge/changedetection

OCI registry:

helm install changedetection oci://ghcr.io/helmforgedev/helm/changedetection

Port-forward a default install:

kubectl port-forward svc/changedetection 5000:80

Open http://localhost:5000/.

Deployment Examples

changedetection:
  baseUrl: 'https://changes.example.com'
  timezone: 'UTC'

persistence:
  enabled: true
  size: 10Gi
changedetection:
  baseUrl: 'https://changes.example.com'
  fetchWorkers: 4

browser:
  enabled: true
  resources:
    requests:
      cpu: 250m
      memory: 512Mi
    limits:
      memory: 2Gi

persistence:
  enabled: true
  size: 20Gi
changedetection:
  baseUrl: 'https://changes.example.com'
  fetchWorkers: 4
  minimumSecondsRecheckTime: '300'
  timezone: 'America/Sao_Paulo'
  envFrom:
    - secretRef:
        name: changedetection-env

browser:
  enabled: true
  resources:
    requests:
      cpu: 250m
      memory: 512Mi
    limits:
      memory: 2Gi

persistence:
  enabled: true
  size: 50Gi
  storageClass: fast-retain

ingress:
  enabled: true
  ingressClassName: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: changes.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - hosts:
        - changes.example.com
      secretName: changedetection-tls

externalSecrets:
  enabled: true
  secretStoreRef:
    name: cluster-secrets
    kind: ClusterSecretStore
  target:
    name: changedetection-env
    creationPolicy: Owner
  data:
    - secretKey: LOGGER_LEVEL
      remoteRef:
        key: changedetection/app
        property: loggerLevel

resources:
  requests:
    cpu: 250m
    memory: 512Mi
  limits:
    cpu: 1000m
    memory: 1Gi
changedetection:
  baseUrl: 'https://changes.example.com'

gateway:
  enabled: true
  parentRefs:
    - name: shared-gateway
      namespace: ingress
  hostnames:
    - changes.example.com

Operational Guidance

Keep persistence.enabled=true for any environment where watches or history matter. changedetection.io uses SQLite, so the chart intentionally runs a single replica and does not expose horizontal scaling knobs. Increase persistence.size for frequent checks, long retention, rendered screenshots, and many watches.

Enable browser.enabled=true only for pages that require JavaScript rendering. When enabled, reduce changedetection.fetchWorkers and set browser.resources, because every concurrent browser-backed fetch can hold a Chromium process in memory.

Set changedetection.baseUrl when exposing the service publicly. Notification links depend on this value and should match the Ingress or Gateway hostname.

Use externalSecrets for notification credentials, application tuning values, or other upstream-supported environment variables that should not live in Git. The rendered Secret is automatically consumed through envFrom when externalSecrets.enabled=true.

Routing

Ingress and Gateway API are both disabled by default. Choose the routing model managed by your platform and avoid publishing the same hostname through both unless your cluster policy explicitly expects that topology.

Ingress supports class name, annotations, host paths, and TLS:

ingress:
  enabled: true
  ingressClassName: nginx
  hosts:
    - host: changes.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - hosts:
        - changes.example.com
      secretName: changedetection-tls

Gateway API renders an HTTPRoute and requires at least one gateway.parentRefs entry:

gateway:
  enabled: true
  parentRefs:
    - name: shared-gateway
      namespace: ingress
  hostnames:
    - changes.example.com

Service dual-stack fields are optional and preserve cluster defaults unless explicitly set:

service:
  ipFamilyPolicy: PreferDualStack
  ipFamilies:
    - IPv4
    - IPv6

Configuration Reference

Image And Application

Parameter Default Description
image.repository ghcr.io/dgtlmoon/changedetection.io Official changedetection.io image.
image.tag 0.55.7 Pinned upstream image tag.
image.pullPolicy IfNotPresent Kubernetes image pull policy.
changedetection.port 5000 Container HTTP port.
changedetection.baseUrl "" Public URL used in notification links.
changedetection.fetchWorkers 10 Concurrent fetch workers. Lower this when browser rendering is enabled.
changedetection.minimumSecondsRecheckTime "" Minimum seconds between rechecks per watch.
changedetection.timezone "" Container TZ value.
changedetection.locale C LANG and LC_ALL locale.
changedetection.defaultWatches.enabled false Allow upstream sample watches on a fresh datastore.
changedetection.extraEnv [] Extra environment variables.
changedetection.envFrom [] Additional envFrom sources.

Browser

Parameter Default Description
browser.enabled false Enable the Browserless Chromium sidecar.
browser.image.repository ghcr.io/browserless/chromium Browser sidecar image.
browser.image.tag v2.46.0 Browser sidecar tag.
browser.image.pullPolicy IfNotPresent Browser image pull policy.
browser.resources {} Resource requests and limits for the sidecar only.

Persistence

Parameter Default Description
persistence.enabled true Create/use persistent storage for /datastore.
persistence.size 10Gi PVC size.
persistence.storageClass "" StorageClass name; empty uses cluster default.
persistence.accessModes [ReadWriteOnce] PVC access modes.
persistence.existingClaim "" Reuse an existing PVC.

Routing And Service

Parameter Default Description
service.type ClusterIP Service type.
service.port 80 Service HTTP port.
service.annotations {} Service annotations.
service.ipFamilyPolicy unset Optional Service IP family policy.
service.ipFamilies [] Optional Service IP families.
ingress.enabled false Render Ingress.
ingress.ingressClassName traefik Ingress class name.
ingress.annotations {} Ingress annotations.
ingress.hosts [] Ingress host/path rules.
ingress.tls [] Ingress TLS entries.
gateway.enabled false Render Gateway API HTTPRoute.
gateway.parentRefs [] Required parent Gateway references when enabled.
gateway.hostnames [] HTTPRoute hostnames.
gateway.path / HTTPRoute path prefix.
gateway.pathType PathPrefix HTTPRoute path match type.

External Secrets

Parameter Default Description
externalSecrets.enabled false Render an ExternalSecret and consume the target Secret.
externalSecrets.apiVersion external-secrets.io/v1 ExternalSecret API version.
externalSecrets.refreshInterval 1h Reconciliation interval.
externalSecrets.secretStoreRef.name "" SecretStore or ClusterSecretStore name.
externalSecrets.secretStoreRef.kind SecretStore Store reference kind.
externalSecrets.target.name "" Target Secret name; empty derives from release name.
externalSecrets.target.creationPolicy Owner ExternalSecret target creation policy.
externalSecrets.data [] Individual remote key mappings.
externalSecrets.dataFrom [] Provider-side extraction entries.

Runtime, Security, And Scheduling

Parameter Default Description
probes.startup.enabled true Enable startup probe.
probes.liveness.enabled true Enable liveness probe.
probes.readiness.enabled true Enable readiness probe.
resources.requests.cpu 100m Main container CPU request.
resources.requests.memory 256Mi Main container memory request.
resources.limits.cpu 1000m Main container CPU limit.
resources.limits.memory 1Gi Main container memory limit.
podSecurityContext.fsGroup 1000 Writable group for /datastore.
podSecurityContext.seccompProfile.type RuntimeDefault Pod seccomp profile.
securityContext.runAsNonRoot true Run main container as non-root.
securityContext.allowPrivilegeEscalation false Prevent privilege escalation.
securityContext.readOnlyRootFilesystem false Writable root remains enabled for upstream runtime behavior.
securityContext.capabilities.drop [ALL] Drop Linux capabilities.
nodeSelector {} Node selector.
tolerations [] Pod tolerations.
affinity {} Pod affinity.
topologySpreadConstraints [] Topology spread constraints.
priorityClassName "" PriorityClass name.
terminationGracePeriodSeconds 30 Pod termination grace period.
extraVolumes [] Extra pod volumes.
extraVolumeMounts [] Extra mounts for the main container.
extraManifests [] Extra Kubernetes manifests rendered with the release.

Validation

The chart is validated through HelmForge chart gates: strict linting, templating across CI values, helm-unittest, kubeconform with real CRD schemas, Artifact Hub linting, and k3d behavioral installs.

More Information