Skip to content

Configuration

PosterPilot is configured two ways, and they work together:

  • Environment variables — set on the container. Good for unattended setups and secret management.
  • The in-app Settings page — entered in the UI and persisted to the SQLite database under /data so they survive restarts. Settings is organized into tabs: Media server, Metadata & providers, Kometa & advanced, Language, and Activity (the in-app event log). Managing Kometa’s own config.yml lives on its own Kometa manager page (the Kometa item in the main nav), not in Settings. A guided first-install wizard at /setup covers the same ground in order for a fresh install.

For any given setting, the environment variable always takes precedence over the persisted UI value. When a value is supplied via the environment, the Settings page shows it as environment-managed and locks it from editing in the UI — so the source of truth is unambiguous.

If a value is set in neither place, the documented default (if any) applies, or the feature that depends on it stays unconfigured until you set it.

Secrets (the Plex token, the Jellyfin/Emby API keys, the TMDB credential, and the Fanart.tv key) are never echoed back to the browser after they are saved and are redacted from logs — the Settings page only indicates that a secret is set.

Those same secrets — the Plex token, the Jellyfin and Emby API keys / access tokens, the TMDB credential, and the Fanart.tv key — are encrypted at rest with AES-256-GCM before they are written to the SQLite database. Each stored value is self-describing (it carries an enc:v1: prefix), so PosterPilot can tell encrypted values from legacy plaintext.

  • Zero setup by default. On first run PosterPilot generates a random 32-byte instance key and persists it — owner-readable only — at data/.app-key. Nothing to configure: secrets are encrypted automatically. (Override the path with APP_KEY_FILE if you need to.)
  • Portable key for shared deployments. Set the optional APP_SECRET environment variable to derive the key from a value you control (deterministically via scrypt). Use it when you run multiple replicas sharing one database, or when you want the same key to survive recreating the container without carrying the key file across. When APP_SECRET is set it takes precedence over the generated data/.app-key.
  • Existing installs are not broken. Secrets saved by an older version as plaintext are read transparently and re-encrypted the next time that setting is saved — no manual re-entry needed.
  • Safe failure. If a secret cannot be decrypted (for example the key was lost or changed), PosterPilot treats it as unset and prompts you to re-enter it rather than crashing.

PosterPilot talks to one active media server at a time, chosen by SERVER_TYPE (plex, jellyfin, or emby; defaults to plex). Only the active server’s credentials are validated before a sync.

