Documentation

Run your own Bookplate.

Everything you need to install Bookplate with Docker, keep it running and backed up, and get the most out of it. Written for Bookplate 0.9.

Get started

Requirements

Bookplate runs as two Docker containers — the app and its Postgres database. You need:

  • Docker with Compose. On a Mac or Windows PC, install Docker Desktop. On Linux, install Docker Engine and the Compose plugin.
  • A 64-bit machine — Intel/AMD or ARM, so a Mac, a PC, a Raspberry Pi 4 or 5 (with a 64-bit OS), most NAS boxes, or a cloud server all work.
  • About 300 MB of free memory while it runs, and about 750 MB of disk for the two images plus room for your covers and pasted images.

Check Docker is ready with docker compose version. It should print a version number.

Install

  1. Make a folder for Bookplate

    This folder holds its two config files. Your library itself lives in Docker volumes.

    Terminal
    $ mkdir bookplate && cd bookplate
  2. Download the compose file and the settings template

    Terminal
    $ curl -fsSLO https://raw.githubusercontent.com/LeoPhh/bookplate/main/docker-compose.yml
    $ curl -fsSL https://raw.githubusercontent.com/LeoPhh/bookplate/main/.env.example -o .env
  3. Fill in your settings

    Open .env in any text editor. Two values are required, and both should be long random strings. Generate them with:

    Terminal
    # paste this after AUTH_SECRET=
    $ openssl rand -base64 32
    # and this after DB_PASSWORD=
    $ openssl rand -hex 24

    Then set the address you’ll open Bookplate at. Your finished file looks like this:

    .env
    AUTH_SECRET=q3F…your first random string…
    DB_PASSWORD=8c1e…your second random string…
    PUBLIC_URL=http://localhost:3000
    BOOKPLATE_PORT=3000
    REGISTRATION=closed

    Keep this file private, and keep it with your backups. You never type these values anywhere — they’re not your login.

  4. Start it

    The first start downloads the images, which takes a minute or two.

    Terminal
    $ docker compose up -d

    Check both containers are running — after a few seconds, each should say healthy:

    Terminal
    $ docker compose ps
  5. Open it

    Go to your PUBLIC_URL — http://localhost:3000 if you kept the default — and carry on with the first run.

First run

The first visit shows a Set up your library screen. Enter your name, an email address and a password of at least 8 characters, and choose Create account. This is the account that owns this Bookplate — after it exists, nobody else can sign up.

Use a real email address. Without email set up it’s only your username, but if you set up email later, it’s where password-reset links go.

Configuration

All settings live in .env. After changing one, run docker compose up -d to apply it.

SettingDefaultWhat it does
AUTH_SECRET—Signs your login cookie. Required. Changing it just signs everyone out.
DB_PASSWORD—Password between the app and its database. Required. Set it once and never change it.
PUBLIC_URLhttp://localhost:3000The address you open Bookplate at. With https://, sign-in cookies are marked secure.
BOOKPLATE_PORT3000The port on the host machine.
REGISTRATIONclosedclosed: only the first account can be created. open: anyone who can reach the server can create their own, separate library, from Create an account on the sign-in page. With email set up, new accounts confirm their address first. See Letting others sign up.
SMTP_HOST—Your mail server. Setting it switches email on — see Email.
SMTP_PORT587587 for most providers; 465 for an encrypted-from-the-start connection.
SMTP_SECUREfollows the porttrue or false, only if your provider says to set it.
SMTP_USER, SMTP_PASSWORD—The mail server’s login, if it needs one.
SMTP_FROMBookplate <bookplate@localhost>Who emails come from, e.g. Bookplate <books@example.com>. Use an address your provider lets you send from.
STORAGElocals3 keeps images in an S3-compatible bucket, with the S3_* settings. See Storing images in S3.
LOG_LEVELinfodebug for more detail while chasing a problem; warn or error for less. See Troubleshooting.
METRICS_TOKEN—Switches on /api/metrics: totals (accounts, active people, books by status…) for Prometheus or Grafana Alloy, read with this token. Only totals, nothing about anyone.
BOOKPLATE_VERSIONlatestPin a release, e.g. 0.2.0 or 0.2. See Updating.
BOOKPLATE_IMAGEleophh/bookplateSet to ghcr.io/leophh/bookplate to pull from GitHub’s registry instead of Docker Hub.

