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.
Data directory
Section titled “Data directory”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.
Configuring the location
Section titled “Configuring the location”The data directory is resolved from, in order:
- The
--data-dirflag onodeion serveand related subcommands. - The
ODEION_DATA_DIRenvironment variable. - 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.
| Environment | Default 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.
Layout
Section titled “Layout”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 logsWhat each directory holds
Section titled “What each directory holds”Load-bearing
Section titled “Load-bearing”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.
Assets & data
Section titled “Assets & data”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.
Cache directories
Section titled “Cache directories”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 three cache directories, each individually configurable from the setup wizard and from Admin > Settings:
- 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.
- Subtitle cache. Extracted and downloaded subtitle files. In Docker
this is merged into
subtitles/under the data directory; in other deployments it defaults to its own cache subdirectory.
Defaults by environment:
| Environment | Default root |
|---|---|
| Docker | /var/cache/odeion/ |
| Otherwise | ~/.cache/odeion/ |
Inside that root you get transcode/, metadata/, and (outside Docker)
subtitles/ subdirectories.
Storage performance
Section titled “Storage performance”The two classes of storage have very different requirements.
The data directory does not need fast storage. Postgres benefits modestly from low-latency SSDs, and the thumbnail and asset directories are accessed often enough that a fast disk feels nicer, but there is no hard requirement. Spinning disks, network storage, and shared volumes are all fine for the data directory in typical deployments.
The cache directories, especially the transcode cache, do. During playback Odeion continuously writes new transcode segments and reads them back a moment later to serve the client. Slow or high-latency storage directly translates into:
- Longer startup times when a stream begins.
- Stalls and rebuffering during playback when segment writes back up.
- Sluggish seeks, since the target segment has to be produced and flushed before it can be served.
- Reduced concurrency, because every active session competes for the same I/O budget.
For the transcode cache, prefer the fastest local storage you have available. NVMe is ideal, SATA SSD is fine, and rotational disks are workable for one or two light sessions but fall over quickly under concurrent transcoding. Network-mounted storage (NFS, SMB) is a poor fit here regardless of headline throughput because the per-request latency dominates.
The metadata and subtitle caches are much less sensitive and can share whatever storage is convenient.
Docker deployment
Section titled “Docker deployment”Mount the data directory as a named volume or bind mount so it survives container replacement:
volumes: - odeion-data:/var/lib/odeionPut 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/odeionA 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.