Plex needs a base URL and an X-Plex-Token. You can supply them three ways:

  • PIN login (recommended). In Settings, start a Plex login. PosterPilot creates a strong PIN with plex.tv, shows you a code and an authorization link, and polls until you authorize it — then it stores the acquired token for you, so you never have to find and paste a raw token. If the PIN expires before you authorize, just start a new login.
  • Connection discovery. Once a token is available, PosterPilot can discover your Plex servers and their connections from plex.tv, labelling each connection local or remote (relays are flagged). Pick one instead of typing a URL; the chosen connection is verified with a connection test before it is saved as the active Plex base URL.
  • Manual. Paste the base URL (e.g. http://192.168.1.10:32400) and an X-Plex-Token directly.

Jellyfin needs a base URL (JELLYFIN_URL) and an access token, stored as the API key (JELLYFIN_API_KEY). Set SERVER_TYPE=jellyfin to make it the active server. The simplest way to connect is to sign in with your Jellyfin username and password in Settings — PosterPilot authenticates against the server and stores the returned access token for you (encrypted at rest), so you never have to generate an API key by hand; the password is used only for that one request and is never persisted. Pasting an API key directly stays available as a fallback. Posters and backgrounds are uploaded to the Jellyfin image API (Primary for poster, Backdrop for background). There is no PIN login or connection discovery as there is for Plex.

Emby needs a base URL (EMBY_URL) and an access token, stored as the API key (EMBY_API_KEY). Set SERVER_TYPE=emby to make it the active server. Like Jellyfin, Emby lets you sign in with your username and password — PosterPilot exchanges them for an access token and stores it (encrypted) so you do not have to find an API key, with manual API-key entry as a fallback. There is no PIN login or connection discovery.

A TMDB API credential is required: PosterPilot resolves each synced title to a TMDB id (so providers can be queried accurately) and TMDB is also one of the artwork providers. Set it via TMDB_KEY or in Settings. Both a v3 API key and a v4 bearer/JWT token are accepted — the format is auto-detected.

PosterPilot fans out across multiple artwork providers during discovery and merges their candidates, tagging each with the provider it came from. Each provider can be enabled or disabled independently, in Settings or via its environment variable.

ProviderDefaultNeeds a keyNotes
MediUXonnoScraped poster/background sets with uploader attribution.
TMDBonreuses TMDB_KEYPosters and backdrops from the TMDB images endpoint.
Fanart.tvoffFANART_KEYPosters, backgrounds, and logos from the Fanart.tv API.
ThePosterDBoffnoScraped community poster/background sets, throttled and cached.

Fanart.tv is the only keyed provider: if it is enabled but no FANART_KEY is configured, discovery skips it and surfaces the missing-credential condition rather than failing the whole run. A failure, timeout, or unparseable response from one provider never prevents the others from returning candidates.

A handful of advanced settings (in the Kometa & advanced Settings tab, or via the environment) tune how PosterPilot scores, syncs, applies, and caches. They follow the usual precedence — an environment variable overrides the persisted value and locks the control in the UI.

  • Suggested-artwork pre-selection (SUGGEST_PRESELECT, default on). When on, the item view pre-selects the highest-scored candidate per slot as an overridable suggestion. Turn it off to leave every slot unselected until you choose.
  • Scoring weights. PosterPilot ranks candidates on three terms — a per-provider base weight (MediUX, ThePosterDB, Fanart.tv, TMDB), a resolution score, and an aspect-fit score (2:3 for posters, 16:9 for backdrops and title cards). The defaults favor MediUX while still letting a much sharper or better-shaped image from another provider win. Adjust the weights in Settings; they are stored in the database and have no environment variable.
  • Incremental sync (INCREMENTAL_SYNC, default on). Repeat syncs skip items whose media-server last-modified timestamp has not changed since the last sync. A full rescan stays available on demand.
  • Apply concurrency (APPLY_CONCURRENCY, default 4). How many items a bulk apply processes at once. Raise it to finish large batches faster; lower it to be gentler on your server and the providers.
  • Thumbnail cache (THUMB_CACHE_TTL_DAYS, default 30; THUMB_CACHE_MAX_MB, default 512). Provider preview images are cached on disk under /data to speed up the grid and cut provider bandwidth. Entries are reused until the TTL (in days) expires, and the cache is bounded by a maximum size (in MB) — once it is exceeded, the least-recently-used entries are evicted.
  • Library default sort (LIBRARY_DEFAULT_SORT, default title). Which sort the library wall opens with when the URL doesn’t name one: title, year, rating, runtime, recent (recently changed), or added (date added on the media server). Picking a sort in the library toolbar always overrides it.

FUN (FUN_ENABLED, default off) is an opt-in home for library experiments, hidden until you enable it in Settings → Kometa & advanced. Its first tool is a random movie/series picker — see Usage. While the toggle is off, FUN has no navigation entry and its page returns 404.

When you apply a cover with the Kometa method, PosterPilot writes Kometa/PMM-compatible YAML (url_poster / url_background, keyed by TMDB id) into the directory named by KOMETA_ASSETS_DIR (default /kometa in Docker). Mount that path at your existing Kometa config directory so Kometa applies the covers on its next run. See Usage for how the export is consumed.

That export is a metadata file. PosterPilot can also surgically manage Kometa’s own config.yml — every service connector, per-library collections, overlays and operations, global settings and webhooks, plus a raw editor for anything else — and wire posterpilot.yml into it for you (co-located in the same directory as config.yml). This lives on its own Kometa manager page.

The UI language is resolved per request: (1) the preferred-language setting when it names a supported locale, then (2) the request’s Accept-Language header, then (3) English. Set a preferred language with APP_LANGUAGE, via the Settings page, or with the header language switcher. Supported locales are English (en), Spanish (es), Simplified Chinese (zh), Japanese (ja), and Brazilian Portuguese (pt-BR). An unset or unsupported value falls through to Accept-Language, then English — never an error and never a raw key.

Every operational event is recorded three ways: mirrored to the container console, inserted as a row in the in-app Activity log (Settings → Activity), and appended to a rotating log file. The file is posterpilot.log inside LOG_DIR (default /data/logs in Docker); when it grows past ~5 MB it rotates (posterpilot.log.1.2 …), keeping about five files. Because the default lives under /data, the existing /data volume already persists it — no extra mount is required.

The Activity log table is capped at EVENT_RETENTION rows (default 2000); older rows are pruned automatically. You can wipe the table at any time with the Clear activity button on the Activity tab (this does not delete the on-disk log file).

Every setting below can be supplied as an environment variable. Most are also editable in the Settings page; when set via the environment they take precedence and are locked in the UI.

VariableSettingDefaultMeaning
SERVER_TYPEServer typeplexActive media server: plex, jellyfin, or emby.
PLEX_URLPlex URLPlex base URL, e.g. http://192.168.1.10:32400.
PLEX_TOKENPlex token (secret)Your X-Plex-Token.
PLEX_CLIENT_IDPlex client idgeneratedStable per-install identifier sent to plex.tv for PIN login / discovery.
JELLYFIN_URLJellyfin URLJellyfin base URL (when SERVER_TYPE=jellyfin).
JELLYFIN_API_KEYJellyfin API key (secret)Jellyfin API key.
EMBY_URLEmby URLEmby base URL (when SERVER_TYPE=emby).
EMBY_API_KEYEmby API key (secret)Emby API key.
TMDB_KEYTMDB key (secret)TMDB v3 API key or v4 bearer/JWT (auto-detected).
KOMETA_ASSETS_DIRKometa assets dir./data/kometa (/kometa in Docker)Directory the exported Kometa YAML is written to.
KOMETA_CONFIG_PATHKometa config pathPath to Kometa’s own config.yml to manage. Empty/unset = Kometa manager off.
KOMETA_CONFIG_MODEKometa config modemergemerge (surgical — preserves your other keys and comments) or own (regenerate the whole file).
DEFAULT_APPLY_METHODDefault apply methodbothDefault apply method: plex, kometa, or both.
INCLUDED_SECTIONSIncluded sectionsall movie/showLibrary section keys to sync; comma-separated (env) or a JSON array (persisted). Empty = all.
PROVIDER_MEDIUXMediUX provideronEnable the MediUX provider.
PROVIDER_TMDBTMDB provideronEnable the TMDB-artwork provider.
PROVIDER_FANARTFanart.tv provideroffEnable the Fanart.tv provider (requires FANART_KEY).
PROVIDER_THEPOSTERDBThePosterDB provideroffEnable the ThePosterDB provider.
FANART_KEYFanart.tv key (secret)Fanart.tv API key (the only keyed provider).
MEDIUX_REQUEST_DELAY_MSMediUX request delay2000Delay between MediUX requests, in milliseconds (throttling).
MEDIUX_CONCURRENCYMediUX concurrency5Max concurrent MediUX requests.
HTTP_CACHE_TTL_DAYSHTTP cache TTL7How long cached HTTP responses (scrapes) are reused, in days.
APPLY_CONCURRENCYApply concurrency4How many items a bulk apply processes concurrently.
SUGGEST_PRESELECTSuggested pre-selectonPre-select the top-scored candidate per slot as an overridable suggestion.
INCREMENTAL_SYNCIncremental synconSkip unchanged items on repeat syncs (a full rescan stays available).
LIBRARY_DEFAULT_SORTLibrary default sorttitleSort the library wall opens with: title, year, rating, runtime, recent, or added.
FUN_ENABLEDFUN sectionoffShow the experimental FUN section (random movie/series picker).
THUMB_CACHE_TTL_DAYSThumbnail cache TTL30Days a cached provider preview image stays fresh before it is re-fetched.
THUMB_CACHE_MAX_MBThumbnail cache size512Max on-disk size of the thumbnail cache (MB) before least-recently-used eviction.
APP_LANGUAGELanguage— (auto)Preferred UI locale: en, es, zh, ja, or pt-BR.
LOG_DIR/data/logs (Docker)Folder for the rotating posterpilot.log file (~5 MB × 5 files).
EVENT_RETENTION2000Max number of activity-log rows kept in the database (older rows are pruned).
DATABASE_URLfile:/data/posterpilot.db (Docker)libsql file URL for the SQLite database.
PORT3000Listen port.
APP_SECRET— (auto key)Derives the at-rest encryption key (scrypt); overrides the generated data/.app-key.
APP_KEY_FILE./data/.app-keyPath to the auto-generated instance encryption key file (used when APP_SECRET is unset).

Boolean flags accept 1 / true / on / yes (case-insensitive) for enabled; anything else (or unset) leaves the documented default.

PosterPilot is an independent project, not affiliated with or endorsed by Plex, Jellyfin, Emby, MediUX, Fanart.tv, TMDB, ThePosterDB, or Kometa. Trademarks belong to their respective owners. This product uses the TMDB API but is not endorsed or certified by TMDB.