pending() took the newest files still pending, up to the limit, so once a show's latest three
were down, each full read of its feed took the three before them, working back through its
whole history. In production 4420 files (about 310 GB) were queued this way across 12 shows,
all on the default limit of 3, which is meant as "the latest three". It now takes only from the
feed's newest `limit` items with a file. 0, unlimited, still takes the whole back catalogue:
that is how the shows kept as an archive are set, along with limits of 100 and 10000.
The settings' wording followed the old behaviour ("The rest wait for the next scan"); the field
is now "Newest episodes to download", and says what 0 does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
150 lines
8.2 KiB
Markdown
150 lines
8.2 KiB
Markdown
# Configuration
|
|
|
|
Two places. **config.toml** holds what ipx needs before it reaches its database, and what decides
|
|
who gets in: where things are (`download_dir`, `socket`, `organize`), `[torrent]` and `[web]`.
|
|
**The database** holds the catalogue of feeds (`[feeds.<id>]` below) and the server settings the
|
|
admin page edits (`schedule`, `max_total_gb`, `max_age_days`, `max_new_per_check`,
|
|
`media_types`). Change those in the web UI, or with `ipx add`, `ipx rm` and `ipx import`; they
|
|
take effect without a restart.
|
|
|
|
The first time ipx meets a database that holds no catalogue, it takes the feeds and those
|
|
settings from config.toml, then rewrites config.toml without them, keeping the original beside it
|
|
as `config.toml.pre-database`. After that, feeds or those settings written into config.toml are
|
|
ignored, with a warning in the log saying so. The sections below describe them as they were
|
|
written in config.toml, which is still how a fresh install begins.
|
|
|
|
config.toml's default location is `$XDG_CONFIG_HOME/ipx/config.toml`
|
|
(`~/.config/ipx/config.toml`), overridden with `--config` or `$IPX_CONFIG`.
|
|
|
|
| What | Where | Override |
|
|
|---|---|---|
|
|
| Config | `~/.config/ipx/config.toml` | `--config`, `$IPX_CONFIG` |
|
|
| Database | `~/.local/share/ipx/state.db` | `$IPX_DATA_DIR` |
|
|
| Control socket | `$XDG_RUNTIME_DIR/ipx.sock` | `[general] socket` |
|
|
| Downloads | `[general] download_dir` | — |
|
|
|
|
`~` is expanded in paths. The database is SQLite in WAL mode unless `IPX_DATABASE_URL` names a
|
|
Postgres database instead. Back SQLite up by copying `state.db` while the daemon is stopped, or
|
|
with `sqlite3 state.db .backup`; back Postgres up with `pg_dump`.
|
|
|
|
## `[general]`
|
|
|
|
```toml
|
|
[general]
|
|
download_dir = "~/Podcasts"
|
|
socket = "/run/user/1000/ipx.sock"
|
|
schedule = "every 1h" # "every 30m", "every 4h", "2d", "90" (minutes)
|
|
organize = "feed" # "feed" | "date"
|
|
max_total_gb = 50 # 0 = unlimited
|
|
max_age_days = 30 # 0 = keep forever
|
|
max_new_per_check = 3 # per feed, per scan. 0 = unlimited
|
|
media_types = ["audio", "video"]
|
|
```
|
|
|
|
`schedule`, `max_total_gb`, `max_age_days`, `max_new_per_check` and `media_types` move into the
|
|
database as described above; `download_dir`, `socket` and `organize` stay in config.toml.
|
|
|
|
* **`schedule`** — how often feeds are re-checked. A feed's own `<ttl>` still wins when it asks to
|
|
be polled *less* often, and a per-feed `schedule` overrides both. Admin-only from the UI.
|
|
* **`organize`** — `feed` files downloads under the feed's folder; `date` under `YYYY-MM-DD`.
|
|
* **`max_total_gb`** — the reaper deletes to get back under this, oldest first, keeping a 50 MB
|
|
pad. Kept items are never deleted, and a file only counts as read once every subscriber has
|
|
read it. `0` disables it entirely.
|
|
* **`max_age_days`** — items older than this with no file on disk are pruned from the database.
|
|
Kept ones stay. `0` disables it.
|
|
* **`max_new_per_check`** — how many of a feed's newest episodes are downloaded; older ones stay
|
|
listed to download by hand. It stops a new subscription pulling a whole back catalogue.
|
|
`0` means every episode, for an archive; set it on the feeds you want archived, since on the
|
|
global default it applies to every feed: subscribing to an OPML of 80 feeds with no limit
|
|
fetched 216 files and 22 GB in one scan.
|
|
* **`media_types`** — top-level MIME types taken automatically. Anything else is still listed and
|
|
can be fetched by hand; blog feeds put each article's header image in an `<enclosure>`, and
|
|
without this the disk fills with artwork. Empty takes everything.
|
|
|
|
## `[torrent]`
|
|
|
|
```toml
|
|
[torrent]
|
|
enabled = true
|
|
seed_ratio = 1.0 # stop seeding at this ratio ...
|
|
seed_time_mins = 60 # ... or after this long, whichever comes first
|
|
port_range = "6881-6889"
|
|
stall_mins = 30 # give up on a torrent making no progress
|
|
```
|
|
|
|
A `.torrent` body is handed to the torrent path whatever MIME type it was advertised as. Torrents
|
|
run on their own tasks (two at a time) so a slow swarm never blocks a scan.
|
|
|
|
## `[web]`
|
|
|
|
```toml
|
|
[web]
|
|
enabled = true
|
|
bind = "0.0.0.0:8099" # 127.0.0.1:8080 by default
|
|
token = "" # generated and saved on first run
|
|
trusted_header = "" # e.g. "Cf-Access-Authenticated-User-Email"
|
|
trusted_proxies = ["127.0.0.1", "::1"]
|
|
access_team = "" # e.g. "<team>.cloudflareaccess.com"
|
|
access_aud = "" # the Access application's AUD tag
|
|
auto_create_users = true
|
|
sign_out_url = "" # e.g. "/cdn-cgi/access/logout"
|
|
session_days = 30
|
|
```
|
|
|
|
* **`token`** — the shared secret, which signs in as the **admin**. `?token=…` sets a cookie, so
|
|
you paste it once per browser. It is what the Docker healthcheck and any scripts use.
|
|
* **`trusted_header`** — a header naming the signed-in user, set by whatever fronts ipx. Empty
|
|
disables that path. See [sso.md](sso.md).
|
|
* **`trusted_proxies`** — addresses allowed to assert that header, and the entire security boundary
|
|
for it. Name the proxy, never a subnet.
|
|
* **`access_team`**, **`access_aud`** — with both set, a request through the proxy also has to
|
|
carry the `Cf-Access-Jwt-Assertion` Cloudflare Access signed for this application, and the name
|
|
comes from that token instead of the header. See [sso.md](sso.md#verifying-cloudflares-token).
|
|
* **`auto_create_users`** — create an account the first time the proxy vouches for a new name.
|
|
* **`sign_out_url`** — where Sign out sends someone the proxy signed in: the proxy's own sign-out,
|
|
`/cdn-cgi/access/logout` behind Cloudflare Access. Empty sends them to the sign-in page, where
|
|
the proxy signs them straight back in.
|
|
* **`session_days`** — sign a session out after this long without a request.
|
|
|
|
It is plain HTTP. On a LAN bind everything crosses the network in the clear — and a feed URL can
|
|
itself carry a credential. Put TLS in front of it if that matters.
|
|
|
|
## `[feeds.<id>]`
|
|
|
|
Kept in the database once ipx has moved them in: a feed's settings are changed in the web UI, and
|
|
feeds come and go with `ipx add`, `ipx rm` and `ipx import`. The table key is the feed id: stable,
|
|
human-readable, and used in paths and the API. `ipx add` derives it from the feed title.
|
|
|
|
```toml
|
|
[feeds.atp]
|
|
url = "https://atp.fm/rss"
|
|
folder = "Accidental Tech Podcast" # default: the feed title
|
|
schedule = "every 6h" # overrides [general] for this feed
|
|
media_types = ["audio"] # overrides [general] for this feed
|
|
category = "Technology" # the Directory's, if the feed names none
|
|
username = "ray" # HTTP basic auth
|
|
password_env = "IPX_ATP_PASS" # preferred over a literal `password`
|
|
```
|
|
|
|
With more than one account, **`keywords`, `auto_download`, `allow_explicit` and
|
|
`max_new_per_check` live on each person's subscription in the database**, not here — the values in
|
|
config.toml are the fallback for a feed nobody has claimed. The keys above describe the feed itself
|
|
and are the same for everyone. See [users.md](users.md).
|
|
|
|
Feeds derived from a subscribed OPML are **not** in the catalogue: the OPML is the source of truth
|
|
and they are re-derived on every scan. Editing one in the UI promotes it to a catalogue entry.
|
|
|
|
## Environment
|
|
|
|
| Variable | Effect |
|
|
|---|---|
|
|
| `IPX_CONFIG` | Config file path |
|
|
| `IPX_DATA_DIR` | Directory holding `state.db` |
|
|
| `IPX_DATABASE_URL` | A `postgres://user:password@host:port/database` URL: use that database instead of `state.db` |
|
|
| `IPX_TEST_DATABASE_URL` | For `cargo test`: run the database tests on this Postgres database too, each in a schema of its own |
|
|
| `IPX_LOG` | What reaches stderr (`ipx=debug`, `ipx::scan=debug`, …) |
|
|
| `IPX_LOG_FORMAT` | `json` for one JSON object a line, with each request's and event's fields as its own (for Loki and the like); text otherwise |
|
|
| `IPX_UI_LOG` | What the in-process log buffer captures for the UI's Log view |
|
|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | An OTLP/HTTP collector, such as Tempo at `http://host:4318`: the daemon sends it traces of scans, downloads and web requests. The other `OTEL_EXPORTER_OTLP_*` variables apply too |
|
|
| `http_proxy` / `https_proxy` | Honoured for feed and enclosure fetches |
|