Skip to content

This is the multi-page printable view of this section. .

Return to the regular view of this page.

Docs

Install Stashix, run your library, and look up exact settings.

Start with Get Started if Stashix is new to you. The Admin Guide covers running the server, and the Reference lists every setting.

1 - Get Started

What Stashix does, what it needs to run, and how to install it.

Stashix is a self-hosted library for comics and ebooks. You point it at the folders where your files already live, and it builds a browsable, searchable shelf you can read from any browser on your network.

What you get

  • A reader in the browser. Open a comic or manga and start reading. Your place is saved per user, so you can pick up on another device.
  • A library that keeps itself up to date. Stashix watches your folders and imports new files a few seconds after they land.
  • Metadata from your files. Series, issue numbers, creators and publishers come from ComicInfo.xml, MetronInfo.xml, sidecar files and filenames. See how to organise your folders.
  • Full-text search across titles, series and publishers.
  • Accounts for the household. Admin and user roles, per-library permissions and age-gates for younger readers.
  • Installable on your phone as a progressive web app on Android.

Stashix reads .cbz, .cbr, .cb7, .pdf and .epub files. Anything else in a library folder is ignored. EPUBs are shown as their images, so they work for manga and comics but not yet for text ebooks such as novels; see supported formats.

How it fits together

Stashix runs as two containers: the Stashix server and a PostgreSQL database. The default Compose file mounts your library folder read-write, so metadata you edit can be saved back into your files. Covers, thumbnails and caches go into a separate data volume.

your library folder ◀─(read-write)─▶ stashix ◀──▶ postgres
                                        │
                                        ▼
                              browser · :4000

Before you install

You needDetails
Docker with ComposeAny host that runs Linux containers: a home server, a NAS or a VPS.
Your library on diskOne folder (or a parent of several) that holds your comics and ebooks.
A user ID that can use itStashix runs as PUID/PGID (default 1000) and must be able to read the library folder, and write to it to save metadata.
Three random secretsGenerated with openssl during setup. See Installation.
A modern browserAny current desktop or mobile browser.

PostgreSQL ships in the Compose file, so you don’t need to install a database yourself.

Choose how to install

Docker ComposeRecommended

Copy .env.example, set your secrets and library path, then run docker compose up -d. A setup wizard opens on first launch.

For development. Needs Elixir 1.18+, Erlang/OTP 27+, PostgreSQL and a few system tools (vips, p7zip, unrar, pdfinfo, pdftoppm).

Once Stashix is running, open http://<your-server>:4000, create the admin account, and add your first library.

1.1 - Installation

Install Stashix with Docker Compose, from an empty folder to your first library.

This guide runs Stashix and its PostgreSQL database with Docker Compose. Check what you need before installing first.

Install

  1. Download the Compose files

    Make a folder for Stashix and fetch the Compose file and the example settings into it:

    mkdir stashix && cd stashix
    curl -fsSLO https://raw.githubusercontent.com/aroy-art/stashix/main/docker-compose.yml
    curl -fsSL -o .env https://raw.githubusercontent.com/aroy-art/stashix/main/.env.example
  2. Fill in the secrets

    Stashix needs three random secrets and a database password. Paste these lines into your terminal, in the stashix folder, to generate them and write them into .env:

    set_env() { sed -i "s|^$1=.*|$1=$2|" .env; }
    DB_PASSWORD=$(openssl rand -hex 24)
    set_env SECRET_KEY_BASE "$(openssl rand -base64 48)"
    set_env JWT_SECRET "$(openssl rand -base64 48)"
    set_env STASHIX_ENCRYPTION_KEY "$(openssl rand -base64 32)"
    set_env POSTGRES_PASSWORD "$DB_PASSWORD"
    set_env DATABASE_URL "ecto://stashix:$DB_PASSWORD@db/stashix"

    Run them once, before the first start. On macOS, change sed -i to sed -i '' in the first line.

    Warning

    Back up .env. If STASHIX_ENCRYPTION_KEY is lost or changed, the metadata source credentials Stashix has stored can’t be read anymore. Upgrading an install that ran without this key? See Adding the key to an older install.

    Prefer to fill them in by hand?

    Open .env and set each value to the output of its command:

    SettingCommand
    SECRET_KEY_BASEopenssl rand -base64 48
    JWT_SECRETopenssl rand -base64 48 (run it again for a different value)
    STASHIX_ENCRYPTION_KEYopenssl rand -base64 32
    POSTGRES_PASSWORDAny password made of letters and numbers
    DATABASE_URLecto://stashix:<password>@db/stashix, with the same password
  3. Point Stashix at your library

    Open .env and set the values only you know:

    SettingSet it to
    LIBRARIES_PATHYour media folder on this machine (the host), such as /srv/media. Not a path inside the container or in Stashix.
    PHX_HOSTThe hostname you’ll use to reach Stashix, such as stashix.home.lan.
    PUID / PGIDA user and group that can read and write LIBRARIES_PATH. id -u and id -g print yours.

    Compose makes LIBRARIES_PATH appear inside the container as /libraries. Stashix only sees that side, so the folders you add as libraries later start with /libraries:

    On this machine           Inside Stashix
    /srv/media           ->   /libraries
    /srv/media/comics    ->   /libraries/comics    <- add as a library
    /srv/media/manga     ->   /libraries/manga     <- add as a library
  4. Start Stashix

    docker compose up -d

    The first start pulls the images and creates the database, which can take a minute. The Stashix image comes from the GitHub Container Registry as ghcr.io/aroy-art/stashix.

  5. Finish setup in the browser

    Open http://<your-server>:4000. A setup wizard asks you to create the admin account. Then add a library. Enter its folder as Stashix sees it, under /libraries (for example /libraries/comics), not the path on your machine.

    Stashix scans it straight away. Watch the progress under Admin → Libraries.

Confirm it’s running

docker compose ps

Both the stashix and db containers should be up, and db should report healthy. If the page doesn’t load, read the server log with docker compose logs stashix.

Where your data lives

WhatWhere
Your comics and ebooksLIBRARIES_PATH on the host, mounted at /libraries
Covers, thumbnails and image cachesThe stashix_data volume
Library, users and reading progressThe postgres_data volume

Back up the postgres_data volume to keep users and reading progress. Covers are extracted again on the next scan if stashix_data is lost.

Libraries on more than one disk

LIBRARIES_PATH mounts one host folder. For media in another place, add a line under volumes: in docker-compose.yml that mounts it under /libraries:

    volumes:
      - ${LIBRARIES_PATH:-${LIBRARY_PATH:-./libraries}}:/libraries
      - /mnt/disk2/manga:/libraries/manga

Run docker compose up -d, then add /libraries/manga as a library.

Metadata in your files

By default Stashix saves metadata you edit or match back to your files: CBZ files get a MetronInfo.xml inside the archive, and other formats get a <book name>.xml file next to them. Your edits then travel with the files and survive a database reset.

To keep your library untouched, mount it read-only:

  1. In Stashix, open Admin → Metadata and turn off Write metadata to files. Metadata is then kept only in the database.
  2. In docker-compose.yml, add :ro to the library mount: ${LIBRARIES_PATH:-${LIBRARY_PATH:-./libraries}}:/libraries:ro.
  3. Run docker compose up -d.

Turn the setting off first: with a read-only mount and the setting on, every metadata change still saves to the database, but each attempt to write the file fails and is logged as an error.

If something goes wrong

The library stays empty. The container user can’t read your files. Check that PUID and PGID match an account with read access to LIBRARIES_PATH, then run docker compose up -d again.

Metadata changes don’t show up in the files. Check that PUID and PGID can write to LIBRARIES_PATH and that the library isn’t mounted with :ro. See Metadata in your files.

Some files are missing. Only .cbz, .cbr, .cb7, .pdf and .epub files are imported. See How the scanner works and File structure.

docker compose up refuses to start. A required secret is empty. The error names it, for example set STASHIX_ENCRYPTION_KEY in .env.

