Library Structure
Grimoire derives game systems, categories, and groups entirely from your folder structure, with no manual configuration required.
Top-level layout
library/
├── books/ ← PDF collection (and archive files)
├── maps/ ← battle maps and scene images
├── tokens/ ← character tokens and portraits
├── audio/ ← ambient tracks, music, and sound effects
└── models/ ← 3D models for printingBooks
Each top-level folder under books/ becomes a game system. Subfolders are auto-detected as categories.
books/
└── Dungeons and Dragons 5e/ ← game system
├── core/ ← category: Core Rulebooks
│ ├── Players Handbook.pdf
│ └── monsters/ ← subfolder group "Monsters"
│ └── Monster Manual.pdf
├── supplements/ ← category: Supplements
├── adventures/ ← category: Adventures
│ └── Curse of Strahd/ ← subfolder group "Curse of Strahd"
│ ├── Curse of Strahd.pdf
│ └── Strahd DM Screen.pdf
└── homebrew/ ← category: HomebrewCategory folder names
Folder name matching is case-insensitive and treats hyphens, underscores, and spaces as equivalent.
| Category | Recognized folder names |
|---|---|
| Core Rulebooks | core, rulebooks, rules, players, gms, game masters |
| Starter Set | starter-set, starter kit, beginner box, boxed set, essentials |
| Supplements | supplements, sourcebooks, sources, expansions |
| Adventures | adventures, modules, campaigns |
| Character Sheets | character-sheets, character sheets, charsheets, sheets |
| Handouts | handouts, reference, screen |
| Homebrew | homebrew, custom, house-rules, unofficial |
| Magazines & Zines | magazines, zines, fanzines, periodicals, journals, issues, back issues |
TIP
players, gms, game masters, sheets, sources, journals, issues and back issues count only when they are the whole folder name. In a longer name the more specific word decides: Player Handouts and GM Screens are handouts, Cheat Sheets stays with handouts, Source Adventures are adventures, and Zine Maps are maps.
Files placed directly in a system folder (not in a subfolder) default to the Core Rulebooks category.
Any unrecognized subfolder name becomes its own category, slugified from the folder name, so Bestiary becomes the bestiary category.
Disabling category inference
If you organize your folders differently and don't want Grimoire deriving categories from folder names, you can turn the behavior off. When inference is disabled, books fall back to the neutral uncategorized category (any category you set in the web UI is still respected).
There are two ways to disable it, applied in this order of precedence:
- Globally — turn it off for the whole library in Settings → Library → Files & folders → Folder Category Inference, or pin it with the
DISABLE_FOLDER_CATEGORY_INFERENCEenvironment variable. When the env var is set, the in-app toggle shows the effective value read-only. - Per game system — place an empty file named
.no-auto-categoryat a system's folder root (e.g.books/My System/.no-auto-category) to disable inference for just that system while leaving the rest of the library on the default behavior.
The change takes effect on the next scan of the affected books.
Long systems start collapsed
When a game system has more than 25 books, its category sections start collapsed, so the page opens on an overview of the categories rather than one long list. Core Rulebooks is the exception: if it holds five books or fewer, it stays open. A system with only one category always starts open.
Filtering the list down to 25 books or fewer opens the categories again. Once you open or close a category yourself, Grimoire keeps your choice for the rest of the session instead.
The same rule applies to other grouped lists that hold more than 25 items: favorites, the tags list and a tag's items, search results, campaign resources, the campaign notes page tree, and a book's table of contents.
Subfolder groups
Any category folder can contain named subfolders to group related books. Grimoire detects these automatically and displays them as collapsible groups, with no configuration needed.
By default, folders are listed ahead of the loose books beside them, like most file managers. The same rule applies at every level, both in a category and inside a folder. Subfolder groups include a download button for the whole group.
To sort folders in among the books instead, open Settings → Preferences → Reading & browsing → Library Browsing and set Folders in book lists to Mixed with books. Each folder then takes its place by name, so a series where one volume has companion material still reads in order:
Aventurisches Jahrbuch 1035 BF
[folder] Aventurisches Jahrbuch 1036 BF
Aventurisches Jahrbuch 1037 BFFolders follow the sort you pick in the toolbar, including its direction. Sorting by year, page count, size, or product code, a folder has no value of its own, so it sits where its first book would. The setting is saved in your browser, like the other Library Browsing preferences.
Archive files
Archive files placed anywhere under books/ are shown alongside your books in their category, handy for bundling related files (a maps pack, a COMP/CON export, loose handouts) next to the book they belong to. Recognized extensions:
| Type | Extensions |
|---|---|
| Zip | .zip, .cbz |
| RAR | .rar, .cbr |
| 7-Zip | .7z, .cb7 |
| Tar | .tar, .cbt, .tar.gz, .tgz, .tar.bz2, .tbz2 |
Archives are treated as opaque downloads: Grimoire does not extract or read their contents, so clicking one downloads the file rather than opening the reader. They're included when you download a whole system, category, or subfolder as an archive. Comic-book archives (.cbz, .cbr, .cb7, .cbt) additionally get a cover thumbnail generated from the first image inside them.
System-agnostic collections
Some books don't belong to a single game system. Create a folder with one of these names and Grimoire displays its contents in a separate Special Collections section:
| Folder name |
|---|
System Agnostic |
Generic |
Any |
Subfolders directly under the agnostic root become custom category headings, and the folder name is used as-is.
One-page and micro RPG collections
Some "systems" are really a bucket of many tiny games. Folders with one of these names are treated as a sub-library: a single tidy entry in the Special Collections strip whose contents are each their own game system.
| Folder name |
|---|
One-Page RPGs |
Single-Page RPGs |
One-Shot RPGs |
Micro RPGs |
Both subfolders and loose files at the root become systems:
books/
└── One-Page RPGs/
├── honey-heist.pdf → system "Honey Heist" (1 book)
├── lasers-and-feelings.pdf → system "Lasers And Feelings" (1 book)
└── cbr+pnk/ → system "Cbr+pnk" (2 books)
├── core/
│ └── core-rules.pdf
└── character-sheets/
└── character.pdfA single-file game becomes a system holding that one book; a folder-backed game keeps its internal category structure. Either way, each game gets full system metadata, tags, and system-level filtering — so a pile of one-shot PbtA games can all be found by system without cluttering the main library grid.
Systems count
Games nested inside a collection count toward your library's game-system total. If you already use a One-Page RPGs folder, that number rises after the first rescan as each game inside becomes its own system.
Parent systems and editions
A folder can also be a shelf for the editions of one game. Drop a .parent-system-container marker file at its root (or add a (parent-system) suffix to the folder name), and its immediate subfolders become systems:
books/
└── Dungeons & Dragons/
├── .parent-system-container
├── 3e/
│ └── core/
│ └── Players Handbook.pdf
└── 5e/
└── core/
└── Players Handbook.pdfThis yields the systems "Dungeons & Dragons 3e" and "Dungeons & Dragons 5e", each with Parent System set to "Dungeons & Dragons" and Edition set to the folder name — both available as library filters. Category folders work normally inside each edition.
System families
A family groups related but distinct systems that share a lineage — unlike a parent system, whose children are editions of one game. Use a .system-family-container marker (or a (system-family) folder-name suffix):
books/
└── d20 System/
├── .system-family-container
├── Pathfinder/
│ ├── .parent-system-container
│ ├── 1e/
│ └── 2e/
├── Mutants & Masterminds/
└── d20 Modern/Each child indexes as an independent system, and the container's name populates its System Family field, so the folder structure lines up with the family filter. Family children keep their own names — no {Parent} {Child} prefixing — and get no Edition or Parent System, because they are not variants of anything.
Containers nest: as shown above, a family can hold a multi-edition system. The inner .parent-system-container resolves its editions exactly as it would at the top level, and inherits the family name itself.
Publisher containers
A .publisher-container marker (or a (publisher) suffix) groups the systems one company puts out:
books/
└── Paizo/
├── .publisher-container
├── Pathfinder 2e/
└── Starfinder/Each child is an independent system with the container's name recorded as its Publisher.
Generic containers
If your shelf does not fit any of the named kinds, a bare .container marker (or a (container) suffix) says only "the folders in here are systems" and claims nothing about how they relate:
books/
└── Kickstarter Hauls/
├── .container
├── Mörk Borg/
└── Mothership/Unlike the named kinds it propagates no metadata — no family, publisher, edition, or parent system. It groups, and nothing more.
Family and publisher containers only fill in metadata a system does not already have. A family or publisher set by an OPF sidecar, an add-on, or your own edit is never overwritten by a rescan.
Declaring a container
Any of these declare a folder to be a container of systems, and they combine with (nsfw) and sort-order prefixes:
| Method | Example | Kind |
|---|---|---|
| Marker file | books/D&D/.parent-system-container | Parent system |
| Marker file | books/Itch Bundle/.one-page-container | One-page collection |
| Marker file | books/d20 System/.system-family-container | System family |
| Marker file | books/Paizo/.publisher-container | Publisher |
| Marker file | books/Kickstarter Hauls/.container | Generic |
| Folder-name suffix | books/Cyberpunk (parent-system)/ | Parent system |
| Folder-name suffix | books/Jam Games (one-page)/ | One-page collection |
| Folder-name suffix | books/Powered by the Apocalypse (system-family)/ | System family |
| Folder-name suffix | books/Chaosium (publisher)/ | Publisher |
| Folder-name suffix | books/My Shelf (container)/ | Generic |
| Recognized name | books/One-Page RPGs/ | One-page collection |
If a folder carries more than one declaration, the most specific kind wins, in this order: parent system → one-page → system family → publisher → generic. Every recognized suffix is stripped from the stored name either way, so a stray (publisher) never reaches the UI.
Child systems get a default name — {Parent} {Edition} for parent systems, the prettified file or folder name for one-page games, and their own folder name for family, publisher, and generic children. Only a parent-system container sets its children's Parent System: the other kinds hold independent games, not variants of the shelf. Rename one in the UI and it sticks: rescans never overwrite a system you have renamed, so "Dungeons & Dragons 2e" can become "Advanced Dungeons & Dragons".
Flattening containers in the library
Containers organize the grid; they do not lock systems away. The Group collections switch beside the "Your Collection" heading — shown only once you have a container — flattens them: container cards drop out and their child systems take their place, giving a plain list of every real system with the usual sorting and filters applied. Turn it back on to return to the drill-down view. The choice is remembered across sessions.
One-page collections are the deliberate exception and stay grouped either way. Keeping a pile of tiny one-book games out of the main grid is the entire reason that collection exists, so flattening leaves its chip in the Special Collections strip and its games reachable by drilling in.
Reorganizing an existing library
Moving a flat books/Dungeons & Dragons 5e/ into books/Dungeons & Dragons/5e/ produces a child whose generated name matches the system you already have. Grimoire adopts that existing system instead of creating a duplicate — its books, metadata, tags, and cover follow it into the container, and the old entry disappears from the top level.
System cover art
A system's cover comes from the first of these that exists:
- a
cover.*orfolder.*image at the system's folder root, - an image uploaded from the system's page (Cover image, GM/admin only),
- a thumbnail from one of the system's books.
A cover.* / folder.* image at a system's folder root is artwork only — it is not also indexed as a book. The same name deeper in the tree (in a category folder, say) is an ordinary image book.
Container folders hold no books of their own, so options 1 and 2 are the only ones available to them:
books/
└── Dungeons & Dragons/
├── .parent-system-container
├── cover.jpg ← folder artwork
└── 5e/Explicit content
Append (nsfw) to a system folder name to mark all content in that system as explicit:
books/
└── Some Adult Game (nsfw)/
└── core/
└── rulebook.pdfUsers with explicit content disabled will not see this system or its books.
An empty .nsfw file at the system root does the same thing, for when parenthesised folder names are awkward for your filesystem or sync tool:
books/
└── Some Adult Game/
├── .nsfw
└── core/
└── rulebook.pdfSort-order prefixes
Many file browsers sort folders alphabetically, so it's common to prepend punctuation to a folder name to pull it to the top of the list. Grimoire ignores a leading run of !, $, or % characters when deriving a system's name — only the contiguous run at the very start is stripped, and everything from the first other character onward is kept as-is:
books/
├── !!Dungeons & Dragons/ → shown as "Dungeons & Dragons"
├── !system-agnostic/ → still the System-Agnostic collection
└── $%Pathfinder 2e/ → shown as "Pathfinder 2e"Only !, $, and % are recognized, and only as a leading prefix — internal occurrences (e.g. D&D $ Extras) are left untouched. The prefix also stacks with (nsfw), so !!Forbidden Lore (NSFW) becomes the explicit system "Forbidden Lore".
Product codes
Most RPG PDFs have no ISBN, and titles repeat across printings and editions. A book's Product code holds the publisher's own catalogue number (also called a SKU or stock number), such as PZO9001, DDAL05-01, TSR 9247, or CAT35000. It's an optional field in the book editor and the bulk editor.
Once set, a product code is:
- Searchable. Type the code into the search box, or use
code:PZO9001. Spaces and hyphens are ignored, soTSR9247findsTSR 9247. - Sortable. Choose Product code in the toolbar's sort menu. Codes sort in number order, so
PZO10000comes afterPZO9001, and books without a code come last. - Filterable. The Product code filter offers the letter prefixes found in the system (
PZO…,TSR…), plus books with or without a code. - Kept in sidecars. It's written to and read from OPF files, and included in JSON and YAML metadata exports.
The DriveThruRPG add-on also fills in the code from the store's Publisher Stock #.
Maps
maps/
└── Creator Name/ ← shown as a group header in the map gallery
└── map-file.pngTokens
tokens/
└── Category/ ← shown as a group header in the token browser
└── token-file.pngAudio
audio/
└── Category or Creator/ ← shown as a group header in the audio library
├── cover.jpg ← optional folder artwork (cover.* or folder.*)
└── track.mp3Supported formats: .mp3, .ogg, .opus, .flac, .wav, .m4a, .m4b, .aac. Duration and embedded title/artist/album tags are read on scan. See the Audio Library guide for playback and the global player.
Models
models/
└── Creator or Release/ ← shown as a group header in the model browser
├── goblin_presupported.stl
└── goblin_unsupported.stlSupported formats: .stl, .3mf, .glb, .ply, .obj, .gltf, and sliced printer output (.ctb, .lys, .cbddlp, .pwmx, .photon). .stl files get a rendered preview and most mesh formats open in an interactive 3D viewer. Presupported and unsupported copies are detected from the file or folder name. See the 3D Models guide.
Recently added items
Grimoire records when each book, map, token, audio track, and 3D model first appears in the library. Anything added in the last 7 days shows a small sparkle icon beside its name; hover it and it reads New.
- Sort by date added. Choose Date added in the toolbar's sort menu on a system page or in any gallery. It starts newest first; flip the arrow for oldest first. On a system page the sort applies inside every category.
- Show only new items. Open Filters and tick Recently added (last 7 days) to hide everything without the badge. Save it as a filter preset to make it a one-click "what's new" view.
The first scan of a new install brings in your whole library at once, so those items get no date - otherwise everything would show as new for a week. Only what you add after that first full scan is dated. If you upgraded from an earlier version, existing items take the date Grimoire first scanned them.
Moving or renaming a file, or replacing its contents, keeps its original date. An item with no date gets no badge and sorts last.
To notify another service about new books, poll the books API with sort=added_at and added_since.
Ignoring files
To keep files on disk but out of Grimoire, add a .grimoireignore file. It uses the same syntax as .gitignore or .dockerignore, so anything matched by a rule is skipped by the scanner and never shown in the UI. This is handy when a book ships extra print variants — black-and-white single-page versions, zine-sized layouts — that you want kept alongside the book but hidden from the library.
Place .grimoireignore at your library root to apply rules everywhere, or in any subfolder to add rules for just that subtree. Rules are cumulative and nested, exactly like git.
library/
├── .grimoireignore ← applies to the whole library
└── books/
└── Example TTRPG/
├── core/
│ └── Players Handbook.pdf
└── ignore/ ← ignored: this folder is skipped
├── Players Handbook BW Single Pages.pdf
└── Players Handbook Zine-sized.pdfA .grimoireignore with:
# Skip an entire folder
ignore/
# Skip print variants anywhere in the library
*BW Single Pages*.pdf
*Zine-sized*.pdfPatterns support the full gitignore dialect, including ! to re-include a previously excluded file and ** for arbitrary-depth matching. Rules apply to every collection — books/, maps/, tokens/, audio/, and models/.
Changes take effect on the next scan. If you add a rule that matches a file Grimoire already indexed, that item is marked missing and hidden on the next rescan; remove the rule and rescan to bring it back.
Rescanning
Grimoire watches the library for changes by default, so files you add, remove, or reorganize show up by themselves a few seconds later. To rescan by hand, click Rescan at the top of any Server settings page (admins) or go to Settings → Library → Scanning → Rescan Library. You can also configure a scheduled rescan there.
Watching for changes
Settings → Library → Scanning → Watch for Changes rescans by itself whenever something changes in the library folder. It waits until the folder has been quiet for a few seconds, so a large copy is scanned once, when it has finished. A change inside one system or folder rescans only that folder; one that spans folders, or adds, renames, or removes a system folder, rescans the whole library. NAS thumbnail folders, .DS_Store, half-downloaded .part files, and the sidecars Grimoire writes itself are ignored.
| Mode | How changes are noticed |
|---|---|
| Automatic (default) | File system events, except on a network share (NFS, SMB/CIFS, or a FUSE mount such as rclone or mergerfs), where it checks regularly instead. |
| File system events | Instant and light, but the kernel only reports changes made on the machine Grimoire runs on. A change made from another computer straight to the share is missed. |
| Check regularly | Looks through the whole library every two minutes. Works on any share, at the cost of reading every folder each time. |
The section shows what the watcher is doing, and why if it cannot watch - most often the host's limit on watched folders (fs.inotify.max_user_watches), which you can raise or avoid by checking regularly. LIBRARY_WATCH and LIBRARY_WATCH_MODE pin both settings; see environment variables.
Prefer a schedule?
On a slow NAS, or for a table that plays in the evening and would rather not share the server with a scan, turn watching off and set a Scheduled Rescan for a quiet hour instead.
Renamed and deleted folders
Renaming a folder is recognised by the next full scan: books are matched by content and keep their tags, favorites, bookmarks, and reading progress. A renamed system folder keeps the same system - its description, cover, tags, campaigns, and access - under the new name, rather than leaving an empty one behind. Identical copies in a renamed folder are told apart by filename.
Deleting a system's folder removes the system on the next full scan, unless a campaign is played in it or you gave it a name, description, or cover of your own. Files deleted from a system that is still there are marked missing rather than forgotten, since a share that was briefly unmounted looks the same; Settings → Library → Danger zone → Database Cleanup removes them for good. It first counts what it would remove and asks you to confirm, and it refuses to run while the library folder is missing or empty - most likely a share that isn't mounted, where every file would look missing. A cleanup set to run after a scheduled rescan skips itself in that case too.
Scoped rescan
For large libraries you don't have to rescan everything. Every system, category, subfolder, and map/token/audio group has its own rescan button that re-scans just that folder.
Metadata refresh modes
OPF and tags.json metadata is only applied when an item is first indexed: ordinary rescans leave existing records alone so your web-UI edits aren't overwritten. To pick up a sidecar file you added or corrected after the initial scan, choose a mode in the rescan dialog (available on the global Rescan button and every per-folder rescan button):
| Mode | Behaviour |
|---|---|
| Find new files | Default: add new files, flag missing ones, leave existing records untouched. |
| Update missing metadata | Additionally fill empty book fields from sidecar files, without touching anything you've already set (non-destructive). |
| Replace all metadata | Overwrite fields with whatever the sidecar files provide (this discards UI edits the sidecar covers). |
