An open API service indexing awesome lists of open source software.

https://github.com/daemonless/papra


https://github.com/daemonless/papra

Last synced: 12 days ago
JSON representation

Awesome Lists containing this project

README

          

# Papra

[![Build Status](https://img.shields.io/github/actions/workflow/status/daemonless/papra/build.yaml?style=flat-square&label=Build&color=green)](https://github.com/daemonless/papra/actions)
[![Last Commit](https://img.shields.io/github/last-commit/daemonless/papra?style=flat-square&label=Last+Commit&color=blue)](https://github.com/daemonless/papra/commits)

Minimalist self-hosted document management platform (Paperless alternative) on FreeBSD.

| | |
|---|---|
| **Registry** | `ghcr.io/daemonless/papra` |
| **Source** | [https://github.com/papra-hq/papra](https://github.com/papra-hq/papra) |
| **Website** | [https://papra.app](https://papra.app) |

## Version Tags

| Tag | Description | Best For |
| :--- | :--- | :--- |
| `latest` | **Upstream Binary**. Built from official release. | Most users. Matches Linux Docker behavior. |

## Prerequisites

Before deploying, ensure your host environment is ready. See the [Quick Start Guide](https://daemonless.io/guides/quick-start) for host setup instructions.

## Deployment

### Podman Compose

```yaml
services:
papra:
image: "ghcr.io/daemonless/papra:latest"
container_name: papra
environment:
- PUID=1000 # User ID for the application process
- PGID=1000 # Group ID for the application process
- TZ=Etc/UTC # Timezone for the container
- NODE_ENV=production # Node runtime mode; leave as 'production'
- PORT=1222 # Internal node backend port (nginx proxies to it); leave as 1222
- SERVER_HOSTNAME=127.0.0.1 # Internal bind address for the node backend; leave as 127.0.0.1
- SERVER_SERVE_PUBLIC_DIR=false # Whether the node backend serves the SPA itself; 'false' (nginx serves it)
- DATABASE_URL=file:/app-data/db/db.sqlite # SQLite database URL (file:/app-data/db/db.sqlite)
- DOCUMENT_STORAGE_FILESYSTEM_ROOT=/app-data/documents # Filesystem path where uploaded documents are stored (under the /app-data volume)
- PAPRA_CONFIG_DIR=/app-data # Directory Papra reads its config from (under the /app-data volume)
- INGESTION_FOLDER_ROOT=/ingestion # Watched folder for drop-in document ingestion
- EMAILS_DRY_RUN=true # If 'true', emails are logged instead of sent (no SMTP configured by default)
- BETTER_AUTH_TELEMETRY=0 # better-auth telemetry; '0' disables it
- AUTH_SECRET=${PAPRA_AUTH_SECRET} # better-auth session signing secret, >=32 chars. Optional: if unset, the container generates a strong secret on first boot and persists it under /app-data. Set one you control with `openssl rand -hex 48` to manage it yourself.
- AUTH_IS_REGISTRATION_ENABLED=true # Set to false after creating your account to lock down signups
volumes:
- "/path/to/containers/papra/app-data:/app-data"
restart: unless-stopped
```

### AppJail Director

**.env**:

```
DIRECTOR_PROJECT=papra
PUID=1000
PGID=1000
TZ=Etc/UTC
NODE_ENV=production
PORT=1222
SERVER_HOSTNAME=127.0.0.1
SERVER_SERVE_PUBLIC_DIR=false
DATABASE_URL=file:/app-data/db/db.sqlite
DOCUMENT_STORAGE_FILESYSTEM_ROOT=/app-data/documents
PAPRA_CONFIG_DIR=/app-data
INGESTION_FOLDER_ROOT=/ingestion
EMAILS_DRY_RUN=true
BETTER_AUTH_TELEMETRY=0
AUTH_SECRET=${PAPRA_AUTH_SECRET}
AUTH_IS_REGISTRATION_ENABLED=true
```

**appjail-director.yml**:

```yaml
options:
- virtualnet: ': default'
- nat:
services:
papra:
name: papra
options:
- container: 'boot args:--pull'
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
- NODE_ENV: !ENV '${NODE_ENV}'
- PORT: !ENV '${PORT}'
- SERVER_HOSTNAME: !ENV '${SERVER_HOSTNAME}'
- SERVER_SERVE_PUBLIC_DIR: !ENV '${SERVER_SERVE_PUBLIC_DIR}'
- DATABASE_URL: !ENV '${DATABASE_URL}'
- DOCUMENT_STORAGE_FILESYSTEM_ROOT: !ENV '${DOCUMENT_STORAGE_FILESYSTEM_ROOT}'
- PAPRA_CONFIG_DIR: !ENV '${PAPRA_CONFIG_DIR}'
- INGESTION_FOLDER_ROOT: !ENV '${INGESTION_FOLDER_ROOT}'
- EMAILS_DRY_RUN: !ENV '${EMAILS_DRY_RUN}'
- BETTER_AUTH_TELEMETRY: !ENV '${BETTER_AUTH_TELEMETRY}'
- AUTH_SECRET: !ENV '${AUTH_SECRET}'
- AUTH_IS_REGISTRATION_ENABLED: !ENV '${AUTH_IS_REGISTRATION_ENABLED}'
volumes:
- papra_app-data: /app-data
volumes:
papra_app-data:
device: '/path/to/containers/papra/app-data'
```

**Makejail**:

```
ARG tag=latest

OPTION overwrite=force
OPTION from=ghcr.io/daemonless/papra:${tag}
```

### Podman CLI

```bash
podman run -d --name papra \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Etc/UTC \
-e NODE_ENV=production \
-e PORT=1222 \
-e SERVER_HOSTNAME=127.0.0.1 \
-e SERVER_SERVE_PUBLIC_DIR=false \
-e DATABASE_URL=file:/app-data/db/db.sqlite \
-e DOCUMENT_STORAGE_FILESYSTEM_ROOT=/app-data/documents \
-e PAPRA_CONFIG_DIR=/app-data \
-e INGESTION_FOLDER_ROOT=/ingestion \
-e EMAILS_DRY_RUN=true \
-e BETTER_AUTH_TELEMETRY=0 \
-e AUTH_SECRET=${PAPRA_AUTH_SECRET} \
-e AUTH_IS_REGISTRATION_ENABLED=true \
-v /path/to/containers/papra/app-data:/app-data \
ghcr.io/daemonless/papra:latest
```

### Ansible

```yaml
- name: Deploy papra
containers.podman.podman_container:
name: papra
image: "ghcr.io/daemonless/papra:latest"
state: started
restart_policy: always
env:
PUID: "1000"
PGID: "1000"
TZ: "Etc/UTC"
NODE_ENV: "production"
PORT: "1222"
SERVER_HOSTNAME: "127.0.0.1"
SERVER_SERVE_PUBLIC_DIR: "false"
DATABASE_URL: "file:/app-data/db/db.sqlite"
DOCUMENT_STORAGE_FILESYSTEM_ROOT: "/app-data/documents"
PAPRA_CONFIG_DIR: "/app-data"
INGESTION_FOLDER_ROOT: "/ingestion"
EMAILS_DRY_RUN: "true"
BETTER_AUTH_TELEMETRY: "0"
AUTH_SECRET: "${PAPRA_AUTH_SECRET}"
AUTH_IS_REGISTRATION_ENABLED: "true"
volumes:
- "/path/to/containers/papra/app-data:/app-data"
```

## Parameters

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `PUID` | `1000` | User ID for the application process |
| `PGID` | `1000` | Group ID for the application process |
| `TZ` | `Etc/UTC` | Timezone for the container |
| `NODE_ENV` | `production` | Node runtime mode; leave as 'production' |
| `PORT` | `1222` | Internal node backend port (nginx proxies to it); leave as 1222 |
| `SERVER_HOSTNAME` | `127.0.0.1` | Internal bind address for the node backend; leave as 127.0.0.1 |
| `SERVER_SERVE_PUBLIC_DIR` | `false` | Whether the node backend serves the SPA itself; 'false' (nginx serves it) |
| `DATABASE_URL` | `file:/app-data/db/db.sqlite` | SQLite database URL (file:/app-data/db/db.sqlite) |
| `DOCUMENT_STORAGE_FILESYSTEM_ROOT` | `/app-data/documents` | Filesystem path where uploaded documents are stored (under the /app-data volume) |
| `PAPRA_CONFIG_DIR` | `/app-data` | Directory Papra reads its config from (under the /app-data volume) |
| `INGESTION_FOLDER_ROOT` | `/ingestion` | Watched folder for drop-in document ingestion |
| `EMAILS_DRY_RUN` | `true` | If 'true', emails are logged instead of sent (no SMTP configured by default) |
| `BETTER_AUTH_TELEMETRY` | `0` | better-auth telemetry; '0' disables it |
| `AUTH_SECRET` | `${PAPRA_AUTH_SECRET}` | better-auth session signing secret, >=32 chars. Optional: if unset, the container generates a strong secret on first boot and persists it under /app-data. Set one you control with `openssl rand -hex 48` to manage it yourself. |
| `AUTH_IS_REGISTRATION_ENABLED` | `true` | Set to false after creating your account to lock down signups |

### Volumes

| Path | Description |
|------|-------------|
| `/app-data` | Application data — SQLite database, stored documents, and config |

**Architectures:** amd64
**User:** `bsd` (UID/GID via PUID/PGID, defaults to 1000:1000)
**Base:** FreeBSD 15.0

---

Need help? Join our [Discord](https://discord.gg/Kb9tkhecZT) community.