Stashix exits saying STASHIX_ENCRYPTION_KEY is not valid base64 or must decode to 32 bytes. The key was mistyped or generated with the wrong length. Generate it with openssl rand -base64 32 and paste the whole output.

2 - Admin Guide

Run and maintain a Stashix server.

Run and maintain a Stashix server. Start with the scanner: how Stashix finds your files and how to lay out your library folders.

2.1 - Scanner

How Stashix discovers, parses, and imports your library files.

Stashix scans your library folders on demand and in the background whenever files change. This section explains what it expects from your folder structure, how it parses filenames, and what happens during each phase of a scan.

2.1.1 - How the Scanner Works

File discovery, metadata parsing, thumbnail generation, and folder watching.

Supported formats

Stashix reads the following file types:

FormatExtensionNotes
Comic Book ZIP.cbzMost common; ZIP archive of images
Comic Book RAR.cbrRAR archive of images
Comic Book 7-Zip.cb77-Zip archive of images
EPUB.epubImage-based EPUBs, such as manga (see below)
PDF.pdfComics and books as PDF

All other extensions are silently ignored. Files are matched case-insensitively (.CBZ and .cbz are equivalent).

EPUB support is image-only. Stashix opens an EPUB like a .cbz: the images inside become its pages and the chapter text is ignored. Fixed-layout manga and comic EPUBs read normally. Text ebooks such as novels are imported into the library, but the reader shows only their images (often just the cover). A reader for text EPUBs is not available yet.


Scan phases

A full library scan runs through four sequential phases. You can watch progress in Admin → Libraries.

1. Collecting

Stashix walks the library root recursively and lists every file whose extension is in the supported set. The walk is parallelised so even very large libraries (tens of thousands of files) collect quickly.

2. Parsing

Each new or modified file is parsed concurrently (up to 16 at a time). Parsing extracts:

  • Embedded MetronInfo.xml or ComicInfo.xml from ZIP/RAR/7z archives
  • A sidecar <filename>.xml placed next to the file (takes highest precedence)
  • PDF info-dict fields (title, author, creation date) for .pdf files
  • Filename heuristics for anything not covered by embedded metadata

Files whose path and modification time have not changed since the last scan are skipped entirely.

3. Importing

Parsed files are upserted into the database:

  • A new file that shares a hash with an existing record is treated as a move — the book record is preserved and its path updated.
  • A file at a known path whose modification time has advanced is updated in place.
  • A file at an unknown path with an unknown hash creates a new book.

4. Thumbnails

After all books are imported, cover thumbnails are generated (up to 4 at a time). The scanner looks for a sidecar image first:

Batman 001.cbz
Batman 001.jpg   ← used as cover if present

Supported sidecar extensions: .jpg, .jpeg, .png, .webp.

If no sidecar exists, the first image inside the archive becomes the cover. Thumbnails are stored as JPEG in the configured thumbnail directory and a BlurHash is computed for smooth loading placeholders.


Orphan detection

After importing, Stashix compares the files found on disk with the database. Any book whose file no longer exists is soft-deleted (marked with a deletion timestamp). It is hidden from all library views but its metadata and reading progress are retained. Re-adding the file restores it.

Series with no remaining books are also soft-deleted.


Folder watching

Stashix watches your library folders for filesystem events using the OS native API (inotify on Linux, FSEvents on macOS). When a supported file or its sidecar is created, modified, or moved into a watched folder, the scanner queues a single-file scan after a 5-second debounce — rapid saves (e.g. a download in progress) do not trigger repeated scans.

Events for files Stashix itself just wrote (metadata write-back) are suppressed for 30 seconds to avoid re-importing its own output.


Forcing a rescan

A normal scan skips files whose path and modification time are unchanged. To re-parse everything regardless:

  1. Go to Admin → Libraries
  2. Click the ⋯ menu next to a library
  3. Choose Force rescan

You can also trigger a rescan on a single series from the series detail page.

2.1.2 - File Structure

How to organise your library folders so Stashix groups books into series correctly.

The scanner uses folder structure as its primary grouping signal — a subfolder becomes a series, the files inside become its issues. Metadata embedded in the files or their names enriches that grouping but does not override it.