Bring in an existing library

From Goodreads or StoryGraph

  1. Export your library

    In Goodreads: My Books → Import and export → Export Library, then download the file once it’s ready. In StoryGraph: Manage Account → Export StoryGraph Library. Either way you get a .csv file.

  2. Preview it

    In Bookplate, go to Settings → Import → From Goodreads or StoryGraph → Choose a CSV… and pick the file. Bookplate works out which service the file came from and shows what it found — how many books are read, reading and to read, which are new, and which are already in your library — before anything is saved.

  3. Import

    Choose Import. Titles, authors, ISBNs, page counts, statuses, ratings, finish dates and owned copies come across, and reviews and private notes become each book’s notes page.

  4. Find covers (optional)

    Exports don’t include covers, so Bookplate offers to look them up — on Open Library by ISBN, then iTunes. It fetches one every few seconds to stay within Open Library’s limits and tells you roughly how long it will take; keep the page open while it runs, or press Stop and carry on later. Books it can’t find a cover for keep their cloth cover.

Importing is safe to repeat. Each book is matched to one already in your library — by an earlier import, its ISBN, or its title and author — and updated rather than added twice. Its reading details come from the export, while its cover, colour, genre and existing notes stay as they are.

From another Bookplate

Go to Settings → Import → Choose a zip… and pick either a Bookplate export (from Export library on any Bookplate) or a zip of the data folder from the original, git-based version of the app.

Books, covers, notes, pasted images and vocabulary all come across. Books and words with the same ids are updated; nothing already in your library is removed, so importing the same file twice is harmless.

Run it

Updating

From your Bookplate folder:

Terminal
$ docker compose pull
$ docker compose up -d

The first command downloads the newest release; the second replaces the app container with it, keeping your data and settings. Database changes are applied automatically when the new version starts. The version you’re on is shown at the bottom of Settings.

To upgrade on your own schedule, pin a version in .env: BOOKPLATE_VERSION=0.2 gets bug fixes for 0.2 but never jumps to 0.3. What changed in each release is listed on GitHub.

Backups and restoring

The easy way: an export

Settings → Export library downloads a zip of your whole library — books, notes, pasted images, covers and vocabulary — as plain JSON, Markdown and image files. Settings → Import reads it back into any Bookplate. It doesn’t include your account, password or profile photo.

A full server backup

This saves everything, every account included. From your Bookplate folder:

Terminal
$ docker compose exec -T postgres pg_dump -U bookplate bookplate > bookplate-db.sql
$ docker compose cp bookplate:/data/uploads ./bookplate-uploads

Keep bookplate-db.sql, the bookplate-uploads folder and your .env together, somewhere other than the same machine.

With images in S3, skip the second command (and the image step when restoring): the images are in your bucket. Back it up with your provider’s tools, or turn on its versioning.

Restoring a full backup

On a fresh install that uses the same .env, with the backup files in the Bookplate folder, and before starting the app:

Terminal
$ docker compose up -d --wait postgres
$ docker compose exec -T postgres psql -q -U bookplate -d bookplate < bookplate-db.sql
$ docker compose run --rm --no-deps --user root -v "$PWD/bookplate-uploads:/backup:ro" bookplate sh -c 'cp -a /backup/. /data/uploads/ && chown -R node:node /data/uploads'
$ docker compose up -d

That starts only the database, loads your data into it, copies your images back into place, then starts Bookplate.

Email and forgotten passwords

