Licensed under GPL-3.0-only; see LICENSE. See RELEASE.md for verification commands and conversion limitations.
A self-hosted reading and listening library for EPUB, MOBI, PDF, MP3, and WAV files. EPUB and MOBI books are converted to PDF; PaperBrain remembers page or playback progress.
Install with Docker or bare metal, without Docker. See CHANGELOG.md for the initial 0.1.0 release.
Conversion limits: EPUB/MOBI conversion is text-first. Images, tables, complex layout, and some non-Latin text may be lost. DRM-protected MOBI and KF8-only books are unsupported. Original PDFs retain their layout; scanned PDFs have no OCR.
Demo content generated for these screenshots; no private books or accounts are shown.
Requirements: Docker Engine with Docker Compose.
git clone https://github.com/1338/PaperBrain.git
cd PaperBrain
cp .env.example .envBefore starting, edit .env:
- replace
SESSION_SECRETwith the output ofopenssl rand -hex 32; - replace
POSTGRES_PASSWORDwith a strong URL-safe password; - optionally change
APP_PORTfrom3000.
Start the complete application:
docker compose up -d --buildOpen http://localhost:3000 (or your APP_PORT). The application waits for PostgreSQL, applies database migrations automatically, and then starts the web server. Create your own administrator account before exposing the service publicly.
Check status and logs with:
docker compose ps
docker compose logs -f appCompose creates two named volumes:
paperbrain_database_datacontains accounts, library metadata, sessions, and reading progress;paperbrain_book_storagecontains uploaded ebooks/PDFs/audio and generated PDFs.
The paperbrain_ prefix assumes the default project directory name. If you use a
different directory or docker compose -p NAME, the volumes use that project
prefix; substitute the actual volume name in backup/restore commands.
Normal container recreation and docker compose down preserve both volumes. Do not run docker compose down --volumes unless you intend to permanently delete all PaperBrain data.
Stop the app to keep the database and files consistent during backup (the database service stays running), then create both backups:
mkdir -p backups
chmod 700 backups
docker compose stop app
docker compose exec -T database pg_dump -U app app > backups/paperbrain.sql
docker run --rm -v paperbrain_book_storage:/data -v "$PWD/backups:/backup" alpine tar czf /backup/paperbrain-books.tgz -C /data .
docker compose start appIf you changed POSTGRES_USER or POSTGRES_DB, use those values in the pg_dump command. Test restoring backups before relying on them.
Securely back up .env separately; the original SESSION_SECRET is needed to
decrypt saved credentials. S3 objects require their own backup.
Restore into empty volumes on a separate host/project first. These commands assume the default database name/user (app), the Compose project name paperbrain, and the backup filenames above. Keep the application stopped until both backups are restored:
docker compose -p paperbrain up -d database
docker compose -p paperbrain exec -T database psql -v ON_ERROR_STOP=1 -U app -d app < backups/paperbrain.sql
docker volume create paperbrain_book_storage
docker run --rm -v paperbrain_book_storage:/data -v "$PWD/backups:/backup:ro" alpine tar xzf /backup/paperbrain-books.tgz -C /data
docker compose -p paperbrain up -d --build appRestore the original SESSION_SECRET in your private .env before starting. For S3 libraries, retain/restore the bucket objects as well as the database. The filesystem archive does not contain S3 objects. Check a PDF, an audio item, and saved progress after restoring. Do not import a dump over an existing database; use a fresh restore target.
An administrator with shell access can reset an existing account's password. The command does not send email, create accounts, change roles, activate disabled accounts, or bypass email verification. It revokes existing login sessions. Use a new password of at least 12 characters; communicate it privately and have the user change it in Profile after logging in.
Run these commands in Bash (the password is hidden and is not a command argument):
read -r -s -p 'New password: ' recovery_password
printf '\n'
printf '%s' "$recovery_password" | docker compose exec -T app npm run account:recover -- user@example.com
unset recovery_passwordFor a non-Docker installation, pipe the password into npm run account:recover -- user@example.com from the project directory with the correct private .env.
Use the admin panel to resolve disabled/unverified accounts separately.
EPUB/MOBI uploads enter a persistent database queue. The library refreshes their
status automatically; leaving the page does not cancel a job. One conversion
runs per database at a time in an isolated Node process (512 MiB JavaScript heap
limit). CONVERSION_TIMEOUT_SECONDS defaults to 300; timed-out jobs show an error
and can be retried. PDF/audio metadata processing remains synchronous.
Start with npm start, npm run dev, or Docker so the queue runner is active.
Interrupted ebook jobs are picked up again after restart. Instances sharing a
database must also share the local storage directory: queued sources are local
until conversion and any S3 transfer finish. S3 configuration at processing time
determines the destination. Do not change storage configuration with jobs pending.
The heap limit is not a total process-memory cap; use container memory limits for
untrusted uploads. An active conversion must finish or time out before deletion.
git pull
docker compose up -d --buildThe application image runs the idempotent schema migration before each startup. Back up both volumes before significant upgrades.
For use outside a trusted local network, place PaperBrain behind an HTTPS reverse proxy such as Caddy, Traefik, or nginx. Set this in .env once requests use HTTPS:
COOKIE_SECURE=trueLeave it false only for direct HTTP access. PaperBrain trusts one reverse-proxy hop and binds the published application port configured by APP_PORT.
The PostgreSQL port is bound only to 127.0.0.1 for local maintenance. It is not exposed on the host network externally.
| Variable | Default | Purpose |
|---|---|---|
APP_PORT |
3000 |
Host port for PaperBrain |
PORT |
3000 |
Bare-metal HTTP port; Compose sets the internal port automatically |
DATABASE_URL |
Local development URL | Bare-metal PostgreSQL connection string |
STORAGE_PATH |
./storage |
Bare-metal storage path; Compose uses /data |
CONVERSION_TIMEOUT_SECONDS |
300 |
Maximum background conversion job duration |
SESSION_SECRET |
Required | Random session-signing secret of at least 32 characters |
COOKIE_SECURE |
false |
Send the session cookie only over HTTPS |
ADMIN_EMAIL |
Empty | Optionally grant admin to a matching registering account |
MAX_UPLOAD_SIZE_MB |
100 |
Maximum EPUB, MOBI, PDF, MP3, or WAV upload size |
POSTGRES_DB |
app |
PostgreSQL database name |
POSTGRES_USER |
app |
PostgreSQL user |
POSTGRES_PASSWORD |
ChangeMe |
PostgreSQL password; change before deployment |
POSTGRES_PORT |
5432 |
Optional localhost-only database port |
Inside the container, DATABASE_URL, NODE_ENV, PORT, and STORAGE_PATH are configured automatically by Compose.
The bundled PostgreSQL service uses DATABASE_SSL=false. Manual deployments using a managed PostgreSQL provider can set DATABASE_SSL=true when that provider requires TLS.
Requirements: Node.js 22.13+, npm, and PostgreSQL (Docker is optional).
Configure .env as described in the Docker or bare-metal guide before continuing.
docker compose up -d database # Skip if using an existing PostgreSQL server
npm ci
npm run db:migrate
npm run devOpen http://localhost:5173. Vite proxies API requests to the Node server on port 3000. If the production app container is already running, stop it first with docker compose stop app to free that port.
For development, keep PORT=3000 or update the API proxy target in vite.config.js.
If Docker database credentials/ports differ from defaults, also update the
host-side DATABASE_URL in .env; Compose does not generate that value for npm.
npm run dev # Run frontend and API in watch mode
npm run build # Build the Vue application
npm start # Serve the production API and built frontend
npm run db:migrate # Create/update the PostgreSQL schema
npm test # Run tests- Use Previous/Next or the left/right arrow keys to change pages.
- Page Up/Down and Space also navigate; Home/End jump to the first/last page.
- Zoom controls and Fit width are available in the reader toolbar.
- Reading progress is saved automatically.
- Standalone PDFs open directly without conversion.
- MP3 and WAV playback position is restored automatically.
- Failed conversions can be retried from the library.
- Deleting an item permanently removes its database record and all stored source/derived files.
GET /api/health/livechecks the application process.GET /api/health/readychecks database availability.
Docker uses the readiness check to report container health.
See FUNCTIONALITY.md for the complete feature index and current conversion limitations.
The first account on an instance is automatically an administrator. Existing installations promote the oldest account during migration if no administrator exists. ADMIN_EMAIL can also identify an account that should receive the administrator role when it registers.
Administrators can open /admin to:
- enable or disable public registration;
- require email verification;
- configure SMTP and send a test email;
- choose local file storage or configure and test an S3-compatible bucket;
- activate, disable, verify, promote, demote, or delete users.
SMTP passwords and S3 secret keys are encrypted in PostgreSQL using a key derived from SESSION_SECRET. Keep that secret stable across upgrades; changing it prevents PaperBrain from decrypting saved credentials.
Local filesystem storage is the default and uses the persistent Docker data volume described above. S3 mode supports AWS S3 and compatible services such as MinIO: provide a region and bucket, optionally a custom endpoint, and enable path-style URLs when the provider requires them. Files are served through PaperBrain, so the bucket does not need public access or browser CORS rules.
The selected backend applies to new uploads. Existing local files remain local and existing S3 files remain in their bucket. Test S3 settings before saving them, and do not change or remove a bucket or credentials while it still contains library files.
Authenticated users can open /profile to change their display name, email address, or password. Email and password changes require the current password. When instance email verification is enabled, an email change sends a fresh verification link and signs the user out until the new address is verified.


