Skip to content

daemonless/papra

Repository files navigation

Papra

Build Status Last Commit

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

Registry ghcr.io/daemonless/papra
Source https://github.com/papra-hq/papra
Website 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 for host setup instructions.

Deployment

Podman Compose

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:

options:
  - virtualnet: ':<random> 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

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

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

About

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

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors