Skip to content
daemonlessPublic

About

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

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

Papra

Build Status Last Commit OCI Pulls

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 — recommended.

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:
      - "/containers/papra/app_data:/app_data"
    # always (not unless-stopped) so FreeBSD's podman rc.d auto-starts it at boot
    restart: always

Save as compose.yaml, then run podman-compose up -d.

AppJail Director

.env:

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

# appjail-director.yml

options:
  - virtualnet: ':<random> default'
  - nat:
services:
  papra:
    name: papra
    options:
      - container: '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: '/containers/papra/app_data'

Makejail:

# Makejail

ARG tag=latest

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

Save the files above, then run appjail-director up.

Bastille

Warning

Bastille's OCI support is experimental. It requires buildah and shares the host network stack (inherit). Mount volumes with --volume HOST JAIL; without it, image-declared volumes are stored under ${bastille_volumesdir}/${jail}.

services:
  papra:
    name: papra
    image: "ghcr.io/daemonless/papra:latest"
    network:
      - mode: host
    environment:
      - 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:
      - "/containers/papra/app_data:/app_data"

Save as bastille-compose.yml, then run bastille up.

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


Need help? Join our Discord community.

About

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

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages