Skip to main content

Installation

This chapter takes you from "it works on my machine" (Quick start) to an installation you keep: what the stack needs from the host, what each piece does, and how to run and upgrade it. All examples use the container images; they work identically with Docker and Podman.

Prerequisites​

  • A container runtime with Compose v2 (Docker, or Podman with podman compose/a recent podman-compose). The compose file relies on depends_on: condition: service_completed_successfully, which Compose v1 does not support.
  • x86_64 host. The published native images are built for amd64.
  • Memory and CPU: the server itself is a GraalVM native image and idles well under 512 MB; PostgreSQL, RabbitMQ and (optionally) Typesense and Keycloak come on top. 4 GB RAM is a comfortable floor. Transcoding is CPU-bound unless you enable hardware acceleration — budget cores accordingly.
  • Disk: besides your media (read-only is fine), the server needs a persistent cache directory (artwork, downloaded podcast episodes) and a transcode temp directory (HLS segments; sized by what people stream, safe to lose). Both are volumes in the compose file.
  • SELinux hosts (Fedora, RHEL): keep the :Z suffix on the bind mounts, as the shipped compose file does, or the containers cannot read them.
  • File permissions: the media mount must be readable by the container user; the cache and temp mounts must be writable.
  • An OIDC provider for anything beyond trying it out. The compose file bundles a development Keycloak; production wants your own provider — see the OIDC section below.

The reference stack​

docker-compose.yml in the repository root is the reference deployment:

ServiceRole
databasePostgreSQL 18, the single source of truth (bind mount ./db-data/)
rabbitMQMessage broker; management UI on port 15672
keycloakDevelopment OIDC provider with a pre-imported Ister realm
migrationsOne-shot Flyway job; the server waits for it to complete
serverThe application, on port 8080

Copy it, fill in your own values (see Configuration), and start it:

docker compose up -d

The quick start walks through this file end to end, including test media and a first scan. Two sibling files in the repository are development aids, not deployment templates: docker-compose-local.yml is the maintainer's local-dev file (the search setup it touches is documented in chapter 06), and docker-compose-nodes-local.yml is a worked multi-node example (chapter 05).

The OIDC provider​

Ister does not manage users; it validates JWTs from the provider configured in OIDC_URL (chapter 09). The bundled Keycloak runs in development mode with a test account (ister/ister) and wildcard redirect URIs — fine for evaluating, not for an installation others sign in to. For production, either harden it (production mode, TLS, real accounts, tight redirect URIs) or point OIDC_URL at the provider you already run. Whatever you use must put its roles in a roles claim; user grants access, admin grants administration — the details are in chapter 09.

Volumes that must persist​

  • ./db-data/ — the database. Everything lives here; back it up (chapter 07).
  • ./cache-data/ (CACHE_DIR) — artwork and podcast downloads. Losing it means re-fetching everything from the metadata providers.
  • ./transcode-tmp/ (TMP_DIR) — HLS segments. Safe to lose between restarts, but it must be writable and can grow while people stream.
  • ./media/ — your media, mounted read-mostly; the server writes nothing into it.

The images​

Two images are published to GHCR, always tagged in lockstep:

ImagePurpose
ghcr.io/ister-app/serverThe server itself (GraalVM native image, Fedora base with FFmpeg and mkvtoolnix included)
ghcr.io/ister-app/migrationsA Flyway image carrying the database migrations

Releases get clean semver tags (<major>.<minor>.<patch>, see the releases page for the current one); every push to main is additionally tagged with the next snapshot version (<version>-SNAPSHOT) and main. Pin the server and migrations images to the same tag so schema and code never drift apart.

Database migrations​

The schema is managed by Flyway and migrations are forward-only. You have two options:

  • Run the migrations image before the server starts (as the compose file does: the server depends_on the migrations container completing). This is the recommended pattern — it is also how the Kubernetes chart does it.
  • Let the server migrate on boot: spring.flyway.enabled=true is the default, so a server started against an outdated database brings it up to date itself.

Either way, upgrading is: pull the new image pair, run migrations, start the server. The server validates the schema at startup (ddl-auto=validate) and refuses to boot against a wrong one — a loud failure, never silent corruption.

First start​

On every boot the server reconciles its configuration with the database (StartupTasks):

  • creates or updates its node row (name, URL, cluster),
  • creates libraries and directories from the app.ister.disk.* configuration,
  • creates the cache directories on disk,
  • validates the multi-node configuration and logs any problems.

So on a fresh install there is nothing to click through: configure your libraries (chapter 04), start the server, sign in through your OIDC provider, and trigger a scan. Renaming a library or directory in config later is picked up on the next start.

Health, metrics, logs​

Management endpoints listen on a separate port, 8081:

  • http://host:8081/actuator/health — liveness/readiness
  • http://host:8081/actuator/metrics and /actuator/prometheus — metrics, Prometheus format

Keep 8081 internal; only port 8080 needs to be reachable by clients. Logs go to stdout; raise verbosity with e.g. LOGGING_LEVEL_APP_ISTER=DEBUG.

Kubernetes​

The chart repository provides a Helm chart that deploys the same pieces (migrations as an init job included) and runs a full end-to-end suite against it in CI. If you run Kubernetes, start there rather than translating the compose file yourself.

Building images yourself​

From a repository checkout:

./gradlew nativeCompile # GraalVM native binary
docker build -f Dockerfile.native -t ister-server .
docker build -f Dockerfile.migrations -t ister-migrations .

./gradlew bootBuildImage builds a JVM-based image via buildpacks — fine for testing, but the native image is what production runs. Dockerfile.native also bakes in FFmpeg with VAAPI drivers and mkvtoolnix, so prefer it.

Where to next​