The two patterns

Place all issues of a series in one folder named after the series. The folder name becomes the series name.

/comics/
└── Batman (1940-2011)/
    ├── Batman 001 (1940).cbz
    ├── Batman 002 (1940).cbz
    └── Batman 500 (1993).cbz

Stashix strips common year and date suffixes from the folder name and stores them as start_year / end_year on the series record. Supported patterns:

Folder nameSeries nameStartEndOngoing
Batman (1940-2011)Batman19402011No
The Disavowed (2025-)The Disavowed2025—Yes
AD Police (1994)AD Police1994—No
SagaSaga——No

Standalone books at the library root

A file placed directly in the library root (not inside any subfolder) is imported as a standalone book with no series.

/books/
├── The Complete Persepolis.cbz   ← standalone
└── Watchmen.cbz                  ← standalone

Standalone folders

You can also designate a folder as a standalone container — every file inside is treated as a standalone book. Configure additional folder names in Admin → Libraries → Standalone folders. The built-in defaults are:

  • one-shot
  • one shot
  • oneshot
/comics/
└── one-shot/
    ├── Batman Noel.cbz      ← standalone
    └── Superman Smashes.cbz ← standalone

Optional: publisher folder

If your library has a three-level hierarchy (publisher → series → issues), Stashix infers the publisher from the grandparent folder for new books:

/comics/
└── DC Comics/           ← inferred publisher
    └── Batman (1940)/
        ├── Batman 001.cbz
        └── Batman 002.cbz

The publisher is only inferred at import time for new books. Renaming the folder later does not automatically update existing books.


Metadata precedence

Within a single file, Stashix merges metadata from multiple sources. The order of precedence, highest first:

  1. Sidecar XML — <filename>.xml next to the file (MetronInfo or ComicInfo format)
  2. Embedded MetronInfo.xml — inside the archive
  3. Embedded ComicInfo.xml — inside the archive (or the PDF info-dict for .pdf)
  4. Filename parsing — series name, issue number, volume, year

Later sources fill in gaps; earlier sources win on any field they provide. Exception: ComicInfo.Year beats a year derived from MetronInfo’s cover-date fields.


Filename patterns

When no embedded metadata is present, Stashix extracts the series name, issue number, volume, and year from the filename. The parser tries the following patterns in order and stops at the first match:

PatternExample filenameExtracted
Series NN (YEAR)Batman 001 (1940)series=Batman, issue=1, year=1940
Series Vol. NN (YEAR)Pariah Vol. 01 (2014)series=Pariah, volume=1, year=2014
Series NN - Title (YEAR)The Bank 01 - The Waterloo Insider (Cinebook 2025)series=The Bank, issue=1, title=The Waterloo Insider, year=2025
Series - cNN Title (YEAR)Amulet - c01 The Stonekeeper (2008)series=Amulet, issue=1, title=The Stonekeeper, year=2008
Series - cNN - TitleAKIRA - c001 - The Highwayseries=AKIRA, issue=1, title=The Highway
Issue N - Title (YEAR)Issue 6 - Angel of Death (1996)issue=6, title=Angel of Death, year=1996
Series (YEAR) - Issue NGhost in the Shell (1991) - Issue 02series=Ghost in the Shell, year=1991, issue=2

Noise tags — (Digital), (HQ), (Webrip), (c2c), (Retail), (FIXED), (v2), etc. — are stripped before parsing. The (of N) suffix is also removed.


Tips and common mistakes

Do keep each series in its own folder, named consistently. The scanner detects folder renames by comparing file hashes, so your reading progress survives renaming a series folder.

Do not mix multiple series in one folder. Stashix uses one folder = one series, so all files in the same directory end up in the same series.

Do not nest series inside each other more than one level below the library root unless you use the publisher pattern. Deep nesting is supported by the recursive walker but only one folder level above the file becomes the series.

Do use a sidecar XML (Batman 001.xml) when you want reliable metadata that overrides filename guesses. The ComicInfo.xml or MetronInfo.xml format is both accepted.

