services:
  searxng:
    image: searxng/searxng:latest
    container_name: searxng
    restart: unless-stopped
    # ------------------------------------------------------------------
    # Port binding — 9996 on all interfaces.
    # Change to "127.0.0.1:9996:8080" to restrict to localhost only.
    # Traefik handles public routing and TLS via the labels below.
    # ------------------------------------------------------------------
    ports:
      - 9996:8080
    # ------------------------------------------------------------------
    # Volumes
    #   Config: settings.yml bind-mounted read-only into the container.
    volumes:
      - /opt/docker/conf/searxng/searxng-settings.yml:/etc/searxng/settings.yml:ro
    # ------------------------------------------------------------------
    # Environment — see https://docs.searxng.org/admin/settings/index.html
    #   SEARXNG_SECRET  — required for cryptographic signing (cookies, etc.)
    #   BASE_URL        — public URL SearXNG reports in pages/RSS/OPDS
    #   INSTANCE_NAME   — shown in the page title / footer
    # ------------------------------------------------------------------
    environment:
      - SEARXNG_SECRET=${SEARXNG_SECRET}
      # searxng.ana.internal, not the old searxng.pfi.local (migrated
      # 2026-08-19). `.local` is reserved for mDNS, so the old name was a
      # standards collision that happened to work; `.internal` is ICANN-
      # reserved for exactly this. The name is served by the fleet's AdGuard
      # resolvers from dns/internal.yaml — see scripts/dns-sync.py.
      - BASE_URL=https://searxng.ana.internal/
      - INSTANCE_NAME=SearXNG
    # ------------------------------------------------------------------
    # Resource limits — tune for VM 102's available RAM/CPU
    # ------------------------------------------------------------------
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "1.0"
        reservations:
          memory: 128M
    # ------------------------------------------------------------------
    # Health check — SearXNG /healthz is the canonical liveness probe.
    #
    # ⚠️ `--tries=1` MUST keep its `=1`. This read `- --tries` / `- --spider`
    # as two separate argv entries until 2026-08-18, and in that form wget
    # consumed `--spider` as the VALUE of `--tries` — so spider mode never
    # engaged and every probe DOWNLOADED the response to a file instead of
    # just checking it. By the time it was caught the container's working
    # directory held 295,287 `healthz.N` files, one per probe since April,
    # and wget had to scan all of them to pick the next free filename. That
    # scan is what intermittently blew the 10s timeout and made the card on
    # the dashboard flap UNHEALTHY while the service itself was fine. It was
    # self-worsening: every probe made the next one slower.
    #
    # The junk lived in the container's writable layer (the only volume here
    # is the read-only settings mount), so recreating the container cleared
    # it. Symptom to watch for if this regresses: `docker exec searxng ls |
    # wc -l` climbing, and health log entries reading
    # "Health check exceeded timeout (10s)".
    # ------------------------------------------------------------------
    healthcheck:
      test:
        - CMD
        - wget
        - --no-verbose
        - --tries=1
        - --spider
        - http://localhost:8080/healthz
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s
    networks:
      - tnet
    labels:
      # Traefik configuration — auto-discovery via Docker provider
      - traefik.enable=true
      # Both names during the migration: `.internal` is the real one now, and
      # the old `.pfi.local` is kept as a fallback so anything still pointing
      # at it (a bookmark, a hardcoded config elsewhere) does not break the
      # day the name changes. Drop the second Host() once nothing uses it —
      # the Traefik access log will tell you when that is.
      - traefik.http.routers.searxng.rule=Host(`searxng.ana.internal`) || Host(`searxng.pfi.local`)
      - traefik.http.routers.searxng.entrypoints=websecure
      - traefik.http.routers.searxng.tls=true
      - traefik.http.routers.searxng.service=searxng
      - traefik.http.services.searxng.loadbalancer.server.port=8080
      - homepage.group=Daily
      - homepage.name=SearXNG
      - homepage.icon=si-searxng
      - homepage.description=Privacy-respecting meta-search
      - homepage.href=http://10.250.50.70:9996
networks:
  tnet:
    name: traefik-net
    external: true
