Skip to content

Running Spottr in production

Spottr is one container with an SQLite database in /data. Everything is set with environment variables; only NNTP_HOST is required, and the default AUTH_MODE=password refuses to start without APP_PASSWORD.

A compose service

services:
  spottr:
    image: ghcr.io/rousseauxy/spottr:0.7.2
    restart: unless-stopped
    user: "65532:65532"
    read_only: true
    security_opt: [no-new-privileges:true]
    cap_drop: [ALL]
    tmpfs: ["/tmp:rw,noexec,nosuid,size=64m"]
    mem_limit: 1g
    environment:
      - NNTP_HOST=news.example.com
      - NNTP_PORT=563
      - NNTP_TLS=true
      - NNTP_USER=${NNTP_USER}
      - NNTP_PASS=${NNTP_PASS}
      - BACKFILL=true
      - MODERATION_MODE=mark
      - SAB_HOST=sabnzbd
      - SAB_API_KEY=${SAB_API_KEY}
      - AUTH_MODE=password
      - APP_PASSWORD=${APP_PASSWORD}
      - SECURE_COOKIE=true
    volumes:
      - ./data:/data

The image runs as uid 65532 and needs nothing but /data writable; the healthcheck calls /healthz. Publish the port only if nothing sits in front of it: behind a reverse proxy, let only the proxy reach it.

Usenet

Variable Default
NNTP_HOST (required) The news server.
NNTP_PORT 119 563 with TLS, as most providers want.
NNTP_TLS false
NNTP_USER, NNTP_PASS
NNTP_MAX_CONNS 4 A hard ceiling on connections to the news server, shared by the sync, the backfill, the withdrawal sweep and the pages. Keep it under what your subscription allows.

Sync and history

Variable Default
SYNC_INTERVAL 15m How often new articles are read.
SYNC_LOOKBACK 500000 How far the very first sync reaches back. Older history needs the backfill.
BACKFILL false Walk the group back to its first article in the background, checkpointed, two seconds between chunks. Several hours for a full group.
RETENTION_DAYS 0 Delete spots older than this; 0 keeps everything.
CACHE_MAX_BYTES 536870912 NZBs and covers fetched from Usenet are cached in the database up to this size (512 MiB).

Withdrawals and content

Variable Default
MODERATION_MODE off mark hides a withdrawn spot and keeps it (reversible); delete removes it, as Spotweb does. With either, the first start also applies the withdrawals of the past, once. See Withdrawals honoured.
VERIFY_SIGNATURES false Drop spots whose header signature does not verify. Off by default: the oldest spots are unsigned by design.
BLACKLIST_URL, WHITELIST_URL A spotter blacklist or whitelist in the XML format of the spotlist.store feed Spotweb uses (https://spotlist.store/spotnet/blacklist.xml), fetched every BLACKLIST_INTERVAL (12 h).
ALLOW_ADULT false Allow 18+ spots, on their own tab only.

SABnzbd

Variable Default
SAB_HOST, SAB_PORT , 8080 SABnzbd's address. Spottr uploads the NZB, so SABnzbd needs no route back to Spottr.
SAB_API_KEY SABnzbd's API key.
SAB_TLS false

Sign-in

Variable Default
AUTH_MODE password password: one shared password (APP_PASSWORD) for the pages that change something, such as sending to SABnzbd. proxy: a signing-in proxy in front is the only way in (see Sign-in with Authentik). open: no sign-in at all, deliberately.
APP_PASSWORD Required with password.
SESSION_DURATION 24h
SECURE_COOKIE false Set true behind HTTPS.
TRUSTED_PROXIES (none) The proxies whose X-Forwarded-For, X-Forwarded-Proto and sign-in header are believed, as CIDRs. List the address the proxy's requests arrive from, which is not always the proxy's own (a VPN or router that rewrites the source changes it). Never list a whole network your clients are on.
PROXY_AUTH_HEADER X-authentik-username With AUTH_MODE=proxy: the header the proxy sets on a request from a signed-in user. Believed only from TRUSTED_PROXIES; it lets that user download NZBs.

Newznab API

Variable Default
API_KEY Pins the API key. Unset, Spottr generates one on its first start, stores it in the database and prints it in that start's log.

See Prowlarr, Sonarr and Radarr.

Limits

Variable Default
RATE_LIMIT_RPS, RATE_LIMIT_BURST 5, 30 Per client address (as resolved through TRUSTED_PROXIES).
MAX_PAGE_SIZE, MAX_OFFSET 200, 100000 Caps on limit= and offset=.
MAX_ARTICLE_BYTES 33554432 The largest article read from Usenet (32 MiB).
MAX_NZB_BYTES 67108864 The largest NZB after decompression (64 MiB).

What to keep

The /data volume holds everything: the spots, the API key, the NZB and cover cache, and where the sync, backfill and withdrawal sweep stopped. Back it up as a file, with SQLite's own copy (VACUUM INTO) or with the container stopped: copying the file of a running database can catch it halfway through a write. Before an upgrade, a copy is cheap insurance: a release can change stored data (0.5.0 re-decoded titles, 0.6.2 recomputed every spot's kind), and migrations only run forward.

Health and metrics

  • /healthz: the process answers. /readyz: the database answers.
  • /v1/metrics: Prometheus metrics (behind sign-in).
  • Logs are structured, one line per request, with the client address as resolved through TRUSTED_PROXIES.