Skip to main content

Configuration

Everything is a Spring property and every property is overridable via environment variables (relaxed binding: app.ister.server.nameAPP_ISTER_SERVER_NAME). Many settings also have a dedicated short env var, listed below. Defaults are sensible for a single-node home setup.

Core services

SettingEnv varDefaultNotes
Database host / port / nameDB_HOST / DB_PATH / DB_NAMElocalhost / 5432 / isterPostgreSQL
Database credentialsDB_USER / DB_PASSWORDister / isterchange in production
Connection poolDB_POOL_SIZE20playback fans out to many concurrent queries
Connection timeout (ms)DB_CONNECTION_TIMEOUT10000how long a thread waits for a pooled connection before failing
RabbitMQSPRING_RABBITMQ_HOST / _PORT / _USERNAME / _PASSWORDlocalhost, 5672, user/passwordport defaults to the RabbitMQ default 5672
OIDC issuerOIDC_URLKeycloak-compatible; e.g. https://keycloak.example.com/realms/Home

Identity and paths

SettingEnv varDefaultNotes
app.ister.server.nameAPP_ISTER_SERVER_NAMETest servermust be unique per node
app.ister.server.urlAPP_ISTER_SERVER_URLhttp://localhost:8080how clients and other nodes reach this node
app.ister.cluster.nameAPP_ISTER_CLUSTER_NAMEthe server namesame value on every node of a cluster
Cache directoryCACHE_DIR/cache/images, podcast downloads
Temp directoryTMP_DIR/tmp/ister/HLS transcode output

Metadata and languages

SettingEnv varDefaultNotes
app.ister.server.TMDB.apikeyAPP_ISTER_SERVER_TMDB_APIKEYunsetTMDB API read access token. Without it, movie/show metadata fetching is skipped — you get bare filenames.
app.ister.server.TMDB.max-requests-per-second30stays under TMDB's ~40 rps limit
app.ister.languagesISTER_LANGUAGESen,nlcomma-separated ISO-639-1 tags; first = primary. Drives which languages metadata is fetched in and which languages search indexes. Changing it requires a re-scan plus reindexSearch — see Search.

Search (Typesense)

Env varDefault
TYPESENSE_ENABLEDfalse
TYPESENSE_HOST / TYPESENSE_PORT / TYPESENSE_PROTOCOLlocalhost / 8108 / http
TYPESENSE_API_KEY / TYPESENSE_COLLECTIONempty / media

See Search for the enable/reindex procedure.

Transcoding

SettingEnv varDefaultNotes
FFmpeg directoryFFMPEG_DIR/usr/bindirectory holding ffmpeg/ffprobe
mkvextract / subtile-ocrMKVEXTRACT / SUBTILE_OCR/usr/bin/...image-subtitle extraction and OCR
app.ister.transcoder.hls.hwaccelHLS_HWACCELnonevaapi (Intel/AMD) or nvdec (NVIDIA); the compose file shows the required device mappings
app.ister.transcoder.hls.hwaccel-deviceHLS_HWACCEL_DEVICE/dev/dri/renderD128VAAPI only
app.ister.transcoder.hls.max-concurrent-filesHLS_MAX_CONCURRENT_FILES2files transcoded simultaneously; pre-transcoding shares this budget

Advanced transcoding

Rarely touched — the defaults suit most setups. These have no dedicated short env var (use relaxed binding, e.g. APP_ISTER_TRANSCODER_HLS_MAX_BACKGROUND_FILES). The mechanics behind them are in the architecture guide, Transcoding.

PropertyDefaultNotes
app.ister.transcoder.hls.max-concurrent-passes4FFmpeg passes running at once (thread pool size)
app.ister.transcoder.hls.max-background-files1files a background pre-transcode may hold, kept below the interactive budget
app.ister.transcoder.hls.max-background-passes2passes a background pre-transcode may hold
app.ister.transcoder.hls.background-nice10nice value for background passes so interactive playback wins the CPU
app.ister.transcoder.hls.nice-path/usr/bin/nicepath to the nice binary used for the above
app.ister.transcoder.hls.cache-retention-hours2a transcode cache dir is removed only after being untouched this long (and past its keep-until)
app.ister.transcoder.hls.segment-stability-ms200a segment file must be unchanged this long before it counts as finished
app.ister.transcoder.hls.pass-timeout-multiplier4pass timeout = media duration × this
app.ister.transcoder.hls.pass-timeout-min-seconds1800…but never below this floor
app.ister.transcoder.hls.pass-stall-timeout-seconds60abort a pass that writes no new segment for this long
app.ister.transcoder.hls.upload-drain-timeout-ms300000multi-node: how long to keep uploading segments to the requesting node after a pass ends
app.ister.server.hls.segment-timeout-ms60000how long an HTTP .ts request waits for the encoder to produce that segment
app.ister.server.hls.master-playlist-timeout-ms120000how long a master.m3u8 request waits for playlists to be generated