Email is optional. Without it, Bookplate works exactly the same, and forgotten passwords are reset on the server. With it, people reset their own passwords from the sign-in page.

Resetting a forgotten password

On the server — always works, email or not. From your Bookplate folder:

Terminal
$ docker compose exec bookplate reset-password you@example.com

It asks for the new password twice (without showing it) and signs out every device. Only someone with access to the server can run it.

By email — with email set up, choose Forgot password? on the sign-in page and enter your address. The link in the email works once, expires after an hour, and signs out every device when used. Without email, the same page explains the command above.

Setting up email

Bookplate sends email through SMTP, which almost every email service offers. Add your provider’s details to .env and run docker compose up -d:

.env
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=you@example.com
SMTP_PASSWORD=an app password from your provider
SMTP_FROM=Bookplate <you@example.com>
ProviderHost and portLogin
Gmailsmtp.gmail.com, 587Your Gmail address and an app password (needs 2-step verification on the account)
Fastmailsmtp.fastmail.com, 465Your address and an app password
Resendsmtp.resend.com, 587User resend, password = an API key; send from a domain you’ve verified there

Your provider’s help pages have the exact values. Then go to Settings → Email and choose Send a test email: it either arrives, or Bookplate shows what the mail server said.

Bookplate sends at most three emails of each kind to an address every 15 minutes, and the reset form never reveals whether an address has an account.

Storing images in S3

Covers, pasted note images and profile photos are kept on disk, in Docker’s uploads volume. That’s all most people need. If you’d rather keep them in object storage — Scaleway, Cloudflare R2, Backblaze B2, AWS S3, or a NAS running Garage or RustFS — create a bucket and an access key that can read and write it, then add to .env and run docker compose up -d:

.env
STORAGE=s3
S3_ENDPOINT=https://s3.fr-par.scw.cloud
S3_REGION=fr-par
S3_BUCKET=bookplate-images
S3_ACCESS_KEY_ID=your access key
S3_SECRET_ACCESS_KEY=its secret
VariableDefaultWhat it does
S3_ENDPOINTAWSYour provider’s S3 address. Leave it out for AWS.
S3_REGIONus-east-1The bucket’s region, e.g. fr-par. R2 uses auto.
S3_BUCKET—The bucket’s name. Create it first; Bookplate won’t.
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY—The access key.
S3_FORCE_PATH_STYLEfalseSet to true for servers you run yourself (Garage, RustFS, SeaweedFS).
S3_PREFIX—A folder inside the bucket, if it’s shared with other things.

The bucket stays private: Bookplate fetches the images itself and only shows them to whoever owns them. If a setting is wrong, Bookplate won’t start, and docker compose logs bookplate says which part — the bucket name, the key, or the address.

Letting others sign up

With REGISTRATION=open, anyone who can reach your Bookplate can make an account. To keep that manageable, Bookplate:

  • Checks sign-ups invisibly. While someone fills in the sign-up form, their browser solves a small puzzle — a moment’s computing for a person, but slow and costly for a script creating accounts in bulk. There are no picture puzzles, and nothing is sent to anyone else.
  • Allows five new accounts an hour from one address.
  • Has new accounts confirm their email, if email is set up, and removes accounts that never confirm after 7 days.
  • Can limit each account, so nobody fills your disk or bucket on their own.
VariableDefaultWhat it does
LIMIT_BOOKS, LIMIT_WORDSno limitThe most books, and vocabulary words, one account may have.
LIMIT_STORAGE_MBno limitThe most image space one account may use: covers, pasted images and profile photo together.
LIMIT_UPLOAD_MB8The largest single image.
LIMIT_IMPORT_MB1024The largest export zip or CSV file that can be imported.
TRUSTED_PROXIES1How many reverse proxies stand in front of Bookplate. Set 2 if there are two, e.g. Cloudflare in front of Caddy, so the per-address limit sees each visitor’s real address.
SIGNUP_BOT_CHECKtruefalse turns the invisible check off.
CONTACT_EMAIL—Your address, sent to Open Library with book searches. They allow servers that give a contact three times as many searches, which helps when many people use yours.

