## File: README.md

<h1 align="center">
  Papra - Document management platform
</h1>




## 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](https://demo.papra.app) (no backend, client-side local storage only).

[](https://demo.papra.app)

## 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](https://github.com/sponsors/corentinth) or [Buy me a coffee](https://buymeacoffee.com/cthmsst).
If you are a company, you can also [contact me](https://papra.app/contact) 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:

```bash
docker run -d --name papra -p 1221:1221 -e AUTH_SECRET=a-dummy-secret-for-testing-purposes-only ghcr.io/papra-hq/papra:latest
```

> The `AUTH_SECRET` above is fine for kicking the tires, but for any real instance you should generate your own with `openssl rand -hex 48` (or similar).

Please refer to the [self-hosting documentation](https://docs.papra.app/self-hosting/using-docker) for more information and configuration options.

## Contributing

Contributions are welcome! Please refer to the [`CONTRIBUTING.md`](./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`](https://github.com/papra-hq/papra/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22) or [`PR welcome`](https://github.com/papra-hq/papra/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20label%3A%22good%20first%20issue%22) labels.

## License

This project is licensed under the AGPL-3.0 License - see the [LICENSE](./LICENSE) file for details.

## Community

Join the community on [Papra's Discord server](https://papra.app/discord) to discuss the project, ask questions, or get help.

## Credits

This project is crafted with ❤️ by [Corentin Thomasset](https://corentin.tech).
If you find this project helpful, please consider [supporting my work](https://buymeacoffee.com/cthmsst).

## Acknowledgements

### Stack

Papra would not have been possible without the following open-source projects:

- **Frontend**
  - **[SolidJS](https://www.solidjs.com)**: A declarative JavaScript library for building user interfaces.
  - **[Shadcn Solid](https://shadcn-solid.com/)**: UI components library for SolidJS based on Shadcn designs.
  - **[UnoCSS](https://unocss.dev/)**: An instant on-demand atomic CSS engine.
  - **[Tabler Icons](https://tablericons.com/)**: A set of open-source icons.
  - And other dependencies listed in the **[client package.json](./apps/papra-client/package.json)**
- **Backend**
  - **[HonoJS](https://hono.dev/)**: A small, fast, and lightweight web framework for building APIs.
  - **[Drizzle](https://orm.drizzle.team/)**: A simple and lightweight ORM for Node.js.
  - **[Better Auth](https://better-auth.com/)**: A simple and lightweight authentication library for Node.js.
  - **[CadenceMQ](https://github.com/papra-hq/cadence-mq)**: A self-hosted-friendly job queue for Node.js, made by Papra.
  - And other dependencies listed in the **[server package.json](./apps/papra-server/package.json)**
- **Documentation**
  - **[Astro](https://astro.build)**: A great static site generator.
  - **[Starlight](https://starlight.astro.build)**: A module for Astro that provides a starting point for building documentation websites.
  - **[HiDeoo/starlight-theme-rapide](https://github.com/HiDeoo/starlight-theme-rapide)**: A theme for Starlight.
- **Project**
  - **[PNPM Workspaces](https://pnpm.io/workspaces)**: A monorepo management tool.
  - **[Github Actions](https://github.com/features/actions)**: For CI/CD.
- **Infrastructure**
  - **[Cloudflare Pages](https://pages.cloudflare.com/)**: For static site hosting.
  - **[Fly.io](https://fly.io/)**: For backend hosting.
  - **[Turso](https://turso.tech/)**: 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](https://paperless-ngx.com/)**: 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](https://papra.app).

## Development

To start the development server, run:

```bash
# Navigate to the docs directory
cd apps/papra-server

# Install dependencies
pnpm install

# Start the development server
pnpm dev
```

The development server will be available at [http://localhost:1221](http://localhost:1221).

---

## File: apps/papra-client/README.md

# Papra - App Client

This is the server for [Papra](https://papra.app).

## Development

> [!IMPORTANT]
> Unless you are developing for the demo mode (`VITE_IS_DEMO_MODE=true` in adjacent `.env` file), you will need to have the server running locally. See the [server README](../papra-server/README.md) for instructions on how to start the server.

To start the development server, run:

```bash
# Navigate to the docs directory
cd apps/papra-client

# Install dependencies
pnpm install

# Start the development server
pnpm dev
```

The development server will be available at [http://localhost:3000](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](https://owlrelay.email).

[OwlRelay](https://owlrelay.email) 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
<Steps>

1. **Create an account on OwlRelay**

   Go to [owlrelay.email](https://owlrelay.email) and create an account.

2. **Create an OwlRelay API key**

   Once you have created your account, you can create an API key by going to the [API keys page](https://app.owlrelay.email/api-keys).

   

3. **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_KEY` and `INTAKE_EMAILS_WEBHOOK_SECRET` environment 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/ingest
   ```

4. **That's it!**

   You can now generate intake emails in your Papra instance and send emails with attachments to the email address generated.

</Steps>

## 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.

<Aside type="note">
  Setting up a Papra Intake Emails with a Cloudflare Email worker requires bit of knowledge about how to setup a CF Email worker and how to configure your Papra instance to receive emails.

  For a simpler solution, you can use the official [OwlRelay integration](/guides/intake-emails-with-owlrelay) guide.
</Aside>

## Prerequisites

In order to follow this guide, you need:
- a [Cloudflare](https://www.cloudflare.com/) 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](/guides/intake-emails-with-owlrelay) 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](https://github.com/papra-hq/email-proxy) repository.



## Setup

<Steps>

1. **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](https://github.com/papra-hq/email-proxy) repository.

    - **Option 1**: From the Cloudflare dashboard (easier).

      - Go to the [Cloudflare dashboard](https://dash.cloudflare.com/).
      - Select your domain.
      - Go to the `Compute (Workers)` tab.
      - Click on the `Create Worker` button.
      - Name your worker (e.g. `email-proxy`).
      - Copy the code from the [index.js](https://github.com/papra-hq/email-proxy/releases/latest/download/index.js) file (from the [papra-hq/email-proxy](https://github.com/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](https://github.com/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
        ```

2. **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 the `INTAKE_EMAILS_WEBHOOK_SECRET` environment variable in your Papra instance.

3. **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-key
   ```

4. **Configure the Email Routing**

   - Go to the `Email Routing` tab in the Cloudflare dashboard.
   - Follow the email onboarding process.
   - Add a catch-all rule to forward all emails to the Email Worker you created.

   

5. **Test the setup**

   In your Papra instance, go to the `Integrations` page 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.
</Steps>

## 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](https://developers.cloudflare.com/workers/observability/logs/real-time-logs/) 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](https://developers.cloudflare.com/workers/observability/logs/real-time-logs/) 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:

<FileTree>
- ingestion-folder
  - org_abc123
    - document.pdf
    - report.docx
  - org_def456
    - file.txt
  - foo.txt # Ignored as it's not in an organization 
</FileTree>


This allows you to have a single instance of Papra watching multiple organizations' ingestion folders.

<Aside>
Files and folders that are within the `ingestion-root-folder` but not within an organization folder are ignored.
</Aside>

## Setup

Add the following to your `docker-compose.yml` file:

```yaml title="docker-compose.yml" ins={9,12}
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>`).

```bash
mkdir -p <your-ingestion-folder>/<org_id>
touch <your-ingestion-folder>/<org_id>/hello.txt
```

## Post-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 the `INGESTION_FOLDER_POST_PROCESSING_MOVE_FOLDER_PATH` folder (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`).

<Aside>
As for the `INGESTION_FOLDER_POST_PROCESSING_MOVE_FOLDER_PATH`, the `INGESTION_FOLDER_ERROR_FOLDER_PATH` path is relative to the organization ingestion folder.
</Aside>

## 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.


```yaml title="docker-compose.yml" ins={2-3}
environment:
   - INGESTION_FOLDER_WATCHER_USE_POLLING=true
   - INGESTION_FOLDER_WATCHER_POLLING_INTERVAL_MS=2000
```

## Configuration

You can find the list of all configuration options in the [configuration reference](/self-hosting/configuration), the related variables are prefixed with `INGESTION_FOLDER_`.


## Edge cases and behaviors

- The ingestion folder is watched recursively.
- Files in the ingestion folder `done` and `error` folders 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](https://github.com/papra-hq/papra/blob/main/apps/papra-server/src/modules/ingestion-folders/ingestion-folders.constants.ts) for the list of ignored files and patterns. You can change this by setting the `INGESTION_FOLDER_IGNORED_PATTERNS` environment 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.

<Aside type="note">
  Papra's OAuth2 implementation is based on the [Better Auth Generic OAuth plugin](https://www.better-auth.com/docs/plugins/generic-oauth). For more detailed information about the configuration options and advanced usage, please refer to their documentation.
</Aside>

## 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:

```bash
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 provider
- `providerName`: The display name of the provider
- `providerIconUrl`: URL of the icon to display (optional) you can use a base64 encoded image or an url to a remote image.
- `clientId`: OAuth client ID
- `clientSecret`: OAuth client secret
- `type`: 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 request
- `redirectURI`: 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

<Steps>

1. **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

2. **Configure Papra**

   Add the `AUTH_PROVIDERS_CUSTOMS` environment 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"]
     }
   ]
   ```

   <Aside type="note">
     The `discoveryUrl` is recommended for OIDC providers as it automatically configures all the necessary endpoints.
     For standard OAuth2 providers, you'll need to specify the endpoints manually.
   </Aside>

3. **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

</Steps>

## 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_CUSTOMS` is 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 `discoveryUrl` is accessible
- Check that the provider supports OIDC discovery
- Ensure the provider's configuration is properly exposed through the discovery endpoint

## Security Considerations

<Aside type="caution">
  Always use HTTPS for your OAuth2 endpoints and ensure your client secret is kept secure.
  Consider using PKCE (Proof Key for Code Exchange) for additional security by setting `pkce: true` in your configuration.
</Aside>

## Multiple Providers

You can configure multiple custom OAuth2 providers by adding them to the array:

```json
[
  {
    "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`, and `x86_64` systems
- **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](https://www.docker.com/get-started)

Verify Docker installation with:

```bash
docker --version
```

## Quick Deployment

Papra requires an `AUTH_SECRET` to run. Generate a secure one using the generator below or the OpenSSL command:

<Tabs>
  <TabItem label="Key generator">
    <SecretKeyGenerator byteLength={48} />
  </TabItem>
  <TabItem label="OpenSSL command">
    ```bash
    openssl rand -hex 48
    ```
  </TabItem>
</Tabs>

Then launch Papra with default configuration using:

```bash
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:latest
```

This command will:
1. Pull the latest rootless image from GitHub Container Registry
2. Expose the web interface on [http://localhost:1221](http://localhost:1221)
3. Configure automatic restarts for service continuity

## Image Variants

Choose between two security models based on your requirements:

- **Rootless**: Tagged as `latest`, `latest-rootless` or `<version>-rootless` (like `0.2.1-rootless`). Recommended for most users.
- **Root**: Tagged as `latest-root` or `<version>-root` (like `0.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.

<Steps>

1. Create Storage Directories

    Create a directory for Papra data `./papra-data`, with `./papra-data/db` and `./papra-data/documents` subdirectories:

    ```bash
    mkdir -p ./papra-data/{db,documents}
    ```

2. Launch Container with Volume Binding

    Make sure you have generated an `AUTH_SECRET` (see the [Quick Deployment](#quick-deployment) section above), then run:

    ```bash
    docker 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:latest
    ```

    This configuration:
    - Maintains data integrity across container lifecycle events
    - Enforces proper file ownership without manual permission adjustments
    - Stores both database files and document assets persistently

</Steps>

## Image Registries

Papra images are distributed through multiple channels:

**Primary Source (GHCR):**
```bash
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-root
```

**Community Mirror (Docker Hub):**
```bash
docker pull corentinth/papra:latest
docker pull corentinth/papra:latest-rootless
docker pull corentinth/papra:latest-root
```

## Updating Papra

Regularly pull updated images and recreate containers to receive security patches and feature updates.

```bash
docker pull ghcr.io/papra-hq/papra:latest
# Or
docker pull corentinth/papra:latest
```

---

## File: 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](/self-hosting/using-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](https://www.docker.com/get-started)

Verify Docker installation with:

```bash
docker --version
docker compose version
```


<Steps>

1. Initialize Project Structure

    Create working directory and persistent storage subdirectories:

    ```bash
    mkdir -p papra/app-data/{db,documents} && cd papra
    ```

2. Generate an Auth Secret

    Papra requires an `AUTH_SECRET` to run. Generate a secure one using the generator below or the OpenSSL command:

    <Tabs>
      <TabItem label="Key generator">
        <SecretKeyGenerator byteLength={48} />
      </TabItem>
      <TabItem label="OpenSSL command">
        ```bash
        openssl rand -hex 48
        ```
      </TabItem>
    </Tabs>

    Keep the value handy, you will set it as an environment variable in the next step.

3. Create Docker Compose file

    Create a file named `docker-compose.yml` with the following content (replacing `<your-auth-secret>` with the value generated in the previous step):

    ```yaml
    services:
      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}"
    ```

4. Start Papra

    From the directory containing your `docker-compose.yml` file, run:

    ```bash
    UID=$(id -u) GID=$(id -g) docker compose up -d
    ```

    This command downloads the latest Papra image, sets up the container, and starts the Papra service. The `UID` and `GID` variables are used to set the user and group for the container, ensuring proper file ownership. If you don't want to use the `UID` and `GID` variables, you can replace the image with the rootful variant.

5. Access Papra

    Once your container is running, access Papra via your browser at:

    ```
    http://localhost:1221
    ```

    Your Papra instance is now ready for use!

6. To go further

    Check the [configuration](/self-hosting/configuration) page for more information on how to configure your Papra instance.

</Steps>

## Maintenance

Check logs

```bash
docker compose logs -f
```

Stop the service

```bash
docker compose down
```

Update Papra

```bash
docker compose pull
docker compose up -d
```

You're all set! Enjoy managing your documents with Papra.