File: README.md
Papra - Document management platform
Introduction
Papra is a minimalistic document management and archiving platform. It is designed to be simple to use and accessible to everyone. Papra is a platform for long-term document storage and management, like a digital archive for your documents.
Forget about that receipt of that gift you bought for your friend last year, or that warranty for your new phone. With Papra, you can easily store, forget, and retrieve your documents whenever you need them.
A live demo of the platform is available at demo.papra.app (no backend, client-side local storage only).
Features
- Document management: Upload, store, and manage your documents in one place.
- Organizations: Create organizations to manage documents with family, friends, or colleagues.
- Search: Quickly search for documents with full-text search and advanced filters.
- Authentication: User accounts and authentication.
- Dark Mode: A dark theme for those late-night document management sessions.
- Responsive Design: Works on all devices, from desktops to mobile phones.
- Open Source: The project is open-source and free to use.
- Self-hosting: Host your own instance of Papra using Docker or other methods.
- Tags: Organize your documents with tags.
- Email ingestion: Send/forward emails to a generated address to automatically import documents.
- Content extraction: Automatically extract text from images or scanned documents for search.
- Tagging Rules: Automatically tag documents based on custom rules.
- Folder ingestion: Automatically import documents from a folder.
- CLI: Manage your documents from the command line.
- API, SDK and webhooks: Build your own applications on top of Papra.
- i18n: Support for multiple languages.
- Custom properties: Define per-organization custom properties to store additional information about documents.
- Document sharing: Share documents with external users with optional expiration dates and password protection.
- Coming soon: Document requests: Generate upload links for people to add documents.
- Coming maybe one day: Mobile app: Access and upload documents on the go.
- Coming maybe one day: Desktop app: Access and upload documents from your computer.
- Coming maybe one day: Browser extension: Upload documents from your browser.
- Coming maybe one day: AI: Use AI to help you manage or tag your documents.
Support
Papra is a free and open-source project, but it is not free to run and develop. If you want to support the project, you can become a sponsor on GitHub Sponsors or Buy me a coffee.
If you are a company, you can also contact me to discuss a sponsorship.
Self-hosting
Papra is dedicated to providing a simple yet highly configurable self-hosting experience.
For a quick start, simply run the following command:
docker run -d --name papra -p 1221:1221 -e AUTH_SECRET=a-dummy-secret-for-testing-purposes-only ghcr.io/papra-hq/papra:latestThe
AUTH_SECRETabove is fine for kicking the tires, but for any real instance you should generate your own withopenssl rand -hex 48(or similar).
Please refer to the self-hosting documentation for more information and configuration options.
Contributing
Contributions are welcome! Please refer to the CONTRIBUTING.md file for guidelines on how to get started, report issues, and submit pull requests.
You can find easy-to-pick-up tasks with the good first issue or PR welcome labels.
License
This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.
Community
Join the community on Papra's Discord server to discuss the project, ask questions, or get help.
Credits
This project is crafted with โค๏ธ by Corentin Thomasset.
If you find this project helpful, please consider supporting my work.
Acknowledgements
Stack
Papra would not have been possible without the following open-source projects:
- Frontend
- SolidJS: A declarative JavaScript library for building user interfaces.
- Shadcn Solid: UI components library for SolidJS based on Shadcn designs.
- UnoCSS: An instant on-demand atomic CSS engine.
- Tabler Icons: A set of open-source icons.
- And other dependencies listed in the client package.json
- Backend
- HonoJS: A small, fast, and lightweight web framework for building APIs.
- Drizzle: A simple and lightweight ORM for Node.js.
- Better Auth: A simple and lightweight authentication library for Node.js.
- CadenceMQ: A self-hosted-friendly job queue for Node.js, made by Papra.
- And other dependencies listed in the server package.json
- Documentation
- Astro: A great static site generator.
- Starlight: A module for Astro that provides a starting point for building documentation websites.
- HiDeoo/starlight-theme-rapide: A theme for Starlight.
- Project
- PNPM Workspaces: A monorepo management tool.
- Github Actions: For CI/CD.
- Infrastructure
- Cloudflare Pages: For static site hosting.
- Fly.io: For backend hosting.
- Turso: For production database.
Inspiration
This project would not have been possible without the inspiration and work of others. Here are some projects that inspired me:
- Paperless-ngx: A full-featured document management system.
Sponsors
Shout-out to our current sponsors:
File: apps/papra-server/README.md
Papra - App server
This is the server for Papra.
Development
To start the development server, run:
# Navigate to the docs directory
cd apps/papra-server
# Install dependencies
pnpm install
# Start the development server
pnpm devThe development server will be available at http://localhost:1221.
File: apps/papra-client/README.md
Papra - App Client
This is the server for Papra.
Development
[!IMPORTANT]
Unless you are developing for the demo mode (VITE_IS_DEMO_MODE=truein adjacent.envfile), you will need to have the server running locally. See the server README for instructions on how to start the server.
To start the development server, run:
# Navigate to the docs directory
cd apps/papra-client
# Install dependencies
pnpm install
# Start the development server
pnpm devThe development server will be available at http://localhost:3000.
File: apps/docs/src/content/docs/03-guides/01-intake-emails-with-owlrelay.mdx
title: Setup Intake Emails with OwlRelay slug: guides/intake-emails-with-owlrelay
import { Aside } from '@astrojs/starlight/components';
import { Steps } from '@astrojs/starlight/components';
This guide will show you how to setup Papra to receive emails using OwlRelay.
OwlRelay is an open-source agnostic email-to-http service developed by the Papra team. It's a free service that allows you to receive emails and forward them to your self-hosted Papra instance.
Prerequisites
In order to follow this guide, your Papra instance needs to be running and accessible from the internet.
How it works
By integrating Papra with OwlRelay, your instance will generate email addresses on OwlRelay through it's API (specifying a webhook on your Papra instance). And OwlRelay will forward the emails to your Papra instance through a webhook.
Setup
Create an account on OwlRelay
Go to owlrelay.email and create an account.
Create an OwlRelay API key
Once you have created your account, you can create an API key by going to the API keys page.
Configure your Papra instance
Once you have created your API key, you can configure your Papra instance to receive emails by setting the
OWLRELAY_API_KEYandINTAKE_EMAILS_WEBHOOK_SECRETenvironment variables.bash# Enable intake emails INTAKE_EMAILS_IS_ENABLED=true # Tell your Papra instance to use OwlRelay INTAKE_EMAILS_DRIVER=owlrelay # This is your OwlRelay API key OWLRELAY_API_KEY=owrl_***** # Set a random key that will be transmitted to OwlRelay to sign the requests, # this is to authenticate that the emails are coming from OwlRelay INTAKE_EMAILS_WEBHOOK_SECRET=a-random-key # [Optional] # This is the URL that OwlRelay will send the emails to, # if not provided, the webhook will be inferred from the server URL. # Can be relevant if you have multiple urls pointing to your Papra instance # or when using tunnel services OWLRELAY_WEBHOOK_URL=https://your-instance.com/api/intake-emails/ingestThat's it!
You can now generate intake emails in your Papra instance and send emails with attachments to the email address generated.
Troubleshooting
If you encounter any issues, you can:
- Check the logs of your Papra instance to see if there are any errors.
- Connect to your OwlRelay account to see if the emails address are generated and emails are received.
File: apps/docs/src/content/docs/03-guides/02-intake-emails-with-cf-email-worker.mdx
title: Setup Intake Emails with CF Email Workers slug: guides/intake-emails-with-cloudflare-email-workers
import { Aside } from '@astrojs/starlight/components';
import { Steps } from '@astrojs/starlight/components';
This guide will show you how to setup a Cloudflare Email worker to receive emails in your Papra instance.
Prerequisites
In order to follow this guide, you need:
- a Cloudflare account
- a custom domain name available on Cloudflare
- a publicly accessible Papra instance
- basic development skills (git and node.js to setup the Email Worker)
If you prefer a simpler solution, you can use the official OwlRelay integration guide.
How it works
In order to receive emails in your Papra instance, we need to convert the email to an HTTP request. This is currently done by setting up a Cloudflare Email Worker that will forward the email to your Papra instance, basically acting as a bridge between the email and your Papra instance.
The code for the Email Worker proxy is available in the papra-hq/email-proxy repository.
Setup
Create the Email Worker
There are two ways to create an Email Worker, either from the Cloudflare dashboard or by cloning and deploying the code from the papra-hq/email-proxy repository.
Option 1: From the Cloudflare dashboard (easier).
- Go to the Cloudflare dashboard.
- Select your domain.
- Go to the
Compute (Workers)tab. - Click on the
Create Workerbutton. - Name your worker (e.g.
email-proxy). - Copy the code from the index.js file (from the papra-hq/email-proxy repository) and paste it in the editor.
Option 2: Build and deploy the Email Worker
Clone the papra-hq/email-proxy repository and deploy the worker using Wrangler cli. You will need to have Node.js v24 and pnpm installed.
bash# Clone the repository git clone https://github.com/papra-hq/email-proxy.git # Change directory cd email-proxy # Install dependencies pnpm install # Build the worker pnpm build # Deploy the worker (you will be prompted to login to Cloudflare through wrangler) pnpm run deploy
Configure the Email Worker
Add the following environment variables to the worker:
WEBHOOK_URL: The email intake endpoint in your Papra instance (basically.https://<your-papra-instance.com>/api/intake-emails/ingest).WEBHOOK_SECRET: The secret key to authenticate the webhook requests, set the same as theINTAKE_EMAILS_WEBHOOK_SECRETenvironment variable in your Papra instance.
Configure your Papra instance
In your Papra instance, add the following environment variables:
bash# Enable intake emails INTAKE_EMAILS_IS_ENABLED=true # Tell your Papra instance that it can generate any email address from the # domain you setup in the Email Worker as it's a wildcard redirection INTAKE_EMAILS_DRIVER=catch-all # This is the domain from which the intake email will be generated # eg. `domain.com` INTAKE_EMAILS_CATCH_ALL_DOMAIN=papra.email # This is the secret key to authenticate the webhook requests # set the same as the `WEBHOOK_SECRET` variable in the Email Worker INTAKE_EMAILS_WEBHOOK_SECRET=a-random-keyConfigure the Email Routing
- Go to the
Email Routingtab in the Cloudflare dashboard. - Follow the email onboarding process.
- Add a catch-all rule to forward all emails to the Email Worker you created.
- Go to the
Test the setup
In your Papra instance, go to the
Integrationspage in your organization and generates an intake email URL, setup an allowed sender (basically your email address), and copy the generated email address. Send an email to the generated address with a file attached, and check if the file is uploaded to your Papra instance.
Troubleshooting
View logs using wrangler tail
To view real-time logs associated with any deployed Worker using Wrangler:
- Go to your Worker project directory.
- Run npx wrangler tail.
This will log any incoming requests to your application available in your local terminal.
Email Worker not receiving emails
If the Email Worker is not receiving emails, make sure that the email routing is correctly configured in the Cloudflare dashboard.
Also, check the logs in the Email Worker dashboard for any errors.
Papra instance returning 403 Forbidden
If your Papra instance is returning a 403 Forbidden error, make sure that the INTAKE_EMAILS_IS_ENABLED environment variable is set to true in your Papra instance.
Papra instance is returning 401 Unauthorized
If your Papra instance is returning a 401 Unauthorized error, make sure that the INTAKE_EMAILS_WEBHOOK_SECRET environment variable is correctly set in your Papra instance.
The worker is not forwarding the email to the Papra instance
Make sure that the WEBHOOK_URL and WEBHOOK_SECRET environment variables are correctly set in the Email Worker, and the INTAKE_EMAILS_WEBHOOK_SECRET environment variable is correctly set in your Papra instance.
Also, check the logs in the Email Worker dashboard or the logs in your Papra instance for any errors.
TypeError: Too many redirects.
If you get "too many redirects" (check log from wrangler), check that your domain is configured with SSL/TLS Encryption as Full (strict).
Cloudflare dashboard โ (domain) โ SSL/TLS โ Overview โ set encryption mode to Full (strict) or at minimum Full. Save, then re-send a test email.
File: apps/docs/src/content/docs/03-guides/03-setup-ingestion-folder.mdx
title: Setup Ingestion Folder slug: guides/setup-ingestion-folder
import { Steps } from '@astrojs/starlight/components';
import { Aside } from '@astrojs/starlight/components';
import { FileTree } from '@astrojs/starlight/components';
The ingestion folder is a special folder that is watched by Papra for new files. When a new file is added to the ingestion folder, Papra will automatically import it.
Multi-Organization Structure
Papra supports multiple organizations within a single instance, each requiring a dedicated ingestion folder. The ingestion system uses a hierarchical structure where:
This allows you to have a single instance of Papra watching multiple organizations' ingestion folders.
Setup
Add the following to your docker-compose.yml file:
services:
papra:
container_name: papra
image: ghcr.io/papra-hq/papra:latest
restart: unless-stopped
ports:
- "1221:1221"
environment:
- INGESTION_FOLDER_IS_ENABLED=true
volumes:
- ./app-data:/app/app-data
- <your-ingestion-folder>:/app/ingestion
user: "${UID}:${GID}"Then add files to a folder named with the organization id (available in Papra URL, e.g. https://papra.example.com/organizations/<organization-id>, the format is org_<random>).
mkdir -p <your-ingestion-folder>/<org_id>
touch <your-ingestion-folder>/<org_id>/hello.txtPost-processing
Once a file has been ingested in your Papra organization, you can configure what happens to it by setting the INGESTION_FOLDER_POST_PROCESSING_STRATEGY environment variable. There are two strategies:
delete: The file is deleted from the ingestion folder (default strategy)move: The file is moved to theINGESTION_FOLDER_POST_PROCESSING_MOVE_FOLDER_PATHfolder (default:./ingestion-done)
Note that the INGESTION_FOLDER_POST_PROCESSING_MOVE_FOLDER_PATH path is relative to the organization ingestion folder.
So with INGESTION_FOLDER_POST_PROCESSING_MOVE_FOLDER_PATH=ingestion-done, the file /<org_id>/file.pdf will be moved to /<org_id>/ingestion-done/file.pdf once ingested.
Safeguards
To avoid accidental data loss, if for some reason the ingestion fails, the file is moved to the INGESTION_FOLDER_ERROR_FOLDER_PATH folder (default: ./ingestion-error).
Polling
By default, Papra uses native file watchers to detect changes in the ingestion folder. On some OS (like Windows), this can be flaky with Docker. To avoid this issue, you can enable polling by setting the INGESTION_FOLDER_WATCHER_USE_POLLING environment variable to true.
The default polling interval is 2 seconds, you can change it by setting the INGESTION_FOLDER_WATCHER_POLLING_INTERVAL_MS environment variable.
environment:
- INGESTION_FOLDER_WATCHER_USE_POLLING=true
- INGESTION_FOLDER_WATCHER_POLLING_INTERVAL_MS=2000Configuration
You can find the list of all configuration options in the configuration reference, the related variables are prefixed with INGESTION_FOLDER_.
Edge cases and behaviors
- The ingestion folder is watched recursively.
- Files in the ingestion folder
doneanderrorfolders are ignored. - When a file from the ingestion folder is already present (and not in the trash) in the organization, no ingestion is done, but the file is post-processed (deleted or moved) as successfully ingested.
- When a file is moved to "done" or "error" folder
- If a file with the same name and same content is present in the destination folder, the original file is deleted
- If a file with the same name but different content is present in the destination folder, the original file is moved and a timestamp is added to the filename
- Some files are ignored by default (
.DS_Store,Thumbs.db,desktop.ini, etc.) see ingestion-folders.constants.ts for the list of ignored files and patterns. You can change this by setting theINGESTION_FOLDER_IGNORED_PATTERNSenvironment variable.
File: apps/docs/src/content/docs/03-guides/04-setup-custom-oauth2.mdx
title: Setup Custom OAuth2 Providers slug: guides/setup-custom-oauth2-providers
import { Aside } from '@astrojs/starlight/components';
import { Steps } from '@astrojs/starlight/components';
This guide will show you how to configure custom OAuth2 providers for authentication in your Papra instance.
Prerequisites
In order to follow this guide, you need:
- A custom OAuth2 provider
- An accessible Papra instance
- Basic understanding of OAuth2 flows
Configuration
To set up custom OAuth2 providers, you'll need to configure the AUTH_PROVIDERS_CUSTOMS environment variable with an array of provider configurations. Here's an example:
AUTH_PROVIDERS_CUSTOMS='[
{
"providerId": "custom-oauth2",
"providerName": "Custom OAuth2",
"providerIconUrl": "https://api.iconify.design/tabler:login-2.svg",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"type": "oidc",
"discoveryUrl": "https://your-provider.tld/.well-known/openid-configuration",
"scopes": ["openid", "profile", "email"]
}
]'Each provider configuration supports the following fields:
providerId: A unique identifier for the OAuth providerproviderName: The display name of the providerproviderIconUrl: URL of the icon to display (optional) you can use a base64 encoded image or an url to a remote image.clientId: OAuth client IDclientSecret: OAuth client secrettype: Type of OAuth flow ("oauth2" or "oidc")discoveryUrl: URL to fetch OAuth 2.0 configuration (recommended for OIDC providers)authorizationUrl: URL for the authorization endpoint (required for OAuth2 if not using discoveryUrl)tokenUrl: URL for the token endpoint (required for OAuth2 if not using discoveryUrl)userInfoUrl: URL for the user info endpoint (required for OAuth2 if not using discoveryUrl)scopes: Array of OAuth scopes to requestredirectURI: Custom redirect URI (optional)responseType: OAuth response type (defaults to "code")prompt: Controls the authentication experience ("select_account", "consent", "login", "none")pkce: Whether to use PKCE (Proof Key for Code Exchange)accessType: Access type for the authorization request
Setup
Configure your OAuth2 Provider
First, you'll need to register your application with your OAuth2 provider. This typically involves:
- Creating a new application in your provider's dashboard
- Setting up the redirect URI (usually
https://<your-papra-instance>/api/auth/oauth2/callback/:providerId) - Obtaining the client ID and client secret
- Configuring the required scopes
Configure Papra
Add the
AUTH_PROVIDERS_CUSTOMSenvironment variable to your Papra instance. Here are some examples:For OIDC providers:
json[ { "providerId": "custom-oauth2", "providerName": "Custom OAuth2", "providerIconUrl": "https://api.iconify.design/tabler:login-2.svg", "clientId": "your-client-id", "clientSecret": "your-client-secret", "type": "oidc", "discoveryUrl": "https://your-provider.tld/.well-known/openid-configuration", "scopes": ["openid", "profile", "email"] } ]For standard OAuth2 providers:
json[ { "providerId": "custom-oauth2", "providerName": "Custom OAuth2", "providerIconUrl": "https://api.iconify.design/tabler:login-2.svg", "clientId": "your-client-id", "clientSecret": "your-client-secret", "type": "oauth2", "authorizationUrl": "https://your-provider.tld/oauth2/authorize", "tokenUrl": "https://your-provider.tld/oauth2/token", "userInfoUrl": "https://your-provider.tld/oauth2/userinfo", "scopes": ["profile", "email"] } ]Test the Configuration
- Restart your Papra instance to apply the changes
- Go to the login page
- You should see your custom OAuth2 providers as login options
- Try logging in with a test account
Troubleshooting
Providers Not Showing Up
If your OAuth2 providers are not showing up on the login page:
- Check that the JSON configuration in
AUTH_PROVIDERS_CUSTOMSis valid - Ensure all required fields are provided
- Verify that the provider IDs are unique
Authentication Fails
If authentication fails:
- Verify that the redirect URI is correctly configured in your OAuth2 provider
- Check that the client ID and client secret are correct
- Ensure the required scopes are properly configured
- Check the Papra logs for any error messages
OIDC Discovery Issues
If you're using OIDC and experiencing issues:
- Verify that the
discoveryUrlis accessible - Check that the provider supports OIDC discovery
- Ensure the provider's configuration is properly exposed through the discovery endpoint
Security Considerations
Multiple Providers
You can configure multiple custom OAuth2 providers by adding them to the array:
[
{
"providerId": "custom-oauth2-1",
"providerName": "Custom OAuth2 Provider 1",
"type": "oidc",
"discoveryUrl": "https://provider1.tld/.well-known/openid-configuration",
"clientId": "client-id-1",
"clientSecret": "client-secret-1",
"scopes": ["openid", "profile", "email"]
},
{
"providerId": "custom-oauth2-2",
"providerName": "Custom OAuth2 Provider 2",
"type": "oidc",
"discoveryUrl": "https://provider2.tld/.well-known/openid-configuration",
"clientId": "client-id-2",
"clientSecret": "client-secret-2",
"scopes": ["openid", "profile", "email"]
}
]File: apps/docs/src/content/docs/02-self-hosting/01-using-docker.mdx
title: Installing Papra using Docker slug: self-hosting/using-docker
import { Steps } from '@astrojs/starlight/components';
import { Tabs, TabItem } from '@astrojs/starlight/components';
import SecretKeyGenerator from '../../../components/random-hex-key-generator.astro';
Papra provides optimized Docker images for streamlined deployment. This method is recommended for users seeking a production-ready setup with minimal maintenance overhead.
- Simplified management: Single container handles all components
- Lightweight: Optimized image sizes across architectures
- Cross-platform support: Compatible with
arm64,arm/v7, andx86_64systems - Security options: Supports both rootless (recommended) and rootful configurations
Prerequisites
Ensure Docker is installed on your host system. Official installation guides are available at:
docker.com/get-started
Verify Docker installation with:
docker --versionQuick Deployment
Papra requires an AUTH_SECRET to run. Generate a secure one using the generator below or the OpenSSL command:
Then launch Papra with default configuration using:
docker run -d \
--name papra \
--restart unless-stopped \
--env APP_BASE_URL=http://localhost:1221 \
--env AUTH_SECRET=<your-auth-secret> \
-p 1221:1221 \
ghcr.io/papra-hq/papra:latestThis command will:
- Pull the latest rootless image from GitHub Container Registry
- Expose the web interface on http://localhost:1221
- Configure automatic restarts for service continuity
Image Variants
Choose between two security models based on your requirements:
- Rootless: Tagged as
latest,latest-rootlessor<version>-rootless(like0.2.1-rootless). Recommended for most users. - Root: Tagged as
latest-rootor<version>-root(like0.2.1-root). Only use if you need to run Papra as the root user.
The :latest tag always references the latest rootless build.
Persistent Data Configuration
For production deployments, mount host directories to preserve application data between container updates.
Create Storage Directories
Create a directory for Papra data
./papra-data, with./papra-data/dband./papra-data/documentssubdirectories:bashmkdir -p ./papra-data/{db,documents}Launch Container with Volume Binding
Make sure you have generated an
AUTH_SECRET(see the Quick Deployment section above), then run:bashdocker run -d \ --name papra \ --restart unless-stopped \ --env APP_BASE_URL=http://localhost:1221 \ --env AUTH_SECRET=<your-auth-secret> \ -p 1221:1221 \ -v $(pwd)/papra-data:/app/app-data \ --user $(id -u):$(id -g) \ ghcr.io/papra-hq/papra:latestThis configuration:
- Maintains data integrity across container lifecycle events
- Enforces proper file ownership without manual permission adjustments
- Stores both database files and document assets persistently
Image Registries
Papra images are distributed through multiple channels:
Primary Source (GHCR):
docker pull ghcr.io/papra-hq/papra:latest
docker pull ghcr.io/papra-hq/papra:latest-rootless
docker pull ghcr.io/papra-hq/papra:latest-rootCommunity Mirror (Docker Hub):
docker pull corentinth/papra:latest
docker pull corentinth/papra:latest-rootless
docker pull corentinth/papra:latest-rootUpdating Papra
Regularly pull updated images and recreate containers to receive security patches and feature updates.
docker pull ghcr.io/papra-hq/papra:latest
# Or
docker pull corentinth/papra:latestFile: apps/docs/src/content/docs/02-self-hosting/02-using-docker-compose.mdx
title: Using Docker Compose slug: self-hosting/using-docker-compose
import { Steps } from '@astrojs/starlight/components';
import { Tabs, TabItem } from '@astrojs/starlight/components';
import SecretKeyGenerator from '../../../components/random-hex-key-generator.astro';
This guide covers how to deploy Papra using Docker Compose, ideal for users who prefer declarative configurations or plan to integrate Papra into a broader service stack.
Using Docker Compose provides:
- A single, versioned configuration file
- Easy integration with volumes, networks, and service dependencies
- Simplified updates and re-deployments
This method supports both rootless and rootful Papra images, please refer to the Docker guide for more information about the difference between the two. The following example uses the recommended rootless setup.
Prerequisites
Ensure Docker and Docker Compose are installed on your host system. Official installation guides are available at: docker.com/get-started
Verify Docker installation with:
docker --version
docker compose versionInitialize Project Structure
Create working directory and persistent storage subdirectories:
bashmkdir -p papra/app-data/{db,documents} && cd papraGenerate an Auth Secret
Papra requires an
AUTH_SECRETto run. Generate a secure one using the generator below or the OpenSSL command:```bash openssl rand -hex 48 ``` Keep the value handy, you will set it as an environment variable in the next step.
Create Docker Compose file
Create a file named
docker-compose.ymlwith the following content (replacing<your-auth-secret>with the value generated in the previous step):yamlservices: papra: container_name: papra image: ghcr.io/papra-hq/papra:latest restart: unless-stopped ports: - "1221:1221" environment: - AUTH_SECRET=<your-auth-secret> volumes: - ./app-data:/app/app-data user: "${UID}:${GID}"Start Papra
From the directory containing your
docker-compose.ymlfile, run:bashUID=$(id -u) GID=$(id -g) docker compose up -dThis command downloads the latest Papra image, sets up the container, and starts the Papra service. The
UIDandGIDvariables are used to set the user and group for the container, ensuring proper file ownership. If you don't want to use theUIDandGIDvariables, you can replace the image with the rootful variant.Access Papra
Once your container is running, access Papra via your browser at:
texthttp://localhost:1221Your Papra instance is now ready for use!
To go further
Check the configuration page for more information on how to configure your Papra instance.
Maintenance
Check logs
docker compose logs -fStop the service
docker compose downUpdate Papra
docker compose pull
docker compose up -dYou're all set! Enjoy managing your documents with Papra.