Using it from other devices

On your home network

Open http://<your-computer’s-IP>:3000 from a phone or another computer on the same network. On a Mac, the IP is under System Settings → Wi-Fi → Details. Set PUBLIC_URL to that address if you’ll mostly use it from there.

From anywhere, privately

Tailscale (free for personal use) puts your devices on a private network, so you can reach Bookplate from anywhere without exposing it to the internet. Install it on the machine running Bookplate and on your phone, then use the machine’s Tailscale address.

On the public internet

Put Bookplate behind a reverse proxy that provides HTTPS. With Caddy and a domain pointing at your server, this is the whole config:

Caddyfile
books.example.com {
reverse_proxy localhost:3000
}

Then set PUBLIC_URL=https://books.example.com and run docker compose up -d.

Keeping it running

Both containers restart automatically whenever Docker is running — after a crash, or after the machine reboots. On a Mac or PC that means Docker Desktop has to be open: turn on Settings → General → Start Docker Desktop when you sign in. While a laptop sleeps, Bookplate is paused and picks up again on wake.

For a library you can reach at any hour, run it on something that’s always on — a Raspberry Pi, a NAS or a small cloud server.

Useful commands, from your Bookplate folder:

Terminal
# is it running, and healthy?
$ docker compose ps
# the app's live logs (Ctrl+C to leave)
$ docker compose logs -f bookplate
# memory and CPU use
$ docker stats
# stop it, and start it again
$ docker compose stop
$ docker compose start

Docker Desktop shows the same under Containers → bookplate, with logs and live stats for each container.

Troubleshooting

Signing in fails with “Invalid origin”
PUBLIC_URL doesn’t match the address in your browser — often the case behind a reverse proxy. Make them match, then run docker compose up -d.
“Port is already allocated” when starting
Another program uses that port. Change BOOKPLATE_PORT and PUBLIC_URL as in Install.
The app keeps restarting or shows as unhealthy
Look at docker compose logs bookplate: when Bookplate can’t start, its last line says why (“Bookplate couldn’t start: …”). A database connection error almost always means DB_PASSWORD changed after the first start — put the original back.
With S3 storage, the app won’t start
docker compose logs bookplate names the problem: the bucket doesn’t exist, the key was refused, or the storage server couldn’t be reached. Check the S3_* settings — self-run servers usually also need S3_FORCE_PATH_STYLE=true.
I forgot my password
Run docker compose exec bookplate reset-password you@example.com on the server, or — with email set up — use Forgot password? on the sign-in page. See Email and forgotten passwords.
Emails don’t arrive
Use Settings → Email → Send a test email: it shows the mail server’s error if sending fails. Check your spam folder, and that SMTP_FROM is an address your provider lets you send from. Reset and confirmation emails never show errors on screen (that would reveal which addresses have accounts), but docker compose logs bookplate does.
Book search finds nothing, or covers don’t load
Search and covers come from Open Library (with iTunes as a fallback for covers), fetched by your server. If your server can’t reach them, add the book by hand and upload or paste a cover instead.
Something else
Open an issue on GitHub with what you did, what happened, and the output of docker compose logs bookplate. If the app showed a reference (e.g. “reference 7f3a9c12”), include it: every log line from that request carries it.

Use it

Adding books

Choose + Add book. Start typing a title or author in the search box: results come from the Open Library catalogue. Picking one fills in the title, author and page count, fetches the cover, and opens it in the cropper.

  • Covers can also be uploaded, or pasted from the clipboard with ⌘ V (Ctrl V) anywhere in the dialog. Every cover goes through the cropper, which keeps book proportions, and is compressed before it’s saved.
  • No cover? The book gets a cloth-bound cover in one of twelve colours instead.
  • Each book also has a genre, a source (book store, Kindle, audiobook, borrowed, second hand, gifted or library), a status (Read, Reading or TBR), whether you own a copy at home, a rating and the date you finished it.
  • Everything can be typed in by hand too — the catalogue is optional.

