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.