]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Transitions the Docker image to use s6 and s6-overlay for process supervision instead...
authorTrenton H <797416+stumpylog@users.noreply.github.com>
Fri, 7 Feb 2025 19:25:54 +0000 (11:25 -0800)
committerGitHub <noreply@github.com>
Fri, 7 Feb 2025 19:25:54 +0000 (11:25 -0800)
127 files changed:
.dockerignore
.github/workflows/ci.yml
.ruff.toml
Dockerfile
docker/docker-prepare.sh [deleted file]
docker/env-from-file.sh [deleted file]
docker/flower-conditional.sh [deleted file]
docker/init-flow.drawio.png [new file with mode: 0644]
docker/install_management_commands.sh
docker/management_script.sh
docker/paperless_cmd.sh [deleted file]
docker/rootfs/etc/ImageMagick-6/paperless-policy.xml [moved from docker/imagemagick-policy.xml with 100% similarity]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-custom-init [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-env-file [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-folders [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-migrations [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-modify-user [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-search-index [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-start [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-superuser [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-system-checks [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-tesseract-langs [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-db [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-redis [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-search-index [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-system-checks [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-tesseract-langs [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-db [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-redis [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/dependencies.d/init-start [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/dependencies.d/init-modify-user [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-folders [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-wait-for-db [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/dependencies.d/init-env-file [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/dependencies.d/init-migrations [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/dependencies.d/base [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/dependencies.d/init-migrations [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-superuser [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-tesseract-langs [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/dependencies.d/init-env-file [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/dependencies.d/init-env-file [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/dependencies.d/init-env-file [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/up [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/dependencies.d/init-complete [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/init-complete [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-scheduler [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-worker [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/init-complete [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/svc-worker [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/init-complete [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-consumer [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-scheduler [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-worker [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/dependencies.d/init-complete [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/run [new file with mode: 0755]
docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/type [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/init-complete [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-consumer [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-flower [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-scheduler [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-webserver [new file with mode: 0644]
docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-worker [new file with mode: 0644]
docker/rootfs/usr/local/bin/convert_mariadb_uuid [new file with mode: 0755]
docker/rootfs/usr/local/bin/decrypt_documents [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_archiver [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_create_classifier [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_exporter [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_fuzzy_match [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_importer [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_index [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_renamer [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_retagger [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_sanity_checker [new file with mode: 0755]
docker/rootfs/usr/local/bin/document_thumbnails [new file with mode: 0755]
docker/rootfs/usr/local/bin/mail_fetcher [new file with mode: 0755]
docker/rootfs/usr/local/bin/manage_superuser [new file with mode: 0755]
docker/rootfs/usr/local/bin/prune_audit_logs [new file with mode: 0755]
docker/rootfs/usr/local/bin/wait-for-redis.py [new file with mode: 0755]
docker/supervisord.conf [deleted file]
docker/wait-for-redis.py [deleted file]
docs/configuration.md

index c33ee64525e6728521876c12e4866a354a12b3af..8c39dd615c1098f6668ffcc3c54d66d92387fed1 100644 (file)
@@ -26,3 +26,5 @@
 ./dist
 ./scripts
 ./resources
+# Other stuff
+**/*.drawio.png
index d00a8cb43349aee850f0823b394acae3a4b8e1b6..5e090a227a7740631dbc09569b8f7c198f3eccb1 100644 (file)
@@ -131,7 +131,7 @@ jobs:
       -
         name: Configure ImageMagick
         run: |
-          sudo cp docker/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
+          sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml
       -
         name: Install Python dependencies
         run: |
index a29b471c527587fb2f9bcb1dd6782ae441772118..ae1bed609019daf01a328a765026a3230b26cd71 100644 (file)
@@ -76,8 +76,12 @@ ignore = ["DJ001", "SIM105", "RUF012"]
 "src/paperless_tesseract/tests/test_parser.py" = ["RUF001", "PTH"]  # TODO PTH Enable & remove
 "src/paperless_tika/tests/test_live_tika.py" = ["PTH"]  # TODO Enable & remove
 "src/paperless_tika/tests/test_tika_parser.py" = ["PTH"]  # TODO Enable & remove
+# Testing
 "*/tests/*.py" = ["E501", "SIM117"]
+# Migrations
 "*/migrations/*.py" = ["E501", "SIM", "T201"]
+# Docker specific
+"docker/rootfs/usr/local/bin/wait-for-redis.py" = ["INP001", "T201"]
 
 [lint.isort]
 force-single-line = true
index 3eab1dc2f4f078ee30626361e21015a1785fa471..6a575be7470af10fd5463e1c1cb31b35c99cf059 100644 (file)
@@ -43,11 +43,66 @@ RUN set -eux \
   && echo "Generating requirement.txt" \
     && pipenv requirements > requirements.txt
 
+# Stage: s6-overlay-base
+# Purpose: Installs s6-overlay and rootfs
+# Comments:
+#  - Don't leave anything extra in here either
+FROM docker.io/python:3.12-slim-bookworm 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.0.2
+
+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 docker.io/python:3.12-slim-bookworm AS main-app
+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/"
@@ -143,64 +198,23 @@ RUN set -eux \
         && dpkg --install ./ghostscript_${GS_VERSION}.dfsg-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/* \
-  && echo "Installing supervisor" \
-    && python3 -m pip install --default-timeout=1000 --upgrade --no-cache-dir supervisor==4.2.5
+    && rm --recursive --force --verbose /var/lib/apt/lists/*
 
 # Copy gunicorn config
 # Changes very infrequently
 WORKDIR /usr/src/paperless/
 
-COPY gunicorn.conf.py .
-
-# setup docker-specific things
-# These change sometimes, but rarely
-WORKDIR /usr/src/paperless/src/docker/
-
-COPY [ \
-  "docker/imagemagick-policy.xml", \
-  "docker/supervisord.conf", \
-  "docker/docker-entrypoint.sh", \
-  "docker/docker-prepare.sh", \
-  "docker/paperless_cmd.sh", \
-  "docker/wait-for-redis.py", \
-  "docker/env-from-file.sh", \
-  "docker/management_script.sh", \
-  "docker/flower-conditional.sh", \
-  "docker/install_management_commands.sh", \
-  "/usr/src/paperless/src/docker/" \
-]
-
-RUN set -eux \
-  && echo "Configuring ImageMagick" \
-    && mv imagemagick-policy.xml /etc/ImageMagick-6/policy.xml \
-  && echo "Configuring supervisord" \
-    && mkdir /var/log/supervisord /var/run/supervisord \
-    && mv supervisord.conf /etc/supervisord.conf \
-  && echo "Setting up Docker scripts" \
-    && mv docker-entrypoint.sh /sbin/docker-entrypoint.sh \
-    && chmod 755 /sbin/docker-entrypoint.sh \
-    && mv docker-prepare.sh /sbin/docker-prepare.sh \
-    && chmod 755 /sbin/docker-prepare.sh \
-    && mv wait-for-redis.py /sbin/wait-for-redis.py \
-    && chmod 755 /sbin/wait-for-redis.py \
-    && mv env-from-file.sh /sbin/env-from-file.sh \
-    && chmod 755 /sbin/env-from-file.sh \
-    && mv paperless_cmd.sh /usr/local/bin/paperless_cmd.sh \
-    && chmod 755 /usr/local/bin/paperless_cmd.sh \
-    && mv flower-conditional.sh /usr/local/bin/flower-conditional.sh \
-    && chmod 755 /usr/local/bin/flower-conditional.sh \
-  && echo "Installing management commands" \
-    && chmod +x install_management_commands.sh \
-    && ./install_management_commands.sh
+COPY --chown=1000:1000 gunicorn.conf.py /usr/src/paperless/gunicorn.conf.py
 
 WORKDIR /usr/src/paperless/src/
 
 # Python dependencies
 # Change pretty frequently
-COPY --from=pipenv-base /usr/src/pipenv/requirements.txt ./
+COPY --chown=1000:1000 --from=pipenv-base /usr/src/pipenv/requirements.txt ./
 
 # Packages needed only for building a few quick Python
 # dependencies
@@ -222,7 +236,7 @@ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
   && echo "Installing build system packages" \
     && apt-get update \
     && apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
-    && python3 -m pip install --no-cache-dir --upgrade wheel \
+    && python3 -m pip install --upgrade wheel \
   && echo "Installing Python requirements" \
     && curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \
       https://github.com/paperless-ngx/builder/releases/download/psycopg-${PSYCOPG_VERSION}/psycopg_c-${PSYCOPG_VERSION}-cp312-cp312-linux_x86_64.whl \
@@ -267,18 +281,16 @@ RUN set -eux \
   && echo "Adjusting all permissions" \
     && chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \
   && echo "Collecting static files" \
-    && gosu paperless python3 manage.py collectstatic --clear --no-input --link \
-    && gosu paperless python3 manage.py compilemessages
+    && s6-setuidgid paperless python3 manage.py collectstatic --clear --no-input --link \
+    && s6-setuidgid paperless python3 manage.py compilemessages
 
 VOLUME ["/usr/src/paperless/data", \
         "/usr/src/paperless/media", \
         "/usr/src/paperless/consume", \
         "/usr/src/paperless/export"]
 
-ENTRYPOINT ["/sbin/docker-entrypoint.sh"]
+ENTRYPOINT ["/init"]
 
 EXPOSE 8000
 
-CMD ["/usr/local/bin/paperless_cmd.sh"]
-
 HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ]
diff --git a/docker/docker-prepare.sh b/docker/docker-prepare.sh
deleted file mode 100755 (executable)
index e3d9245..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-wait_for_postgres() {
-       local attempt_num=1
-       local -r max_attempts=5
-
-       echo "Waiting for PostgreSQL to start..."
-
-       local -r host="${PAPERLESS_DBHOST:-localhost}"
-       local -r port="${PAPERLESS_DBPORT:-5432}"
-
-       # Disable warning, host and port can't have spaces
-       # shellcheck disable=SC2086
-       while [ ! "$(pg_isready --host ${host} --port ${port})" ]; do
-
-               if [ $attempt_num -eq $max_attempts ]; then
-                       echo "Unable to connect to database."
-                       exit 1
-               else
-                       echo "Attempt $attempt_num failed! Trying again in 5 seconds..."
-               fi
-
-               attempt_num=$(("$attempt_num" + 1))
-               sleep 5
-       done
-       echo "Connected to PostgreSQL"
-}
-
-wait_for_mariadb() {
-       echo "Waiting for MariaDB to start..."
-
-       local -r host="${PAPERLESS_DBHOST:=localhost}"
-       local -r port="${PAPERLESS_DBPORT:=3306}"
-
-       local attempt_num=1
-       local -r max_attempts=5
-
-       # Disable warning, host and port can't have spaces
-       # shellcheck disable=SC2086
-       while ! true > /dev/tcp/$host/$port; do
-
-               if [ $attempt_num -eq $max_attempts ]; then
-                       echo "Unable to connect to database."
-                       exit 1
-               else
-                       echo "Attempt $attempt_num failed! Trying again in 5 seconds..."
-
-               fi
-
-               attempt_num=$(("$attempt_num" + 1))
-               sleep 5
-       done
-       echo "Connected to MariaDB"
-}
-
-wait_for_redis() {
-       # We use a Python script to send the Redis ping
-       # instead of installing redis-tools just for 1 thing
-       if ! python3 /sbin/wait-for-redis.py; then
-               exit 1
-       fi
-}
-
-migrations() {
-       (
-               # flock is in place to prevent multiple containers from doing migrations
-               # simultaneously. This also ensures that the db is ready when the command
-               # of the current container starts.
-               flock 200
-               echo "Apply database migrations..."
-               python3 manage.py migrate --skip-checks --no-input
-       ) 200>"${DATA_DIR}/migration_lock"
-}
-
-django_checks() {
-       # Explicitly run the Django system checks
-       echo "Running Django checks"
-       python3 manage.py check
-}
-
-search_index() {
-
-       local -r index_version=9
-       local -r index_version_file=${DATA_DIR}/.index_version
-
-       if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then
-               echo "Search index out of date. Updating..."
-               python3 manage.py document_index reindex --no-progress-bar
-               echo ${index_version} | tee "${index_version_file}" >/dev/null
-       fi
-}
-
-superuser() {
-       if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then
-               python3 manage.py manage_superuser
-       fi
-}
-
-do_work() {
-       if [[ "${PAPERLESS_DBENGINE}" == "mariadb" ]]; then
-               wait_for_mariadb
-       elif [[ -n "${PAPERLESS_DBHOST}" ]]; then
-               wait_for_postgres
-       fi
-
-       wait_for_redis
-
-       migrations
-
-       django_checks
-
-       search_index
-
-       superuser
-
-}
-
-do_work
diff --git a/docker/env-from-file.sh b/docker/env-from-file.sh
deleted file mode 100644 (file)
index 41f51d0..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-
-# Scans the environment variables for those with the suffix _FILE
-# When located, checks the file exists, and exports the contents
-# of the file as the same name, minus the suffix
-# This allows the use of Docker secrets or mounted files
-# to fill in any of the settings configurable via environment
-# variables
-
-set -eu
-
-for line in $(printenv)
-do
-       # Extract the name of the environment variable
-       env_name=${line%%=*}
-       # Check if it starts with "PAPERLESS_" and ends in "_FILE"
-       if [[ ${env_name} == PAPERLESS_*_FILE ]]; then
-               # This should have been named different..
-               if [[ ${env_name} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${env_name} == "PAPERLESS_MODEL_FILE" ]]; then
-                       continue
-               fi
-               # Extract the value of the environment
-               env_value=${line#*=}
-
-               # Check the file exists
-               if [[ -f ${env_value} ]]; then
-
-                       # Trim off the _FILE suffix
-                       non_file_env_name=${env_name%"_FILE"}
-                       echo "Setting ${non_file_env_name} from file"
-
-                       # Reads the value from th file
-                       val="$(< "${!env_name}")"
-
-                       # Sets the normal name to the read file contents
-                       export "${non_file_env_name}"="${val}"
-
-               else
-                       echo "File ${env_value} referenced by ${env_name} doesn't exist"
-               fi
-       fi
-done
diff --git a/docker/flower-conditional.sh b/docker/flower-conditional.sh
deleted file mode 100644 (file)
index f8719e0..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-echo "Checking if we should start flower..."
-
-if [[ -n  "${PAPERLESS_ENABLE_FLOWER}" ]]; then
-       # Small delay to allow celery to be up first
-       echo "Starting flower in 5s"
-       sleep 5
-       celery --app paperless flower --conf=/usr/src/paperless/src/paperless/flowerconfig.py
-else
-       echo "Not starting flower"
-fi
diff --git a/docker/init-flow.drawio.png b/docker/init-flow.drawio.png
new file mode 100644 (file)
index 0000000..2e19ac1
Binary files /dev/null and b/docker/init-flow.drawio.png differ
index 37c17058ae3749505027470896705f31095f9908..c7c65bfbb9fa123c726e138a3bafaf2f4ad62fb7 100755 (executable)
@@ -1,5 +1,7 @@
 #!/usr/bin/env bash
 
+# Run this script to generate the management commands again (for example if a new command is create or the template is updated)
+
 set -eu
 
 for command in decrypt_documents \
@@ -19,6 +21,6 @@ for command in decrypt_documents \
        prune_audit_logs;
 do
        echo "installing $command..."
-       sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command
-       chmod +x /usr/local/bin/$command
+       sed "s/management_command/$command/g" management_script.sh >"$PWD/rootfs/usr/local/bin/$command"
+       chmod +x "$PWD/rootfs/usr/local/bin/$command"
 done
index 996435745b871e068cc3dc5b30b3501cbccba3d7..1fa31c372a3e54125d40447f9c1407e420a49165 100755 (executable)
@@ -1,17 +1,13 @@
-#!/usr/bin/env bash
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
 
 set -e
 
-cd /usr/src/paperless/src/
-# This ensures environment is setup
-# shellcheck disable=SC1091
-source /sbin/env-from-file.sh
+cd "${PAPERLESS_SRC_DIR}"
 
-if [[ $(id -u) == 0 ]] ;
-then
-       gosu paperless python3 manage.py management_command "$@"
-elif [[ $(id -un) == "paperless" ]] ;
-then
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py management_command "$@"
+elif [[ $(id -un) == "paperless" ]]; then
        python3 manage.py management_command "$@"
 else
        echo "Unknown user."
diff --git a/docker/paperless_cmd.sh b/docker/paperless_cmd.sh
deleted file mode 100755 (executable)
index afedb15..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-
-SUPERVISORD_WORKING_DIR="${PAPERLESS_SUPERVISORD_WORKING_DIR:-$PWD}"
-rootless_args=()
-if [ "$(id -u)" == "$(id -u paperless)" ]; then
-       rootless_args=(
-               --user
-               paperless
-               --logfile
-               "${SUPERVISORD_WORKING_DIR}/supervisord.log"
-               --pidfile
-               "${SUPERVISORD_WORKING_DIR}/supervisord.pid"
-       )
-fi
-
-exec /usr/local/bin/supervisord -c /etc/supervisord.conf "${rootless_args[@]}"
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-custom-init b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-custom-init
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-env-file
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-folders b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-folders
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-migrations b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-migrations
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-modify-user b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-modify-user
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-search-index b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-search-index
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-start b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-start
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-superuser b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-superuser
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-system-checks b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-system-checks
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-tesseract-langs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-db b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-db
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-redis b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-redis
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/run
new file mode 100755 (executable)
index 0000000..83c83df
--- /dev/null
@@ -0,0 +1,8 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+declare -r log_prefix="[init-complete]"
+declare -r end_time=$(date +%s)
+declare -r start_time=${PAPERLESS_START_TIME_S}
+
+echo "${log_prefix} paperless-ngx docker container init completed in $(($end_time-$start_time)) seconds"
+echo "${log_prefix} Starting services"
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/up
new file mode 100644 (file)
index 0000000..9fb31e4
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-complete/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-search-index b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-search-index
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-system-checks b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-system-checks
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-tesseract-langs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-db b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-db
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-redis b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-redis
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/run
new file mode 100755 (executable)
index 0000000..50cc4c2
--- /dev/null
@@ -0,0 +1,44 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[custom-init]"
+
+# Mostly borrowed from the LinuxServer.io base image
+# https://github.com/linuxserver/docker-baseimage-ubuntu/tree/bionic/root/etc/cont-init.d
+declare -r custom_script_dir="/custom-cont-init.d"
+
+# Tamper checking.
+# Don't run files which are owned by anyone except root
+# Don't run files which are writeable by others
+if [ -d "${custom_script_dir}" ]; then
+       if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 ! -user root)" ]; then
+               echo "${log_prefix} **** Potential tampering with custom scripts detected ****"
+               echo "${log_prefix} **** The folder '${custom_script_dir}' must be owned by root ****"
+               exit 0
+       fi
+       if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 -perm -o+w)" ]; then
+               echo "${log_prefix} **** The folder '${custom_script_dir}' or some of contents have write permissions for others, which is a security risk. ****"
+               echo "${log_prefix} **** Please review the permissions and their contents to make sure they are owned by root, and can only be modified by root. ****"
+               exit 0
+       fi
+
+       # Make sure custom init directory has files in it
+       if [ -n "$(/bin/ls --almost-all "${custom_script_dir}" 2>/dev/null)" ]; then
+               echo "${log_prefix} files found in ${custom_script_dir} executing"
+               # Loop over files in the directory
+               for SCRIPT in "${custom_script_dir}"/*; do
+                       NAME="$(basename "${SCRIPT}")"
+                       if [ -f "${SCRIPT}" ]; then
+                               echo "${log_prefix} ${NAME}: executing..."
+                               /command/with-contenv /bin/bash "${SCRIPT}"
+                               echo "${log_prefix} ${NAME}: exited $?"
+                       elif [ ! -f "${SCRIPT}" ]; then
+                               echo "${log_prefix} ${NAME}: is not a file"
+                       fi
+               done
+       else
+               echo "${log_prefix} no custom files found exiting..."
+       fi
+else
+       echo "${log_prefix} ${custom_script_dir} doesn't exist, nothing to do"
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/up
new file mode 100644 (file)
index 0000000..69a261a
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-custom-init/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/dependencies.d/init-start b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/dependencies.d/init-start
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/run
new file mode 100755 (executable)
index 0000000..08b7635
--- /dev/null
@@ -0,0 +1,30 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[env-init]"
+
+echo "${log_prefix} Checking for environment from files"
+
+if find /run/s6/container_environment/*"_FILE" -maxdepth 1 > /dev/null 2>&1; then
+       for FILENAME in /run/s6/container_environment/*; do
+               if [[ "${FILENAME##*/}" == PAPERLESS_*_FILE ]]; then
+                       # This should have been named different..
+                       if [[ ${FILENAME} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${FILENAME} == "PAPERLESS_MODEL_FILE" ]]; then
+                               continue
+                       fi
+                       SECRETFILE=$(cat "${FILENAME}")
+                       # Check the file exists
+                       if [[ -f ${SECRETFILE} ]]; then
+                               # Trim off trailing _FILE
+                               FILESTRIP=${FILENAME//_FILE/}
+                               # Set environment variable
+                               cat "${SECRETFILE}" > "${FILESTRIP}"
+                               echo "${log_prefix} ${FILESTRIP##*/} set from ${FILENAME##*/}"
+                       else
+                               echo "${log_prefix} cannot find secret in ${FILENAME##*/}"
+                       fi
+               fi
+       done
+else
+               echo "${log_prefix} No *_FILE environment found"
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/up
new file mode 100644 (file)
index 0000000..0bb4c22
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-env-file/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/dependencies.d/init-modify-user b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/dependencies.d/init-modify-user
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/run
new file mode 100755 (executable)
index 0000000..5f731ce
--- /dev/null
@@ -0,0 +1,33 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-folders]"
+
+declare -r export_dir="/usr/src/paperless/export"
+declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
+declare -r media_root_dir="${PAPERLESS_MEDIA_ROOT:-/usr/src/paperless/media}"
+declare -r consume_dir="${PAPERLESS_CONSUMPTION_DIR:-/usr/src/paperless/consume}"
+declare -r tmp_dir="${PAPERLESS_SCRATCH_DIR:=/tmp/paperless}"
+
+echo "${log_prefix} Checking for folder existence"
+
+for dir in \
+       "${export_dir}" \
+       "${data_dir}" "${data_dir}/index" \
+       "${media_root_dir}" "${media_root_dir}/documents" "${media_root_dir}/documents/originals" "${media_root_dir}/documents/thumbnails" \
+       "${consume_dir}" \
+       "${tmp_dir}"; do
+       if [[ ! -d "${dir}" ]]; then
+               mkdir --parents --verbose "${dir}"
+       fi
+done
+
+echo "${log_prefix} Adjusting file and folder permissions"
+for dir in \
+       "${export_dir}" \
+       "${data_dir}" \
+       "${media_root_dir}" \
+       "${consume_dir}" \
+       "${tmp_dir}"; do
+       find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown --changes paperless:paperless {} +
+done
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/up
new file mode 100644 (file)
index 0000000..0fb7dc7
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-folders/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-folders b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-folders
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-wait-for-db b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-wait-for-db
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/run
new file mode 100755 (executable)
index 0000000..db0dc26
--- /dev/null
@@ -0,0 +1,20 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+declare -r log_prefix="[init-migrations]"
+declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
+
+(
+       # flock is in place to prevent multiple containers from doing migrations
+       # simultaneously. This also ensures that the db is ready when the command
+       # of the current container starts.
+       flock 200
+       echo "${log_prefix} Apply database migrations..."
+       cd "${PAPERLESS_SRC_DIR}"
+
+       if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+               exec python3 manage.py migrate --skip-checks --no-input
+       else
+               exec s6-setuidgid paperless python3 manage.py migrate --skip-checks --no-input
+       fi
+
+) 200>"${data_dir}/migration_lock"
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/up
new file mode 100644 (file)
index 0000000..7c4cbcf
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-migrations/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/dependencies.d/init-env-file
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/run
new file mode 100755 (executable)
index 0000000..aa61735
--- /dev/null
@@ -0,0 +1,22 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+declare -r log_prefix="[init-user]"
+
+declare -r usermap_original_uid=$(id -u paperless)
+declare -r usermap_original_gid=$(id -g paperless)
+declare -r usermap_new_uid=${USERMAP_UID:-$usermap_original_uid}
+declare -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}}
+
+if [[ ${usermap_new_uid} != "${usermap_original_uid}" ]]; then
+       echo "${log_prefix} Mapping UID for paperless to $usermap_new_uid"
+       usermod --non-unique --uid "${usermap_new_uid}" paperless
+else
+       echo "${log_prefix} No UID changes for paperless"
+fi
+
+if [[ ${usermap_new_gid} != "${usermap_original_gid}" ]]; then
+       echo "${log_prefix} Mapping GID for paperless to $usermap_new_gid"
+       groupmod --non-unique --gid "${usermap_new_gid}" paperless
+else
+       echo "${log_prefix} No GID changes for paperless"
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/up
new file mode 100644 (file)
index 0000000..4c22ce8
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-modify-user/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/dependencies.d/init-migrations b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/dependencies.d/init-migrations
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/run
new file mode 100755 (executable)
index 0000000..2208faf
--- /dev/null
@@ -0,0 +1,28 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-index]"
+
+declare -r index_version=9
+declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
+declare -r index_version_file="${data_dir}/.index_version"
+
+update_index () {
+       echo "${log_prefix} Search index out of date. Updating..."
+       cd "${PAPERLESS_SRC_DIR}"
+       if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+               python3 manage.py document_index reindex --no-progress-bar
+               echo ${index_version} | tee "${index_version_file}" > /dev/null
+       else
+               s6-setuidgid paperless python3 manage.py document_index reindex --no-progress-bar
+               echo ${index_version} | s6-setuidgid paperless tee "${index_version_file}" > /dev/null
+       fi
+}
+
+if [[ (! -f "${index_version_file}") ]]; then
+       echo "${log_prefix} No index version file found"
+       update_index
+elif [[ $(<"${index_version_file}") != "$index_version" ]]; then
+       echo "${log_prefix} index version updated"
+       update_index
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/up
new file mode 100644 (file)
index 0000000..372763a
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-search-index/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/dependencies.d/base b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/dependencies.d/base
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/run
new file mode 100755 (executable)
index 0000000..b6a26fa
--- /dev/null
@@ -0,0 +1,19 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-start]"
+
+echo "${log_prefix} paperless-ngx docker container starting..."
+
+# Set some directories into environment for other steps to access via environment
+# Sort of like variables for later
+printf "/usr/src/paperless/src" > /var/run/s6/container_environment/PAPERLESS_SRC_DIR
+echo $(date +%s) > /var/run/s6/container_environment/PAPERLESS_START_TIME_S
+
+# Check if we're starting as a non-root user
+if [ $(id -u) == $(id -u paperless) ]; then
+       printf "true" > /var/run/s6/container_environment/USER_IS_NON_ROOT
+       echo "${log_prefix}  paperless-ngx docker container running under a user"
+else
+       echo "${log_prefix}  paperless-ngx docker container starting init as root"
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/up
new file mode 100644 (file)
index 0000000..3a6a26d
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-start/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/dependencies.d/init-migrations b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/dependencies.d/init-migrations
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/run
new file mode 100755 (executable)
index 0000000..6251851
--- /dev/null
@@ -0,0 +1,20 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-superuser]"
+
+if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then
+       echo "${log_prefix} Creating superuser..."
+       cd "${PAPERLESS_SRC_DIR}"
+
+       if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+               python3 manage.py manage_superuser
+       else
+               s6-setuidgid paperless python3 manage.py manage_superuser
+       fi
+
+       echo "${log_prefix} Superuser creation done"
+
+else
+       echo "${log_prefix} Not creating superuser"
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/up
new file mode 100644 (file)
index 0000000..055f10a
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-superuser/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-superuser b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-superuser
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-tesseract-langs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/run
new file mode 100755 (executable)
index 0000000..d024765
--- /dev/null
@@ -0,0 +1,15 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-checks]"
+
+# Explicitly run the Django system checks
+echo "${log_prefix} Running Django checks"
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+       python3 manage.py check
+else
+       s6-setuidgid paperless python3 manage.py check
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/up
new file mode 100644 (file)
index 0000000..7403acc
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-system-checks/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/dependencies.d/init-env-file
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run
new file mode 100755 (executable)
index 0000000..7ab4645
--- /dev/null
@@ -0,0 +1,65 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-tesseract-langs]"
+
+install_languages() {
+       echo "Installing languages..."
+
+       read -ra langs <<<"$1"
+
+       # Check that it is not empty
+       if [ ${#langs[@]} -eq 0 ]; then
+               return
+       fi
+
+       # Build list of packages to install
+       to_install=()
+       for lang in "${langs[@]}"; do
+               pkg="tesseract-ocr-$lang"
+
+               if dpkg --status "$pkg" &>/dev/null; then
+                       echo "${log_prefix} Package $pkg already installed!"
+                       continue
+               else
+                       to_install+=("$pkg")
+               fi
+       done
+
+       # Use apt only when we install packages
+       if [ ${#to_install[@]} -gt 0 ]; then
+
+               # Warn the user if they're not root, but try anyway
+               if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+                       echo "${log_prefix} ERROR: Unable to install language ${pkg} as non-root, startup may fail"
+               fi
+
+               apt-get --quiet update &>/dev/null
+
+               for pkg in "${to_install[@]}"; do
+                       if ! apt-cache --quiet show "$pkg" &>/dev/null; then
+                               echo "${log_prefix} Skipped $pkg: Package not found! :("
+                               continue
+                       fi
+                       echo "${log_prefix} Installing package $pkg..."
+                       if ! apt-get --quiet --assume-yes install "$pkg" &>/dev/null; then
+                               echo "${log_prefix} Could not install $pkg"
+                               exit 1
+                       else
+                               echo "${log_prefix} Installed $pkg"
+                       fi
+               done
+
+       fi
+}
+
+echo "${log_prefix} Checking if additional teseract languages needed"
+
+# Install additional languages if specified
+if [[ -n "$PAPERLESS_OCR_LANGUAGES" ]]; then
+
+       install_languages "$PAPERLESS_OCR_LANGUAGES"
+       echo "${log_prefix} Additional packages installed"
+else
+       echo "${log_prefix} No additional installs requested"
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/up
new file mode 100644 (file)
index 0000000..16ef2f4
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/dependencies.d/init-env-file
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/run
new file mode 100755 (executable)
index 0000000..ede8a65
--- /dev/null
@@ -0,0 +1,70 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-db-wait]"
+
+wait_for_postgres() {
+       local attempt_num=1
+       local -r max_attempts=5
+
+       echo "${log_prefix} Waiting for PostgreSQL to start..."
+
+       local -r host="${PAPERLESS_DBHOST:-localhost}"
+       local -r port="${PAPERLESS_DBPORT:-5432}"
+       local -r user="${PAPERLESS_DBUSER:-paperless}"
+
+       # Disable warning, host and port can't have spaces
+       # shellcheck disable=SC2086
+       while [ ! "$(pg_isready -h ${host} -p ${port} --username ${user})" ]; do
+
+               if [ $attempt_num -eq $max_attempts ]; then
+                       echo "${log_prefix} Unable to connect to database."
+                       exit 1
+               else
+                       echo "${log_prefix} Attempt $attempt_num failed! Trying again in 5 seconds..."
+               fi
+
+               attempt_num=$(("$attempt_num" + 1))
+               sleep 5
+       done
+       # Extra in case this is a first start
+       sleep 5
+       echo "Connected to PostgreSQL"
+}
+
+wait_for_mariadb() {
+       echo "${log_prefix} Waiting for MariaDB to start..."
+
+       local -r host="${PAPERLESS_DBHOST:=localhost}"
+       local -r port="${PAPERLESS_DBPORT:=3306}"
+
+       local attempt_num=1
+       local -r max_attempts=5
+
+       # Disable warning, host and port can't have spaces
+       # shellcheck disable=SC2086
+       while ! true > /dev/tcp/$host/$port; do
+
+               if [ $attempt_num -eq $max_attempts ]; then
+                       echo "${log_prefix} Unable to connect to database."
+                       exit 1
+               else
+                       echo "${log_prefix} Attempt $attempt_num failed! Trying again in 5 seconds..."
+
+               fi
+
+               attempt_num=$(("$attempt_num" + 1))
+               sleep 5
+       done
+       echo "Connected to MariaDB"
+}
+
+if [[ "${PAPERLESS_DBENGINE}" == "mariadb" ]]; then
+       echo "${log_prefix} Waiting for MariaDB to report ready"
+       wait_for_mariadb
+elif [[ -n "${PAPERLESS_DBHOST}" ]]; then
+       echo "${log_prefix} Waiting for postgresql to report ready"
+       wait_for_postgres
+fi
+
+       echo "${log_prefix} Database is ready"
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/up
new file mode 100644 (file)
index 0000000..6cbecf5
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-wait-for-db/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/dependencies.d/init-env-file
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run
new file mode 100755 (executable)
index 0000000..407536a
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[init-redis-wait]"
+
+echo "${log_prefix} Waiting for Redis to report ready"
+
+# We use a Python script to send the Redis ping
+# instead of installing redis-tools just for 1 thing
+if ! python3 /usr/local/bin/wait-for-redis.py; then
+       exit 1
+else
+       echo "${log_prefix} Redis ready"
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/type
new file mode 100644 (file)
index 0000000..bdd22a1
--- /dev/null
@@ -0,0 +1 @@
+oneshot
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/up
new file mode 100644 (file)
index 0000000..8a5fa16
--- /dev/null
@@ -0,0 +1 @@
+/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/dependencies.d/init-complete
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/run
new file mode 100755 (executable)
index 0000000..3e1c047
--- /dev/null
@@ -0,0 +1,10 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+cd ${PAPERLESS_SRC_DIR}
+
+if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+       exec python3 manage.py document_consumer
+else
+       exec s6-setuidgid paperless python3 manage.py document_consumer
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/type
new file mode 100644 (file)
index 0000000..5883cff
--- /dev/null
@@ -0,0 +1 @@
+longrun
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/init-complete
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-scheduler b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-scheduler
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-worker
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/run
new file mode 100755 (executable)
index 0000000..a3e4b6a
--- /dev/null
@@ -0,0 +1,24 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+declare -r log_prefix="[svc-flower]"
+
+echo "${log_prefix} Checking if we should start flower..."
+
+if [[ -n "${PAPERLESS_ENABLE_FLOWER}" ]]; then
+       # Small delay to allow celery to be up first
+       echo "${log_prefix} Starting flower in 5s"
+       sleep 5
+       cd ${PAPERLESS_SRC_DIR}
+
+       if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+               exec /usr/local/bin/celery --app paperless flower --conf=${PAPERLESS_SRC_DIR}/paperless/flowerconfig.py
+       else
+               exec s6-setuidgid paperless /usr/local/bin/celery --app paperless flower --conf=${PAPERLESS_SRC_DIR}/paperless/flowerconfig.py
+       fi
+
+else
+       echo "${log_prefix} Not starting flower"
+       # https://skarnet.org/software/s6/s6-svc.html
+       s6-svc -Od .
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/type
new file mode 100644 (file)
index 0000000..5883cff
--- /dev/null
@@ -0,0 +1 @@
+longrun
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/init-complete
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/svc-worker
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/run
new file mode 100755 (executable)
index 0000000..396a4d2
--- /dev/null
@@ -0,0 +1,10 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+cd ${PAPERLESS_SRC_DIR}
+
+if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+       exec /usr/local/bin/celery --app paperless beat --loglevel INFO
+else
+       exec s6-setuidgid paperless /usr/local/bin/celery --app paperless beat --loglevel INFO
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/type
new file mode 100644 (file)
index 0000000..5883cff
--- /dev/null
@@ -0,0 +1 @@
+longrun
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/init-complete
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-consumer b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-consumer
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-scheduler b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-scheduler
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-worker
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/run
new file mode 100755 (executable)
index 0000000..423b175
--- /dev/null
@@ -0,0 +1,10 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+cd ${PAPERLESS_SRC_DIR}
+
+if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+       exec /usr/local/bin/gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application
+else
+       exec s6-setuidgid paperless /usr/local/bin/gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/type
new file mode 100644 (file)
index 0000000..5883cff
--- /dev/null
@@ -0,0 +1 @@
+longrun
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/dependencies.d/init-complete
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/run
new file mode 100755 (executable)
index 0000000..0bf833c
--- /dev/null
@@ -0,0 +1,10 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+cd ${PAPERLESS_SRC_DIR}
+
+if [[ -n "${USER_IS_NON_ROOT}" ]]; then
+       exec /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
+else
+       exec s6-setuidgid paperless /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
+fi
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/type
new file mode 100644 (file)
index 0000000..5883cff
--- /dev/null
@@ -0,0 +1 @@
+longrun
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/init-complete
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-consumer b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-consumer
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-flower b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-flower
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-scheduler b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-scheduler
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-webserver b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-webserver
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-worker
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docker/rootfs/usr/local/bin/convert_mariadb_uuid b/docker/rootfs/usr/local/bin/convert_mariadb_uuid
new file mode 100755 (executable)
index 0000000..806a98f
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py convert_mariadb_uuid "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py convert_mariadb_uuid "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/decrypt_documents b/docker/rootfs/usr/local/bin/decrypt_documents
new file mode 100755 (executable)
index 0000000..4da1549
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py decrypt_documents "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py decrypt_documents "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_archiver b/docker/rootfs/usr/local/bin/document_archiver
new file mode 100755 (executable)
index 0000000..383acfc
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_archiver "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_archiver "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_create_classifier b/docker/rootfs/usr/local/bin/document_create_classifier
new file mode 100755 (executable)
index 0000000..72dc33d
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_create_classifier "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_create_classifier "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_exporter b/docker/rootfs/usr/local/bin/document_exporter
new file mode 100755 (executable)
index 0000000..7f48215
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_exporter "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_exporter "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_fuzzy_match b/docker/rootfs/usr/local/bin/document_fuzzy_match
new file mode 100755 (executable)
index 0000000..5b95485
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_fuzzy_match "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_fuzzy_match "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_importer b/docker/rootfs/usr/local/bin/document_importer
new file mode 100755 (executable)
index 0000000..2286e89
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_importer "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_importer "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_index b/docker/rootfs/usr/local/bin/document_index
new file mode 100755 (executable)
index 0000000..2d518b5
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_index "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_index "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_renamer b/docker/rootfs/usr/local/bin/document_renamer
new file mode 100755 (executable)
index 0000000..326317a
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_renamer "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_renamer "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_retagger b/docker/rootfs/usr/local/bin/document_retagger
new file mode 100755 (executable)
index 0000000..3bab3e7
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_retagger "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_retagger "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_sanity_checker b/docker/rootfs/usr/local/bin/document_sanity_checker
new file mode 100755 (executable)
index 0000000..5c0c29e
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_sanity_checker "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_sanity_checker "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/document_thumbnails b/docker/rootfs/usr/local/bin/document_thumbnails
new file mode 100755 (executable)
index 0000000..c1000c3
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py document_thumbnails "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py document_thumbnails "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/mail_fetcher b/docker/rootfs/usr/local/bin/mail_fetcher
new file mode 100755 (executable)
index 0000000..2ae1d1d
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py mail_fetcher "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py mail_fetcher "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/manage_superuser b/docker/rootfs/usr/local/bin/manage_superuser
new file mode 100755 (executable)
index 0000000..9f7f37e
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py manage_superuser "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py manage_superuser "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/prune_audit_logs b/docker/rootfs/usr/local/bin/prune_audit_logs
new file mode 100755 (executable)
index 0000000..b9142e9
--- /dev/null
@@ -0,0 +1,14 @@
+#!/command/with-contenv /usr/bin/bash
+# shellcheck shell=bash
+
+set -e
+
+cd "${PAPERLESS_SRC_DIR}"
+
+if [[ $(id -u) == 0 ]]; then
+       s6-setuidgid paperless python3 manage.py prune_audit_logs "$@"
+elif [[ $(id -un) == "paperless" ]]; then
+       python3 manage.py prune_audit_logs "$@"
+else
+       echo "Unknown user."
+fi
diff --git a/docker/rootfs/usr/local/bin/wait-for-redis.py b/docker/rootfs/usr/local/bin/wait-for-redis.py
new file mode 100755 (executable)
index 0000000..9ae4a35
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+"""
+Simple script which attempts to ping the Redis broker as set in the environment for
+a certain number of times, waiting a little bit in between
+
+"""
+
+import os
+import sys
+import time
+
+import click
+from redis import Redis
+
+
+@click.command(context_settings={"show_default": True})
+@click.option(
+    "--retry-count",
+    default=5,
+    type=int,
+    help="Count of times to retry the Redis connection",
+)
+@click.option(
+    "--retry-sleep",
+    default=5,
+    type=int,
+    help="Seconds to wait between Redis connection retries",
+)
+@click.argument(
+    "redis_url",
+    type=str,
+    envvar="PAPERLESS_REDIS",
+    default="redis://localhost:6379",
+)
+def wait(redis_url: str, retry_count: int, retry_sleep: int) -> None:
+    click.echo("Waiting for Redis...")
+
+    attempt = 0
+    with Redis.from_url(url=redis_url) as client:
+        while attempt < retry_count:
+            try:
+                client.ping()
+                break
+            except Exception as e:
+                click.echo(
+                    f"Redis ping #{attempt} failed.\n"
+                    f"Error: {e!s}.\n"
+                    f"Waiting {retry_sleep}s",
+                )
+                time.sleep(retry_sleep)
+                attempt += 1
+
+    if attempt >= retry_count:
+        click.echo(
+            "Failed to connect to redis using environment variable PAPERLESS_REDIS.",
+        )
+        sys.exit(os.EX_UNAVAILABLE)
+    else:
+        click.echo("Connected to Redis broker.")
+        sys.exit(os.EX_OK)
+
+
+if __name__ == "__main__":
+    wait()
diff --git a/docker/supervisord.conf b/docker/supervisord.conf
deleted file mode 100644 (file)
index 0097607..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-[supervisord]
-nodaemon=true               ; start in foreground if true; default false
-logfile=/var/log/supervisord/supervisord.log ; main log file; default $CWD/supervisord.log
-pidfile=/var/run/supervisord/supervisord.pid ; supervisord pidfile; default supervisord.pid
-logfile_maxbytes=50MB        ; max main logfile bytes b4 rotation; default 50MB
-logfile_backups=10           ; # of main logfile backups; 0 means none, default 10
-loglevel=info                ; log level; default info; others: debug,warn,trace
-user=root
-
-[program:gunicorn]
-command=gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application
-user=paperless
-priority = 1
-stdout_logfile=/dev/stdout
-stdout_logfile_maxbytes=0
-stderr_logfile=/dev/stderr
-stderr_logfile_maxbytes=0
-environment = HOME="/usr/src/paperless",USER="paperless"
-
-[program:consumer]
-command=python3 manage.py document_consumer
-user=paperless
-stopsignal=INT
-priority = 20
-stdout_logfile=/dev/stdout
-stdout_logfile_maxbytes=0
-stderr_logfile=/dev/stderr
-stderr_logfile_maxbytes=0
-environment = HOME="/usr/src/paperless",USER="paperless"
-
-[program:celery]
-
-command = celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
-user=paperless
-stopasgroup = true
-stopwaitsecs = 60
-priority = 5
-stdout_logfile=/dev/stdout
-stdout_logfile_maxbytes=0
-stderr_logfile=/dev/stderr
-stderr_logfile_maxbytes=0
-environment = HOME="/usr/src/paperless",USER="paperless"
-
-[program:celery-beat]
-
-command = celery --app paperless beat --loglevel INFO
-user=paperless
-stopasgroup = true
-priority = 10
-stdout_logfile=/dev/stdout
-stdout_logfile_maxbytes=0
-stderr_logfile=/dev/stderr
-stderr_logfile_maxbytes=0
-environment = HOME="/usr/src/paperless",USER="paperless"
-
-[program:celery-flower]
-command = /usr/local/bin/flower-conditional.sh
-user = paperless
-startsecs = 0
-priority = 40
-stdout_logfile=/dev/stdout
-stdout_logfile_maxbytes=0
-stderr_logfile=/dev/stderr
-stderr_logfile_maxbytes=0
-environment = HOME="/usr/src/paperless",USER="paperless"
diff --git a/docker/wait-for-redis.py b/docker/wait-for-redis.py
deleted file mode 100755 (executable)
index c3e4f1d..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python3
-"""
-Simple script which attempts to ping the Redis broker as set in the environment for
-a certain number of times, waiting a little bit in between
-
-"""
-
-import os
-import sys
-import time
-from typing import Final
-
-from redis import Redis
-
-if __name__ == "__main__":
-    MAX_RETRY_COUNT: Final[int] = 5
-    RETRY_SLEEP_SECONDS: Final[int] = 5
-
-    REDIS_URL: Final[str] = os.getenv("PAPERLESS_REDIS", "redis://localhost:6379")
-
-    print("Waiting for Redis...", flush=True)
-
-    attempt = 0
-    with Redis.from_url(url=REDIS_URL) as client:
-        while attempt < MAX_RETRY_COUNT:
-            try:
-                client.ping()
-                break
-            except Exception as e:
-                print(
-                    f"Redis ping #{attempt} failed.\n"
-                    f"Error: {e!s}.\n"
-                    f"Waiting {RETRY_SLEEP_SECONDS}s",
-                    flush=True,
-                )
-                time.sleep(RETRY_SLEEP_SECONDS)
-                attempt += 1
-
-    if attempt >= MAX_RETRY_COUNT:
-        print("Failed to connect to redis using environment variable PAPERLESS_REDIS.")
-        sys.exit(os.EX_UNAVAILABLE)
-    else:
-        print("Connected to Redis broker.")
-        sys.exit(os.EX_OK)
index b81c10b0d9930754372632b7defbc0d037bf7020..3724c792d210ef7f40b647d3964d6826ed1ae079 100644 (file)
@@ -1596,9 +1596,11 @@ started by the container.
 
 #### [`PAPERLESS_SUPERVISORD_WORKING_DIR=<defined>`](#PAPERLESS_SUPERVISORD_WORKING_DIR) {#PAPERLESS_SUPERVISORD_WORKING_DIR}
 
-: If this environment variable is defined, the `supervisord.log` and `supervisord.pid` file will be created under the specified path in `PAPERLESS_SUPERVISORD_WORKING_DIR`. Setting `PAPERLESS_SUPERVISORD_WORKING_DIR=/tmp` and `PYTHONPYCACHEPREFIX=/tmp/pycache` would allow paperless to work on a read-only filesystem.
+!!! warning
 
-    Please take note that the `PAPERLESS_DATA_DIR` and `PAPERLESS_MEDIA_ROOT` paths still have to be writable, just like the `PAPERLESS_SUPERVISORD_WORKING_DIR`. The can be archived by using bind or volume mounts. Only works in the container is run as user *paperless*
+        This option is deprecated and has no effect.  For read only file system support,
+        see [S6_READ_ONLY_ROOT](https://github.com/just-containers/s6-overlay#customizing-s6-overlay-behaviour)
+        from s6-overlay.
 
 ## Frontend Settings