mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-09-21 17:08:33 +00:00
* Preprocesses classifier content with Tantivy instead of NLTK Tokenizing and stemming now happen in one Rust call instead of NLTK's Python tokenizer and per word stemming, which also removes the Redis backed stem cache from every preprocessing call. The output matches the NLTK pipeline closely; tokens containing digits are now stemmed, and the English stop words follow Snowball's list. Stemming and stop word removal apply whenever the OCR language is one of the supported classifier languages, so PAPERLESS_ENABLE_NLTK and PAPERLESS_NLTK_DIR are removed. * Copies packages instead of hardlinking them in backend CI, some NLTK thing * Adds a normalization to NFC to better fit what Tantivy expects
256 lines
9.2 KiB
Docker
256 lines
9.2 KiB
Docker
# syntax=docker/dockerfile:1
|
|
# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md
|
|
|
|
# Stage: compile-frontend
|
|
# Purpose: Compiles the frontend
|
|
# Notes:
|
|
# - Does PNPM stuff with Typescript and such
|
|
FROM --platform=$BUILDPLATFORM docker.io/node:24-trixie-slim AS compile-frontend
|
|
|
|
COPY ./src-ui /src/src-ui
|
|
|
|
WORKDIR /src/src-ui
|
|
RUN set -eux \
|
|
&& corepack enable \
|
|
&& pnpm install
|
|
|
|
ARG PNGX_TAG_VERSION=
|
|
# Add the tag to the environment file if its a tagged dev build
|
|
RUN set -eux && \
|
|
case "${PNGX_TAG_VERSION}" in \
|
|
dev|beta|fix*|feature*) \
|
|
sed -i -E "s/tag: '([a-z\.]+)'/tag: '${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
|
|
;; \
|
|
esac
|
|
|
|
RUN set -eux \
|
|
&& ./node_modules/.bin/ng build --configuration production
|
|
|
|
# Stage: s6-overlay-base
|
|
# Purpose: Installs s6-overlay and rootfs
|
|
# Comments:
|
|
# - Don't leave anything extra in here either
|
|
FROM ghcr.io/astral-sh/uv:0.12.9-python3.14-trixie-slim AS s6-overlay-base
|
|
|
|
WORKDIR /usr/src/s6
|
|
|
|
# https://github.com/just-containers/s6-overlay#customizing-s6-overlay-behaviour
|
|
ENV \
|
|
S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \
|
|
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
|
|
S6_VERBOSITY=1 \
|
|
PATH=/command:$PATH
|
|
|
|
# Buildx provided, must be defined to use though
|
|
ARG TARGETARCH
|
|
ARG TARGETVARIANT
|
|
# Lock this version
|
|
ARG S6_OVERLAY_VERSION=3.2.2.0
|
|
|
|
ARG S6_BUILD_TIME_PKGS="curl \
|
|
xz-utils"
|
|
|
|
RUN set -eux \
|
|
&& echo "Installing build time packages" \
|
|
&& apt-get update \
|
|
&& apt-get install --yes --quiet --no-install-recommends ${S6_BUILD_TIME_PKGS} \
|
|
&& echo "Determining arch" \
|
|
&& S6_ARCH="" \
|
|
&& if [ "${TARGETARCH}${TARGETVARIANT}" = "amd64" ]; then S6_ARCH="x86_64"; \
|
|
elif [ "${TARGETARCH}${TARGETVARIANT}" = "arm64" ]; then S6_ARCH="aarch64"; fi\
|
|
&& if [ -z "${S6_ARCH}" ]; then { echo "Error: Not able to determine arch"; exit 1; }; fi \
|
|
&& echo "Installing s6-overlay for ${S6_ARCH}" \
|
|
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \
|
|
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" \
|
|
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz.sha256" \
|
|
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz" \
|
|
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz.sha256" \
|
|
&& echo "Validating s6-archive checksums" \
|
|
&& sha256sum --check ./*.sha256 \
|
|
&& echo "Unpacking archives" \
|
|
&& tar --directory / -Jxpf s6-overlay-noarch.tar.xz \
|
|
&& tar --directory / -Jxpf s6-overlay-${S6_ARCH}.tar.xz \
|
|
&& echo "Removing downloaded archives" \
|
|
&& rm ./*.tar.xz \
|
|
&& rm ./*.sha256 \
|
|
&& echo "Cleaning up image" \
|
|
&& apt-get --yes purge ${S6_BUILD_TIME_PKGS} \
|
|
&& apt-get --yes autoremove --purge \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Copy our service defs and filesystem
|
|
COPY ./docker/rootfs /
|
|
|
|
# Stage: main-app
|
|
# Purpose: The final image
|
|
# Comments:
|
|
# - Don't leave anything extra in here
|
|
FROM s6-overlay-base AS main-app
|
|
|
|
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
|
|
LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/"
|
|
LABEL org.opencontainers.image.source="https://github.com/paperless-ngx/paperless-ngx"
|
|
LABEL org.opencontainers.image.url="https://github.com/paperless-ngx/paperless-ngx"
|
|
LABEL org.opencontainers.image.licenses="GPL-3.0-only"
|
|
|
|
ARG DEBIAN_FRONTEND=noninteractive
|
|
|
|
# Buildx provided, must be defined to use though
|
|
ARG TARGETARCH
|
|
|
|
# Can be workflow provided, defaults set for manual building
|
|
ARG JBIG2ENC_VERSION=0.30
|
|
|
|
# Set Python environment variables
|
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
PYTHONUNBUFFERED=1 \
|
|
# Ignore warning from Whitenoise about async iterators
|
|
PYTHONWARNINGS="ignore:::django.http.response:517" \
|
|
PNGX_CONTAINERIZED=1 \
|
|
# https://docs.astral.sh/uv/reference/settings/#link-mode
|
|
UV_LINK_MODE=copy
|
|
|
|
#
|
|
# Begin installation and configuration
|
|
# Order the steps below from least often changed to most
|
|
#
|
|
|
|
# Packages need for running
|
|
ARG RUNTIME_PACKAGES="\
|
|
# General utils
|
|
curl \
|
|
# Docker specific
|
|
gosu \
|
|
# Timezones support
|
|
tzdata \
|
|
# fonts for text file thumbnail generation
|
|
fonts-liberation \
|
|
gettext \
|
|
ghostscript \
|
|
gnupg \
|
|
icc-profiles-free \
|
|
imagemagick \
|
|
# PostgreSQL
|
|
postgresql-client \
|
|
# MySQL / MariaDB
|
|
mariadb-client \
|
|
# OCRmyPDF dependencies
|
|
tesseract-ocr \
|
|
tesseract-ocr-eng \
|
|
tesseract-ocr-deu \
|
|
tesseract-ocr-fra \
|
|
tesseract-ocr-ita \
|
|
tesseract-ocr-spa \
|
|
unpaper \
|
|
pngquant \
|
|
jbig2dec \
|
|
# lxml
|
|
libxml2 \
|
|
libxslt1.1 \
|
|
# itself
|
|
qpdf \
|
|
# Mime type detection
|
|
file \
|
|
libmagic1 \
|
|
media-types \
|
|
zlib1g \
|
|
poppler-utils"
|
|
|
|
# Install basic runtime packages.
|
|
# These change very infrequently
|
|
RUN set -eux \
|
|
echo "Installing system packages" \
|
|
&& apt-get update \
|
|
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} \
|
|
&& echo "Installing pre-built updates" \
|
|
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all \
|
|
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-trixie-v${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
|
&& echo "Installing jbig2enc" \
|
|
&& dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
|
&& echo "Configuring imagemagick" \
|
|
&& cp /etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml \
|
|
&& echo "Cleaning up image layer" \
|
|
&& rm --force --verbose *.deb \
|
|
&& rm --recursive --force --verbose /var/lib/apt/lists/*
|
|
|
|
WORKDIR /usr/src/paperless/src/
|
|
|
|
# Python dependencies
|
|
# Change pretty frequently
|
|
COPY --chown=1000:1000 ["pyproject.toml", "uv.lock", "/usr/src/paperless/src/"]
|
|
|
|
# Packages needed only for building a few quick Python
|
|
# dependencies
|
|
ARG BUILD_PACKAGES="\
|
|
build-essential \
|
|
# https://github.com/PyMySQL/mysqlclient#linux
|
|
default-libmysqlclient-dev \
|
|
pkg-config"
|
|
|
|
# hadolint ignore=DL3042
|
|
RUN set -eux \
|
|
&& echo "Installing build system packages" \
|
|
&& apt-get update \
|
|
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
|
&& echo "Installing Python requirements" \
|
|
&& uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt \
|
|
&& uv pip install --no-cache --system --no-python-downloads --python-preference system \
|
|
--index https://pypi.org/simple \
|
|
--index https://download.pytorch.org/whl/cpu \
|
|
--index-strategy unsafe-best-match \
|
|
--requirements requirements.txt \
|
|
&& echo "Cleaning up image" \
|
|
&& apt-get --yes purge ${BUILD_PACKAGES} \
|
|
&& apt-get --yes autoremove --purge \
|
|
&& apt-get clean --yes \
|
|
&& rm --recursive --force --verbose *.whl \
|
|
&& rm --recursive --force --verbose /var/lib/apt/lists/* \
|
|
&& rm --recursive --force --verbose /tmp/* \
|
|
&& rm --recursive --force --verbose /var/tmp/* \
|
|
&& rm --recursive --force --verbose /var/cache/apt/archives/* \
|
|
&& truncate --size 0 /var/log/*log
|
|
|
|
# copy backend
|
|
COPY --chown=1000:1000 ./src ./
|
|
|
|
# copy frontend
|
|
COPY --from=compile-frontend --chown=1000:1000 /src/src/documents/static/frontend/ ./documents/static/frontend/
|
|
|
|
# add users, setup scripts
|
|
# Mount the compiled frontend to expected location
|
|
RUN set -eux \
|
|
&& sed -i '1s|^#!/usr/bin/env python3|#!/command/with-contenv python3|' manage.py \
|
|
&& echo "Setting up user/group" \
|
|
&& addgroup --gid 1000 paperless \
|
|
&& useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \
|
|
&& echo "Creating volume directories" \
|
|
&& mkdir --parents --verbose /usr/src/paperless/data \
|
|
&& mkdir --parents --verbose /usr/src/paperless/media \
|
|
&& mkdir --parents --verbose /usr/src/paperless/consume \
|
|
&& mkdir --parents --verbose /usr/src/paperless/export \
|
|
&& echo "Creating gnupg directory" \
|
|
&& mkdir -m700 --verbose /usr/src/paperless/.gnupg \
|
|
&& echo "Adjusting all permissions" \
|
|
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \
|
|
&& echo "Making fontconfig cache writable for arbitrary container UIDs" \
|
|
&& chmod 1777 /var/cache/fontconfig \
|
|
&& echo "Making /run world-writable for rootless operation" \
|
|
&& chmod 1777 /run \
|
|
&& echo "Removing setuid from s6-overlay-suexec for rootless compat" \
|
|
&& chmod u-s /command/s6-overlay-suexec \
|
|
&& echo "Collecting static files" \
|
|
&& PAPERLESS_SECRET_KEY=build-time-dummy s6-setuidgid paperless python3 manage.py collectstatic --clear --no-input --link \
|
|
&& PAPERLESS_SECRET_KEY=build-time-dummy s6-setuidgid paperless python3 manage.py compilemessages \
|
|
&& /usr/local/bin/deduplicate.py --verbose /usr/src/paperless/static/
|
|
|
|
VOLUME ["/usr/src/paperless/data", \
|
|
"/usr/src/paperless/media", \
|
|
"/usr/src/paperless/consume", \
|
|
"/usr/src/paperless/export"]
|
|
|
|
ENTRYPOINT ["/init"]
|
|
|
|
EXPOSE 8000
|
|
|
|
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "-L", "--max-time", "2", "http://localhost:8000" ]
|