3 - Reference

Look up exact settings and behaviour.

Look up exact settings when you already know what you need.

3.1 - Configuration

Every environment variable Stashix reads, with defaults and what each one does.

Stashix is configured with environment variables. With Docker Compose they live in the .env file next to docker-compose.yml. See Installation for a first setup.

Required

VariableDefaultWhat it does
SECRET_KEY_BASEnoneEncrypts and signs sessions and cookies. Generate with openssl rand -base64 48.
JWT_SECRETnoneSigns login tokens. Use a different value from SECRET_KEY_BASE.
STASHIX_ENCRYPTION_KEYnoneBase64 32-byte key that encrypts stored metadata source credentials. Generate with openssl rand -base64 32.
DATABASE_URLpostgresql://stashix:stashix@db/stashixPostgreSQL connection, as ecto://user:password@host/database.
LIBRARIES_PATH./librariesYour media folder on the host, not a path inside the container. Compose mounts it read-write at /libraries, and each library in Stashix points at a folder under that, such as /libraries/comics. Read-write so metadata can be saved to your files; see Metadata in your files.

LIBRARIES_PATH is used only by Docker Compose to mount the folder; Stashix itself never reads it. It used to be called LIBRARY_PATH, and .env files with the old name keep working.

docker compose up refuses to start while SECRET_KEY_BASE, JWT_SECRET or STASHIX_ENCRYPTION_KEY is empty. Stashix also refuses to start if STASHIX_ENCRYPTION_KEY isn’t valid base64 or doesn’t decode to 32 bytes.

Warning

Don’t change STASHIX_ENCRYPTION_KEY once it’s set: credentials encrypted with the old key become unreadable.

Adding the key to an older install

Installs that started without STASHIX_ENCRYPTION_KEY encrypted their stored credentials with a key derived from JWT_SECRET. To upgrade, generate a key, add it to .env and restart. On that start Stashix re-encrypts the existing credentials with the new key, and from then on they no longer depend on JWT_SECRET.

Don’t change JWT_SECRET in the same restart: Stashix needs the old value to read the credentials it is re-encrypting. Rotate it on a later restart.

VariableDefaultWhat it does
PHX_HOSTlocalhostPublic hostname, used when Stashix builds links.
PUID / PGID1000 / 1000User and group the server runs as. They must be able to read and write LIBRARIES_PATH. The data volume is handed to them on start.

Database

These configure the bundled PostgreSQL container. Keep the user and password in step with DATABASE_URL.

VariableDefaultWhat it does
POSTGRES_DBstashixDatabase name.
POSTGRES_USERstashixDatabase user.
POSTGRES_PASSWORDstashixDatabase password. Change it.
POOL_SIZE10Database connections Stashix keeps open.
ECTO_IPV6falseSet to true or 1 to reach the database over IPv6.

Storage

VariableDefaultWhat it does
DATA_DIR/data in DockerBase folder for everything Stashix generates.
THUMBNAIL_DIRDATA_DIR/thumbnailsCover thumbnails extracted from your files.
IMAGE_CACHE_DIRDATA_DIR/cache/images/resizedResized and WebP cover images.
METADATA_IMAGE_DIRDATA_DIR/cache/images/metadataImages from metadata sources.

All of these are created on start. Covers are extracted again on the next scan if the folders are lost, but keep them on a persistent volume to avoid the rescan.

Server

VariableDefaultWhat it does
PORT4000HTTP port inside the container. To reach Stashix on another port, change the ports mapping in docker-compose.yml instead.
DNS_CLUSTER_QUERYnoneDNS query used to find other nodes when clustering.

Prometheus metrics are served on a separate port, 9568, at /metrics.

Settings Compose doesn’t pass through

The Compose file forwards only the variables it uses, and it pins PORT to 4000 and DATA_DIR to /data. To use POOL_SIZE, ECTO_IPV6, DNS_CLUSTER_QUERY or the individual storage folders, add them to the environment block of the stashix service:

services:
  stashix:
    environment:
      POOL_SIZE: 20