Browsing your library

  • Covers shows a wall of covers; Ledger shows a table with status, rating and dates. Both show the most recently finished books first.
  • The search box matches titles, authors, genres and notes. The All · Read · Reading · TBR chips filter by status.
  • Show notes puts a bookmark ribbon on every book that has notes.
  • Click a book to see its details. The pencil icon edits it; Delete removes it along with its notes, pasted images and cover.

Finishing a book

For a book you’re reading, open it and choose Finished Reading. Pick the date you finished and a rating, then Mark as read. It moves to Read and counts towards your statistics.

Notes

Every book has its own notes page — open a book and choose Notes, then Edit. The editor turns Markdown into formatting as you type:

TypeTo get
#, ##, ### and a spaceHeadings
**bold**, *italic*, ~~struck~~, `code`Inline styles
- or 1. and a spaceBulleted or numbered list
> and a spaceQuote

The toolbar does the same, plus links, callout boxes and code blocks. Paste or drop images straight into the text; large ones are downscaled first.

Save keeps your changes; Cancel throws them away, including any images pasted since the last save. Images you delete from a note are cleaned up on the next save.

Vocabulary

The Vocabulary tab keeps the words you learn while reading. Type a word and Bookplate looks it up on Wiktionary, listing each meaning with its part of speech and an example. Add the meaning you were after — or, if the dictionary has nothing, write your own definition.

Tag a word with the book you found it in, and it also appears on that book’s notes page. The list can be searched, sorted and filtered by book.

Statistics

The Statistics tab counts up your reading: books read, read this year, pages turned and average rating, with charts of books per year, ratings, genres and sources.

Everything drills down: click a year, genre, rating or source to see the books behind it, highest rated first, and click any of those books to open it in your library.

Your account

Your profile picture sits in the top-right corner of every page. Click it for Settings and Sign out. In Settings you can:

  • Profile — add, crop or remove a profile photo.
  • Export and Import your library — see Backups.
  • Password — change it. Every other device you’re signed in on is signed out. Forgot it? See Email and forgotten passwords.
  • Email — whether this server can send email, with a button to send yourself a test.
  • Danger zone — delete your account and everything in it, after confirming your password. This can’t be undone, so export first. If it was the only account, the next visit shows the setup screen again.

Reference

Privacy and your data

  • Your library lives only on the machine running Bookplate, in its Postgres database and uploads volume — or, if you choose, with images in your own S3 bucket.
  • Everything is behind your login — covers and images included.
  • Bookplate sends no analytics or telemetry. The only outside services it contacts are Open Library and iTunes (book search and covers) and Wiktionary (definitions), and only when you use those features — through your server, so your browser never talks to them directly. None need an account or API key.
  • You can take everything with you at any time with Export library.

FAQ

Can my family use it too?
Yes — set REGISTRATION=open, run docker compose up -d, and each person chooses Create an account on the sign-in page for their own, separate library. With email set up, they confirm their address first. Anyone who can reach the server can sign up while it’s open, so switch it back to closed afterwards — or see Letting others sign up to keep it open safely.
Can I bring my books over from Goodreads or StoryGraph?
Yes — export your library there and import the file under Settings → Import. See Bring in an existing library.
Is there a phone app?
Bookplate works in your phone’s browser, with tabs along the bottom. Add it to your home screen from the browser’s share menu and it opens like an app.
Is there a hosted version?
Not at the moment — Bookplate is self-hosted only, so your library stays yours.
What does it cost?
Nothing. Bookplate is open source under the Apache 2.0 license. The code is on GitHub.