Get started
#Configuration
Shelf works with no settings at all. Everything here is optional, for when you want email, a reverse proxy, different folders, or to turn something off.
#How to set things
Settings can come from environment variables, from appsettings.json beside the app, or from the command line. Names are written here as Section:Name; as an environment variable, use two underscores instead of the colon.
| Written here |
As an environment variable |
On the command line |
Accounts:AllowSignUp |
Accounts__AllowSignUp=false |
--Accounts:AllowSignUp false |
Email:Host |
Email__Host=smtp.example.org |
--Email:Host smtp.example.org |
#Accounts
| Setting |
Default |
What it does |
Accounts:AllowSignUp |
true |
Whether new accounts can be made, until an admin chooses under Who may sign up on Shelf admin, which then decides. The first account can always be made. |
Accounts:SignInsPerMinute |
10 |
Sign-in and sign-up attempts allowed a minute from one address. |
DocsUrl |
https://doodersrage.github.io/shelf/ |
Where Help and the ? beside a page's name send readers, for a shelf that keeps its own copy of these guides. |
#Cloudflare Turnstile
Turnstile asks a visitor's browser to show it is one a person is using before signing in, signing up, or asking for a password reset goes through, usually without them doing anything. It is off until both keys are set. Make a widget in the Cloudflare dashboard, under Turnstile, for the host names the shelf is reached at, and copy its keys here.
| Setting |
Default |
What it does |
Turnstile:SiteKey |
none |
The widget's site key. |
Turnstile:SecretKey |
none |
The widget's secret key. Keep it as you would a password. |
Turnstile:Hostnames |
the host of Email:PublicAddress |
The host names the forms are on, separated by commas. Cloudflare says which site a token was made on, and one made anywhere else is refused. Leave out localhost on a shelf others reach. |
With Turnstile on, a form whose check fails, or cannot reach Cloudflare, is refused, so a shelf that cannot reach challenges.cloudflare.com cannot be signed in to with a password; passkeys and single sign-on still work. Scripts signing in with a password stop working too: give them an API token. Server health says whether Turnstile is on, or set up only partly, and why the last form was refused, until a check passes: most often a token made on a host that isn't in Turnstile:Hostnames (or, without it, isn't the host of Email:PublicAddress), such as an address inside your network or one with www.. The widget shows its tick either way, since it has no way of knowing what the shelf expects.
#Single sign-on
Off unless Oidc:Authority and Oidc:ClientId are set. See Single sign-on.
| Setting |
Default |
What it does |
Oidc:Authority |
none |
The provider's issuer address, such as https://auth.example.org/application/o/shelf/ for Authentik. |
Oidc:ClientId |
none |
The client id the provider gave Shelf. |
Oidc:ClientSecret |
none |
Its secret. Leave it out for a public client; PKCE is always used. |
Oidc:Name |
SSO |
What the button says: Sign in with this. |
Oidc:CreateAccounts |
false |
Make accounts for new people who sign in through the provider even when Accounts:AllowSignUp is false. |
Oidc:Scopes |
openid profile email |
The scopes asked for. |
Oidc:RequireHttpsMetadata |
true |
Set to false only for a provider on plain http:// inside your own network. |
#Upload limits
The largest file Shelf takes, in megabytes. A larger one is refused with a note saying how large it may be.
| Setting |
Default |
What it does |
Uploads:MaxEbookMegabytes |
80 |
The largest EPUB, PDF, Kindle file, or comic. |
Uploads:MaxAudioMegabytes |
1024 |
The largest audiobook, all its tracks together; also the most an upload of many files at once may total. |
In Docker, as environment variables:
environment:
- Uploads__MaxEbookMegabytes=500
- Uploads__MaxAudioMegabytes=4096
A reverse proxy in front of Shelf has a limit of its own, which refuses a large file before Shelf sees it; raise it to match (for nginx, client_max_body_size 4g;). See HTTPS and a reverse proxy.
#Where things are kept
| Setting |
Default |
What it does |
ConnectionStrings:Shelf |
Data Source=shelf.db |
The SQLite database. In Docker, /data/shelf.db. |
EbookStore:Root |
ebooks beside the app |
E-book files. In Docker, /data/ebooks. |
AudioStore:Root |
audio beside the app |
Audiobooks. In Docker, /data/audio. |
CoverStore:Root |
covers beside the app |
Cover pictures kept on the shelf. In Docker, /data/covers. |
DataProtection:KeysPath |
keys beside the database |
The keys that seal sign-in cookies and two-step secrets. Keep it private and keep it with backups, or everyone signs in again after a restore. |
#PostgreSQL
Shelf keeps its database in SQLite, one file beside everything else, which suits a shelf of any size a household has. To keep it in PostgreSQL instead (13 or newer), say for a server that runs one for everything:
| Setting |
Default |
What it does |
Database:Provider |
Sqlite |
Postgres to use PostgreSQL. |
ConnectionStrings:Postgres |
none |
The database, as Host=db;Database=shelf;Username=shelf;Password=…. Shelf makes its tables on the first start. |
The keys, backups, and files stay in the folders above, so /data is still wanted in Docker. A few things differ:
- Search finds a phrase anywhere, ignoring case, but doesn't rank the places it finds or overlook accents, as SQLite's full-text index does.
- Snapshots hold
pg_dump's copy of the database when pg_dump is on the server (or at Database__PgDump), and otherwise the library as JSON, which Restore takes. Back the database up with PostgreSQL's own tools as well.
Moving an existing shelf. With PostgreSQL set up and empty, run Shelf once with the move command; it copies every reader, book, note, and setting from the SQLite file, ids and all, then stops:
docker run --rm -v shelf-data:/data -e Database__Provider=Postgres -e ConnectionStrings__Postgres="Host=db;Database=shelf;Username=shelf;Password=…" ghcr.io/doodersrage/shelf:latest --move-to-postgres true
Then start Shelf with the same two settings. The SQLite file stays as it was, to go back to.
#Running behind a proxy
| Setting |
Default |
What it does |
Hosting:BehindProxy |
false |
Trust a reverse proxy's X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-For. Only set it when Shelf cannot be reached except through that proxy, or set Hosting:KnownProxies too. |
Hosting:KnownProxies |
none |
The reverse proxy's addresses or ranges, such as 172.18.0.0/16 or 10.0.0.2, separated by commas. Forwarded headers from anywhere else are ignored, so a request that reaches Shelf's port directly can't claim another address to get around the sign-in limit. Without it, a proxy at any address is trusted. |
Network:AllowPrivateAddresses |
false |
Let addresses readers type reach this machine and the local network: a podcast feed, and an Audiobookshelf to import from when a reader who isn't an admin brings one in. Off, those must be on the internet, so the shelf can't be used to look inside the network it runs in. An admin's Audiobookshelf, and notices and webhooks, may always reach the local network; nothing reaches a cloud's metadata service. |
Passkeys:Origin |
the address of each request |
The address readers open Shelf at, such as https://shelf.example.org, when a proxy hides it. Passkeys are tied to it. |
Passkeys:RelyingParty |
the host name of Passkeys:Origin |
The name passkeys are made for. Rarely needed. |
#Email
Without a mail server, an admin resets forgotten passwords and notifications come only by the other ways a reader sets up. Set at least Email:Host and Email:From, and Email:PublicAddress for password resets; then Forgot the password? on the sign-in page sends a reader a link, to the email address on their account.
| Setting |
Default |
What it does |
Email:Host |
none |
The SMTP server. Email is on once this and Email:From are both set. |
Email:Port |
587 |
Its port. |
Email:Security |
auto |
auto, starttls, ssl (port 465), or none for a server on the same machine. |
Email:User, Email:Password |
none |
The account to send with, if the server wants one. |
Email:From |
none |
The address emails come from. Required: without it, Shelf sends no email, even with Email:Host set. |
Email:PublicAddress |
none |
Shelf's own address, such as https://shelf.example.org, for links in emails. A password reset needs it (or AllowedHosts set to the shelf's own names): without it, no reset email is sent, since the link would point wherever the request claimed to be from. |
#Nightly backups
| Setting |
Default |
What it does |
Backup:Enabled |
true |
Take a copy of the database each night. |
Backup:Hour |
3 |
The hour, in UTC, after which it is taken. |
Backup:Keep |
7 |
How many to keep. |
Backup:Folder |
backups beside the database |
Where they go. |
Backup:IncludeFiles |
false |
Put every e-book and audiobook in each copy too. They never change once saved, so most people back up their folders separately instead. |
Backup:CopyTo |
none |
A second folder to copy each backup to, such as another disk or a mounted network share. It keeps as many as Backup:Keep. |
Backup:S3:Bucket |
none |
A bucket in S3-compatible storage to copy each backup to. Needs the two keys below. |
Backup:S3:AccessKey, Backup:S3:SecretKey |
none |
The keys for it. Give them only the rights to list, put, and delete in that bucket. |
Backup:S3:Endpoint |
Amazon S3 |
The service's address for anything but Amazon, such as https://s3.us-west-004.backblazeb2.com, https://<account>.r2.cloudflarestorage.com, or your MinIO. |
Backup:S3:Region |
us-east-1 |
The region. |
Backup:S3:Prefix |
shelf/ |
Where in the bucket the copies go. The newest Backup:Keep are kept there. |
Backup:S3:PathStyle |
true with an endpoint |
Address the bucket as part of the path, as most S3-compatible services want. |
Backup:S3:PartSizeMegabytes |
64 |
Larger backups go up in parts of this size, at least 5. |
#Trash, history, and file checks
| Setting |
Default |
What it does |
Trash:Days |
30 |
How long deleted books, quotes, sessions, and highlights wait in the trash, with their files, before they go for good. |
History:Days |
365 |
How long each change to a book's details, and each change to many books, is kept. |
FileCheck:Days |
7 |
How often every stored file is read through and checked. 0 turns the check off. |
#Calibre
| Setting |
Default |
What it does |
Calibre:Library |
none |
The Calibre library's folder, filled in on Open a Calibre library. See From Calibre. |
#Import folder
| Setting |
Default |
What it does |
Import:Folder |
none (off) |
A folder to watch. Each reader's books go in a folder inside it named after them; see From a folder. In Docker, mount one at /import and set this to /import. |
Import:AfterImport |
move |
move puts added files into .imported; delete removes them, since Shelf keeps its own copy. Files it could not add are always moved to .not-added. |
Import:EverySeconds |
60 |
How often it looks. |
Import:SettleSeconds |
30 |
How long a file must have stayed unchanged before it is taken. |
#Scanned PDFs (OCR)
| Setting |
Default |
What it does |
Ocr:Enabled |
true |
Read the words of scanned pages, when Tesseract and Poppler are installed. |
Ocr:Languages |
eng |
Tesseract's languages, such as eng+fra once tesseract-data-fra is installed. |
Ocr:Resolution |
200 |
The DPI pages are read at. |
Ocr:Tesseract, Ocr:PdfToPpm, Ocr:PdfToText, Ocr:PdfInfo |
found on the path |
Where those tools are, if not on the path. Ocr:PdfInfo also reads a PDF's title and author when one is added from a file. |
#Books from elsewhere
| Setting |
Default |
What it does |
Comics:ComicVineKey |
none |
An API key from ComicVine, for Match on ComicVine on comics' pages. Without one, the button isn't shown. |
Ebooks:Convert |
ebook-convert |
Calibre's converter, which turns Kindle files into EPUBs. Without it, Kindle files are refused with a note saying why. |
FreeBooks:Enabled |
true |
The Free books page, which reaches gutenberg.org, librivox.org, and archive.org. Turn it off for a shelf with no internet. |
AbsApi:Enabled |
true |
Let Audiobookshelf's phone apps sign in to Shelf. See Audiobookshelf's phone apps. |
AbsSync:Enabled |
true |
Look once a day at the Audiobookshelf libraries readers keep in step with. See Keeping in step. |
AbsSync:LookSeconds |
30 |
How often Shelf checks whether a library is due its daily look, or was asked to look now. |
Details:PauseMilliseconds |
1200 |
The pause between books when details or covers are filled in for many, so Open Library and Google Books are asked politely. |
Fetch:PollSeconds |
20 |
How often Shelf asks the download client after the books it is fetching. The rest is set by an admin under Downloads; see Finding and fetching books. |
Fetch:PauseMilliseconds |
3000 |
The pause between books when looking for the whole want list, so the indexers are asked politely. |
Kobo:Kepub |
true |
Send books to Kobo e-readers as KEPUBs, made from the EPUBs and kept beside them, for the Kobo's page numbers and statistics. false sends the EPUBs as they are. |
Lookup:Dictionary |
true |
Look up in the reader, which sends the chosen words to Wiktionary and Wikipedia. Set false to keep them on the shelf. |
Lookup:GoogleBooks |
true |
Ask Google Books when Open Library has no match or leaves fields blank. It sends the ISBN, or the title and author, to Google; set false to keep look-ups to Open Library alone. |
Lookup:GoogleBooksKey |
none |
A Google Books API key (free, from the Google Cloud console). Without one, look-ups share Google's allowance for anonymous callers everywhere, which often runs out for the day; Shelf then carries on with Open Library alone. |
Hardcover:LookupToken |
none |
A Hardcover API token that every reader's look-ups use, to fill a book's series and number and other blanks. Without it, only readers who connected Hardcover under Account ask it, with their own token. |
AuthorWatch:Enabled |
true |
The daily look at Open Library for new books by the authors readers follow. Set false to stop it for everyone. |
Finding:SearchUrl |
https://openlibrary.org/ |
Where Find new books and followed authors search: Open Library, or a mirror of its search API. |
SeriesAlerts:Enabled |
true |
The daily look at Open Library for new books in a series, for readers who turn it on under Series. Set false to stop it for everyone. |
#Recaps
| Setting |
Default |
What it does |
Recap:ApiKey |
none |
An Anthropic API key, for Previously in this book. Without one, recaps are off. |
Recap:Model |
claude-haiku-5-5 |
The model that writes recaps. A larger one writes better, for more. |
Recap:Enabled |
true |
Turn recaps off without removing the key. |
The text of the chapters a reader has read is sent to Anthropic to be summed up; see Previously in this book.
| Ask:UseModel | true | With a key set, lets Ask your shelf send the question alone to the model to understand it. false keeps questions on the server, read by simple rules. |
#Money and updates
| Setting |
Default |
What it does |
Shelf:Currency |
the reader's language's |
The currency prices paid and got are shown in, as an ISO code such as EUR or USD. |
Updates:Check |
true |
Ask GitHub twice a day whether a newer Shelf is out, and tell admins. |
Updates:Url |
Shelf's releases on GitHub |
Where to ask, for a changed copy with releases of its own. |
#Read-along
| Setting |
Default |
What it does |
ReadAlong:Url |
none |
The transcriptions address of a speech-to-text service with OpenAI's API, such as http://speaches:8000/v1/audio/transcriptions. Without it, read-along is off. See Read-along. |
ReadAlong:Model |
whisper-1 |
The model the service should use. |
ReadAlong:ApiKey |
none |
Sent as a bearer token, for a service that asks for one. |
ReadAlong:FFmpeg |
ffmpeg on the path |
Where ffmpeg is, to cut recordings into ten-minute pieces. Audio:FFmpeg sets it for making M4Bs too. |
Audio:M4bBitrate |
64k |
The AAC bitrate of an M4B made from tracks. |
ReadAlong:MaxUploadMegabytes |
24 for OpenAI, else none |
The largest file to send whole. A larger track needs ffmpeg. |
#Linked shelves
| Setting |
Default |
What it does |
Federation:Name |
Shelf at and the host of Email:PublicAddress |
The name linked shelves know this one by. |
Linking needs Email:PublicAddress, as the other shelf calls this one at that address. See Linked shelves.
#Notifications and webhooks
| Setting |
Default |
What it does |
Notices:Contact |
Email:From, or Shelf's project page |
Who the push services can contact about this shelf's notifications, as mailto:you@example.org or a web address. Apple asks for one. |
Webhooks:Enabled |
true |
Readers' own webhooks. Webhooks post to addresses readers give, which can be on your own network (as Home Assistant usually is); set false if you don't want the shelf posting anywhere. |
Notices:PushHosts |
none |
More push services to allow, by host name and separated by commas, for a browser whose service Shelf doesn't know. Shelf sends browser notifications only to Google's, Mozilla's, Apple's, and Microsoft's. |
Links in ntfy and Gotify notifications need the shelf's own address, Email:PublicAddress; without it, they come without a link.
#Logging
Shelf logs with ASP.NET Core's usual settings, such as Logging:LogLevel:Default. In Docker, docker logs shelf shows them.
#Source code link
| Setting |
Default |
What it does |
SourceUrl |
this project on GitHub |
Where the Source code link at the foot of each page goes. Shelf is under the AGPL: if you run a changed copy for other people, point this at your changed source. |