Skip to content

Storage

Odeion uses two classes of filesystem storage: a single data directory for persistent state, and one or more cache directories for transient processing output. They have different contents, different backup needs, and very different performance profiles. Media files themselves always live on your own storage and are never copied into either.

The data directory is where Odeion keeps everything that needs to survive restarts: the embedded Postgres cluster, generated artwork and preview assets, downloaded tooling, backups, and logs.

The data directory is resolved from, in order:

  1. The --data-dir flag on odeion serve and related subcommands.
  2. The ODEION_DATA_DIR environment variable.
  3. A platform default.

All subcommands that touch persistent state (serve, doctor, psql, reset-password, config) accept the same flag and environment variable. They must agree across commands so they point at the same Postgres cluster.

EnvironmentDefault path
Docker image/var/lib/odeion
XDG_DATA_HOME set$XDG_DATA_HOME/odeion
Otherwise~/.local/share/odeion

Odeion creates the root and the standard subdirectories with mode 0700 on first startup.

odeion-data/
├── postgres/ embedded Postgres cluster
├── postgres-bin/ extracted Postgres binaries
├── postgres-cache/ downloaded Postgres archives
├── backups/ archives from Admin > Backups
├── thumbnails/ still thumbnails and animated previews
├── trickplay/ sprite sheets for timeline scrubbing
├── subtitles/ extracted and downloaded subtitle files
├── assets/
│ ├── blobs/ original upstream artwork bytes
│ └── variants/ generated resizes and format conversions
├── bin/ downloaded tooling (ffmpeg, ffprobe)
└── logs/ server and Postgres logs

postgres/ contains the embedded Postgres data cluster when you use the built-in database: WAL segments, relation files, postgresql.conf, pg_hba.conf, and the superuser credential files (.pg_password, .pg_password_ro). Empty when you point Odeion at an external Postgres server.

backups/ stores archives produced by the built-in backup system, named like odeion-backup-2026-04-14-030000.tar.zst. Odeion does not rotate them; retention is your responsibility. See Backups for the mechanics.

thumbnails/ holds still JPEG thumbs (a few KB each) and animated WebP seek previews (typically 1 to 5 MB per item). A 1,000 item library with previews enabled lands in the 1 to 5 GB range.

trickplay/ holds sprite sheets used for timeline scrubbing. Each item gets a sequence of JPEG sheets and a manifest.json describing the grid. Only populated when the trickplay feature is enabled in setup.

assets/ is a content-addressed store for artwork. assets/blobs/ keeps the original bytes downloaded from upstream metadata providers, keyed by SHA-256. assets/variants/ keeps transcoded and resized versions at the specific dimensions the UI requests. Both repopulate on demand.

subtitles/ holds subtitle files extracted from media and fetched from external providers. Plain text, negligible size.

postgres-bin/ and postgres-cache/ hold the extracted Postgres binaries and the downloaded archives they came from. Populated only when using the embedded database, and re-downloaded on next startup if removed.

bin/ is where Odeion drops helper binaries it downloads itself, currently ffmpeg and ffprobe when they are not found on PATH.

logs/ contains rotated server and Postgres log files. Small.

Separate from the data directory, Odeion writes transient processing output to dedicated cache directories. Unlike the data directory, nothing here is load-bearing: caches can be wiped at any time and will rebuild on demand, at the cost of reprocessing work.

There are two cache directories, each individually configurable from the setup wizard and from Admin > Settings > Paths:

  • Transcode cache. HLS segments, remuxed streams, and intermediate outputs produced while a client is streaming. Grows quickly during playback. Bounded by a size cap (default 32 GB) that Odeion enforces by evicting the oldest segments.
  • Metadata cache. Cached responses from upstream metadata providers (TMDB, TVDB, FanartTV, and so on). Small, read-heavy, low churn.

Defaults by environment:

EnvironmentDefault root
Docker/var/cache/odeion/
Otherwise~/.cache/odeion/

Inside that root you get transcode/ and metadata/ subdirectories. Subtitle files are not cache data; they always live in subtitles/ under the data directory.

StorageIO patternRecommendation
Data directoryModest random IO (Postgres), occasional asset readsSpinning disks, network storage, and shared volumes are all fine. SSD feels nicer but is not required.
Transcode cacheContinuous segment writes with immediate read-back during playbackFastest local storage you have. NVMe ideal, SATA SSD fine, spinning disks workable for 1-2 light sessions. Avoid NFS/SMB; per-request latency dominates.
Metadata cacheSmall, read-heavy, low churnAnything convenient.
Media librariesSequential readsYour existing storage; Odeion never writes to it.

Slow transcode-cache storage shows up directly as slow stream startup, rebuffering, sluggish seeks, and reduced concurrent-session capacity.

Mount the data directory as a named volume or bind mount so it survives container replacement:

volumes:
- odeion-data:/var/lib/odeion

Put the cache on its own volume so you can wipe it independently, and point it at fast local storage following the guidance above:

volumes:
- odeion-cache:/var/cache/odeion

A common pattern is to back odeion-data with a general-purpose volume on bulk storage and back odeion-cache with a bind mount onto an NVMe device.