Pre-transcoding and prefetch

Two background mechanisms warm the transcode cache so playback starts instantly. Pre-transcoding works from the continue-watching list; prefetch reacts to what a play queue is doing right now. See the architecture guide, Transcoding.

PropertyDefaultNotes
app.ister.server.pretranscode.keep-minutes30how long a pre-transcoded file is kept warm before it may be swept
app.ister.server.prefetch.enabledtrueprefetch the next item in a play queue during playback
app.ister.server.prefetch.video-threshold-seconds120start prefetching the next video once this many seconds into the current one
app.ister.server.prefetch.track-threshold-seconds60same, for audio tracks
app.ister.server.prefetch.track-depth2how many upcoming tracks to prefetch
app.ister.server.prefetch.keep-hours24how long a prefetched file is kept warm

Continue watching

Env varDefaultNotes
CONTINUE_WATCHING_HISTORY_DAYS150how far back the continue-watching list looks; also drives what pre-transcoding keeps warm
CONTINUE_WATCHING_REBUILD_CRON0 30 3 * * *nightly self-heal rebuild
CONTINUE_WATCHING_REBUILD_ENABLEDtrue

Cache cleanup and podcasts

SettingDefaultNotes
CACHE_CLEANUP_ENABLED / CACHE_CLEANUP_CRONtrue / 0 30 4 * * *daily zombie sweep of cache and tmp dirs
CACHE_CLEANUP_DRY_RUNtrueit only logs until you set this to false — see Maintenance
CACHE_CLEANUP_MIN_AGE24hnever deletes files younger than this
app.ister.server.cache-cleanup.podcast-retention-days30downloaded podcast episodes expire after this, unless someone is mid-episode
app.ister.worker.podcast.auto-download-count3newest episodes auto-downloaded per feed
app.ister.worker.podcast.refresh-cron0 10 * * * *hourly feed refresh
app.ister.worker.podcast.refresh-min-interval-minutes30a feed is not re-fetched again within this window, even if the cron fires

External metadata endpoints

Every external service the server calls is a property whose default is the real service, so you normally set none of these. They exist so a deployment can route through a proxy or mock — the chart's CI points them all at one WireMock pod:

spring.cloud.openfeign.client.config.tmdb.url, app.ister.worker.tmdb.image-base, app.ister.worker.musicbrainz.base / .coverart-release-base / .coverart-release-group-base / .commons-filepath-base, app.ister.worker.openlibrary.base / .covers-base / .author-photo-base, app.ister.worker.wikidata.entity-base / .api-base, app.ister.worker.wikipedia.summary-template, app.ister.api.podcast.itunes-base.

Libraries and directories

app.ister.disk.libraries[n].* and app.ister.disk.directories[n].* define what gets scanned — covered in full in Libraries and media layout. Dedicated transcoder nodes are assigned disks with app.ister.transcoder.disks[n].name — see Multi-node.

Health, metrics, and other internals

The Spring Actuator (/actuator/health, /actuator/metrics, /actuator/prometheus) runs on its own port so it stays off the public API. See Installation.

PropertyDefaultNotes
management.server.port8081port for the Actuator endpoints
management.endpoints.web.exposure.includehealth,metrics,prometheuswhich Actuator endpoints are exposed
app.ister.server.blur-hash.chunk-size500images processed per chunk during the BlurHash sweep (keeps a chunk under the RabbitMQ consumer timeout)

Local overrides (development)

When running from source, machine-specific settings go in gitignored *-local.properties files next to the module's properties file (e.g. core/src/main/resources/core-local.properties); ./gradlew bootRun activates the local profile automatically. Container deployments should use environment variables instead.