Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ LABEL org.opencontainers.image.title="Papra" \
org.opencontainers.image.authors="daemonless" \
io.daemonless.category="Productivity" \
io.daemonless.port="1221" \
io.daemonless.volumes="/app-data" \
io.daemonless.volumes="/app_data" \
io.daemonless.arch="${FREEBSD_ARCH}" \
io.daemonless.upstream-url="${UPSTREAM_URL}" \
io.daemonless.upstream-jq="${UPSTREAM_JQ}" \
Expand All @@ -205,7 +205,7 @@ LABEL org.opencontainers.image.title="Papra" \
RUN sed -i '' -e 's,/quarterly,/latest,' /etc/pkg/FreeBSD.conf && \
pkg update && \
pkg install -y ${PACKAGES} && \
mkdir -p /app /app/public /app-data/db /app-data/documents /ingestion \
mkdir -p /app /app/public /app_data/db /app_data/documents /ingestion \
/var/log/nginx /var/run /var/tmp/nginx && \
pkg clean -ay && \
rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
Expand All @@ -217,7 +217,7 @@ COPY --from=builder --chown=bsd:bsd /build/apps/papra-client/dist /app/public

# Ownership — /app and /app/public must be world-readable so the nginx worker
# (www user) can serve the static client files.
RUN chown -R bsd:bsd /app /app-data /ingestion && \
RUN chown -R bsd:bsd /app /app_data /ingestion && \
chmod o+x /app && \
chmod -R o+rX /app/public

Expand All @@ -236,14 +236,14 @@ ENV 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 \
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 \
NODE_OPTIONS="--max-old-space-size=512"

EXPOSE 1221

VOLUME /app-data
VOLUME /app_data
14 changes: 7 additions & 7 deletions Containerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ LABEL org.opencontainers.image.title="Papra" \
org.opencontainers.image.authors="daemonless" \
io.daemonless.category="Productivity" \
io.daemonless.port="1221" \
io.daemonless.volumes="/app-data" \
io.daemonless.volumes="/app_data" \
io.daemonless.arch="${FREEBSD_ARCH}" \
io.daemonless.upstream-url="${UPSTREAM_URL}" \
io.daemonless.upstream-jq="${UPSTREAM_JQ}" \
Expand All @@ -199,7 +199,7 @@ LABEL org.opencontainers.image.title="Papra" \
RUN sed -i '' -e 's,/quarterly,/latest,' /etc/pkg/FreeBSD.conf && \
pkg update && \
pkg install -y ${PACKAGES} && \
mkdir -p /app /app/public /app-data/db /app-data/documents /ingestion \
mkdir -p /app /app/public /app_data/db /app_data/documents /ingestion \
/var/log/nginx /var/run /var/tmp/nginx && \
pkg clean -ay && \
rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
Expand All @@ -211,7 +211,7 @@ COPY --from=builder --chown=bsd:bsd /build/apps/papra-client/dist /app/public

# Ownership — /app and /app/public must be world-readable so the nginx worker
# (www user) can serve the static client files.
RUN chown -R bsd:bsd /app /app-data /ingestion && \
RUN chown -R bsd:bsd /app /app_data /ingestion && \
chmod o+x /app && \
chmod -R o+rX /app/public

Expand All @@ -230,14 +230,14 @@ ENV 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 \
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 \
NODE_OPTIONS="--max-old-space-size=512"

EXPOSE 1221

VOLUME /app-data
VOLUME /app_data
48 changes: 24 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ services:
- 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)
- 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_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"
- "/path/to/containers/papra/app_data:/app_data"
restart: unless-stopped
```

Expand All @@ -69,9 +69,9 @@ 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
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
Expand Down Expand Up @@ -109,10 +109,10 @@ services:
- AUTH_SECRET: !ENV '${AUTH_SECRET}'
- AUTH_IS_REGISTRATION_ENABLED: !ENV '${AUTH_IS_REGISTRATION_ENABLED}'
volumes:
- papra_app-data: /app-data
- papra_app_data: /app_data
volumes:
papra_app-data:
device: '/path/to/containers/papra/app-data'
papra_app_data:
device: '/path/to/containers/papra/app_data'
```

