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 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:
| Environment | Default 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.
Storage performance
Section titled “Storage performance”| Storage | IO pattern | Recommendation |
|---|---|---|
| Data directory | Modest random IO (Postgres), occasional asset reads | Spinning disks, network storage, and shared volumes are all fine. SSD feels nicer but is not required. |
| Transcode cache | Continuous segment writes with immediate read-back during playback | Fastest 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 cache | Small, read-heavy, low churn | Anything convenient. |
| Media libraries | Sequential reads | Your 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.
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.