**Makejail**:
Expand All @@ -135,15 +135,15 @@ podman run -d --name papra \
-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 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 \
-v /path/to/containers/papra/app_data:/app_data \
ghcr.io/daemonless/papra:latest
```

Expand All @@ -164,16 +164,16 @@ podman run -d --name papra \
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"
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"
- "/path/to/containers/papra/app_data:/app_data"
```

## Parameters
Expand All @@ -189,20 +189,20 @@ podman run -d --name papra \
| `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) |
| `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_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 |
| `/app_data` | Application data — SQLite database, stored documents, and config |

**Architectures:** amd64
**User:** `bsd` (UID/GID via PUID/PGID, defaults to 1000:1000)
Expand Down
20 changes: 10 additions & 10 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ x-daemonless:
PUID: "User ID for the application process"
PGID: "Group ID for the application process"
TZ: "Timezone for the container"
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."
DATABASE_URL: "SQLite database URL (file:/app-data/db/db.sqlite)"
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."
DATABASE_URL: "SQLite database URL (file:/app_data/db/db.sqlite)"
AUTH_IS_REGISTRATION_ENABLED: "Set to false after creating your account to lock down signups"
NODE_ENV: "Node runtime mode; leave as 'production'"
PORT: "Internal node backend port (nginx proxies to it); leave as 1222"
SERVER_HOSTNAME: "Internal bind address for the node backend; leave as 127.0.0.1"
SERVER_SERVE_PUBLIC_DIR: "Whether the node backend serves the SPA itself; 'false' (nginx serves it)"
DOCUMENT_STORAGE_FILESYSTEM_ROOT: "Filesystem path where uploaded documents are stored (under the /app-data volume)"
PAPRA_CONFIG_DIR: "Directory Papra reads its config from (under the /app-data volume)"
DOCUMENT_STORAGE_FILESYSTEM_ROOT: "Filesystem path where uploaded documents are stored (under the /app_data volume)"
PAPRA_CONFIG_DIR: "Directory Papra reads its config from (under the /app_data volume)"
INGESTION_FOLDER_ROOT: "Watched folder for drop-in document ingestion"
EMAILS_DRY_RUN: "If 'true', emails are logged instead of sent (no SMTP configured by default)"
BETTER_AUTH_TELEMETRY: "better-auth telemetry; '0' disables it"
volumes:
/app-data: "Application data — SQLite database, stored documents, and config"
/app_data: "Application data — SQLite database, stored documents, and config"
ports:
1221: "Web UI (nginx serving the SPA + reverse-proxying /api/ to the node backend)"

Expand All @@ -50,17 +50,17 @@ services:
- 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
- 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
# Optional: a secret you control (>=32 chars). If omitted, the container
# generates one on first boot and persists it under /app-data (secure-by-default).
# generates one on first boot and persists it under /app_data (secure-by-default).
- AUTH_SECRET=${PAPRA_AUTH_SECRET}
# Lock down registration after you create the first (admin) account.
- AUTH_IS_REGISTRATION_ENABLED=true

volumes:
- ./app-data:/app-data
- ./app_data:/app_data
4 changes: 2 additions & 2 deletions root/etc/cont-init.d/20-papra
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh
echo "[init] Initializing Papra..."

mkdir -p /app-data/db /app-data/documents /ingestion
chown -R bsd:bsd /app-data /ingestion
mkdir -p /app_data/db /app_data/documents /ingestion
chown -R bsd:bsd /app_data /ingestion

echo "[init] Done"
4 changes: 2 additions & 2 deletions root/etc/services.d/papra-server/run
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ NODE_FLAGS="--import temporal-polyfill/global"
# Otherwise generate a strong random secret ONCE and persist it under the data volume
# so sessions survive restarts — keeps the image secure-by-default and bootable
# standalone (no hardcoded secret shipped; also lets CIT run the bare image).
SECRET_FILE=/app-data/.auth_secret
SECRET_FILE=/app_data/.auth_secret
if [ -z "${AUTH_SECRET:-}" ]; then
if [ ! -s "$SECRET_FILE" ]; then
mkdir -p /app-data
mkdir -p /app_data
$NODE -e 'process.stdout.write(require("crypto").randomBytes(48).toString("hex"))' > "$SECRET_FILE"
chmod 600 "$SECRET_FILE"
chown bsd:bsd "$SECRET_FILE" 2>/dev/null || true
Expand Down
Loading