Merge branch 'main' into dev
							
								
								
									
										26
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -49,7 +49,7 @@ jobs:
 | 
			
		||||
        name: Set up Python
 | 
			
		||||
        uses: actions/setup-python@v4
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: 3.9
 | 
			
		||||
          python-version: 3.8
 | 
			
		||||
          cache: "pipenv"
 | 
			
		||||
          cache-dependency-path: 'Pipfile.lock'
 | 
			
		||||
      -
 | 
			
		||||
@ -63,14 +63,32 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Make documentation
 | 
			
		||||
        run: |
 | 
			
		||||
          cd docs/
 | 
			
		||||
          pipenv run make html
 | 
			
		||||
          pipenv run mkdocs build --config-file ./mkdocs.yml
 | 
			
		||||
      -
 | 
			
		||||
        name: Upload artifact
 | 
			
		||||
        uses: actions/upload-artifact@v3
 | 
			
		||||
        with:
 | 
			
		||||
          name: documentation
 | 
			
		||||
          path: docs/_build/html/
 | 
			
		||||
          path: site/
 | 
			
		||||
 | 
			
		||||
  documentation-deploy:
 | 
			
		||||
    name: "Deploy Documentation"
 | 
			
		||||
    runs-on: ubuntu-20.04
 | 
			
		||||
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
 | 
			
		||||
    needs:
 | 
			
		||||
      - documentation
 | 
			
		||||
    steps:
 | 
			
		||||
      -
 | 
			
		||||
        name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
      -
 | 
			
		||||
        name: Deploy docs
 | 
			
		||||
        uses: mhausenblas/mkdocs-deploy-gh-pages@master
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          CUSTOM_DOMAIN: paperless-ngx.com
 | 
			
		||||
          CONFIG_FILE: mkdocs.yml
 | 
			
		||||
          EXTRA_PACKAGES: build-base
 | 
			
		||||
 | 
			
		||||
  tests-backend:
 | 
			
		||||
    name: "Tests (${{ matrix.python-version }})"
 | 
			
		||||
 | 
			
		||||
@ -1,16 +0,0 @@
 | 
			
		||||
# .readthedocs.yml
 | 
			
		||||
# Read the Docs configuration file
 | 
			
		||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
 | 
			
		||||
 | 
			
		||||
# Required
 | 
			
		||||
version: 2
 | 
			
		||||
 | 
			
		||||
# Build documentation in the docs/ directory with Sphinx
 | 
			
		||||
sphinx:
 | 
			
		||||
  configuration: docs/conf.py
 | 
			
		||||
 | 
			
		||||
# Optionally set the version of Python and requirements required to build your docs
 | 
			
		||||
python:
 | 
			
		||||
  version: "3.8"
 | 
			
		||||
  install:
 | 
			
		||||
    - requirements: docs/requirements.txt
 | 
			
		||||
							
								
								
									
										3
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						@ -71,10 +71,9 @@ pytest-django = "*"
 | 
			
		||||
pytest-env = "*"
 | 
			
		||||
pytest-sugar = "*"
 | 
			
		||||
pytest-xdist = "*"
 | 
			
		||||
sphinx = "~=5.3"
 | 
			
		||||
sphinx_rtd_theme = "*"
 | 
			
		||||
tox = "*"
 | 
			
		||||
black = "*"
 | 
			
		||||
pre-commit = "*"
 | 
			
		||||
sphinx-autobuild = "*"
 | 
			
		||||
myst-parser = "*"
 | 
			
		||||
mkdocs-material = "*"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										129
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "_meta": {
 | 
			
		||||
        "hash": {
 | 
			
		||||
            "sha256": "9fefc737155e789ced61b41750b4273c7780ac7801c50cf36dc5925be3b85783"
 | 
			
		||||
            "sha256": "0242e3e296e09b30fb69e0d7a2f2e8feb4c6a23d3c7ec99500f2883a032a8c84"
 | 
			
		||||
        },
 | 
			
		||||
        "pipfile-spec": 6,
 | 
			
		||||
        "requires": {},
 | 
			
		||||
@ -99,6 +99,7 @@
 | 
			
		||||
                "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac",
 | 
			
		||||
                "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "markers": "python_version < '3.9'",
 | 
			
		||||
            "version": "==0.2.1"
 | 
			
		||||
        },
 | 
			
		||||
@ -218,7 +219,7 @@
 | 
			
		||||
                "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
 | 
			
		||||
                "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_full_version >= '3.6.0'",
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==2.1.1"
 | 
			
		||||
        },
 | 
			
		||||
        "click": {
 | 
			
		||||
@ -234,7 +235,7 @@
 | 
			
		||||
                "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667",
 | 
			
		||||
                "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_full_version >= '3.6.2' and python_full_version < '4.0.0'",
 | 
			
		||||
            "markers": "python_version < '4' and python_full_version >= '3.6.2'",
 | 
			
		||||
            "version": "==0.3.0"
 | 
			
		||||
        },
 | 
			
		||||
        "click-plugins": {
 | 
			
		||||
@ -1624,7 +1625,7 @@
 | 
			
		||||
                "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa",
 | 
			
		||||
                "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.7'",
 | 
			
		||||
            "markers": "python_version < '3.10'",
 | 
			
		||||
            "version": "==4.4.0"
 | 
			
		||||
        },
 | 
			
		||||
        "tzdata": {
 | 
			
		||||
@ -2054,7 +2055,7 @@
 | 
			
		||||
                "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
 | 
			
		||||
                "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_full_version >= '3.6.0'",
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==2.1.1"
 | 
			
		||||
        },
 | 
			
		||||
        "click": {
 | 
			
		||||
@ -2074,6 +2075,9 @@
 | 
			
		||||
            "version": "==0.4.6"
 | 
			
		||||
        },
 | 
			
		||||
        "coverage": {
 | 
			
		||||
            "extras": [
 | 
			
		||||
                "toml"
 | 
			
		||||
            ],
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79",
 | 
			
		||||
                "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a",
 | 
			
		||||
@ -2198,6 +2202,13 @@
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==3.8.0"
 | 
			
		||||
        },
 | 
			
		||||
        "ghp-import": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619",
 | 
			
		||||
                "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.1.0"
 | 
			
		||||
        },
 | 
			
		||||
        "identify": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440",
 | 
			
		||||
@ -2222,6 +2233,14 @@
 | 
			
		||||
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
 | 
			
		||||
            "version": "==1.4.1"
 | 
			
		||||
        },
 | 
			
		||||
        "importlib-metadata": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:d5059f9f1e8e41f80e9c56c2ee58811450c31984dfa625329ffd7c0dad88a73b",
 | 
			
		||||
                "sha256:d84d17e21670ec07990e1044a99efe8d615d860fd176fc29ef5c306068fda313"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version < '3.10'",
 | 
			
		||||
            "version": "==5.1.0"
 | 
			
		||||
        },
 | 
			
		||||
        "iniconfig": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
 | 
			
		||||
@ -2243,6 +2262,14 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.6.3"
 | 
			
		||||
        },
 | 
			
		||||
        "markdown": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874",
 | 
			
		||||
                "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==3.3.7"
 | 
			
		||||
        },
 | 
			
		||||
        "markdown-it-py": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27",
 | 
			
		||||
@ -2313,6 +2340,38 @@
 | 
			
		||||
            "markers": "python_version >= '3.7'",
 | 
			
		||||
            "version": "==0.1.2"
 | 
			
		||||
        },
 | 
			
		||||
        "mergedeep": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8",
 | 
			
		||||
                "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==1.3.4"
 | 
			
		||||
        },
 | 
			
		||||
        "mkdocs": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:8947af423a6d0facf41ea1195b8e1e8c85ad94ac95ae307fe11232e0424b11c5",
 | 
			
		||||
                "sha256:c8856a832c1e56702577023cd64cc5f84948280c1c0fcc6af4cd39006ea6aa8c"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.7'",
 | 
			
		||||
            "version": "==1.4.2"
 | 
			
		||||
        },
 | 
			
		||||
        "mkdocs-material": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:b0ea0513fd8cab323e8a825d6692ea07fa83e917bb5db042e523afecc7064ab7",
 | 
			
		||||
                "sha256:c907b4b052240a5778074a30a78f31a1f8ff82d7012356dc26898b97559f082e"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==8.5.11"
 | 
			
		||||
        },
 | 
			
		||||
        "mkdocs-material-extensions": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:9c003da71e2cc2493d910237448c672e00cefc800d3d6ae93d2fc69979e3bd93",
 | 
			
		||||
                "sha256:e41d9f38e4798b6617ad98ca8f7f1157b1e4385ac1459ca1e4ea219b556df945"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.7'",
 | 
			
		||||
            "version": "==1.1.1"
 | 
			
		||||
        },
 | 
			
		||||
        "mypy-extensions": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
 | 
			
		||||
@ -2400,6 +2459,14 @@
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==2.13.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pymdown-extensions": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0f8fb7b74a37a61cc34e90b2c91865458b713ec774894ffad64353a5fce85cfc",
 | 
			
		||||
                "sha256:ac698c15265680db5eb13cd4342abfcde2079ac01e5486028f47a1b41547b859"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.7'",
 | 
			
		||||
            "version": "==9.9"
 | 
			
		||||
        },
 | 
			
		||||
        "pyparsing": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
 | 
			
		||||
@ -2516,6 +2583,14 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==6.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pyyaml-env-tag": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb",
 | 
			
		||||
                "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==0.1"
 | 
			
		||||
        },
 | 
			
		||||
        "requests": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
 | 
			
		||||
@ -2552,7 +2627,6 @@
 | 
			
		||||
                "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d",
 | 
			
		||||
                "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==5.3.0"
 | 
			
		||||
        },
 | 
			
		||||
        "sphinx-autobuild": {
 | 
			
		||||
@ -2640,7 +2714,7 @@
 | 
			
		||||
                "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
 | 
			
		||||
                "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_full_version < '3.11.0a7'",
 | 
			
		||||
            "markers": "python_version < '3.11' and python_version >= '3.7'",
 | 
			
		||||
            "version": "==2.0.1"
 | 
			
		||||
        },
 | 
			
		||||
        "tornado": {
 | 
			
		||||
@ -2673,7 +2747,7 @@
 | 
			
		||||
                "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa",
 | 
			
		||||
                "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.7'",
 | 
			
		||||
            "markers": "python_version < '3.10'",
 | 
			
		||||
            "version": "==4.4.0"
 | 
			
		||||
        },
 | 
			
		||||
        "urllib3": {
 | 
			
		||||
@ -2691,6 +2765,45 @@
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version >= '3.6'",
 | 
			
		||||
            "version": "==20.16.6"
 | 
			
		||||
        },
 | 
			
		||||
        "watchdog": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412",
 | 
			
		||||
                "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654",
 | 
			
		||||
                "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306",
 | 
			
		||||
                "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33",
 | 
			
		||||
                "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd",
 | 
			
		||||
                "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7",
 | 
			
		||||
                "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892",
 | 
			
		||||
                "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609",
 | 
			
		||||
                "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6",
 | 
			
		||||
                "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1",
 | 
			
		||||
                "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591",
 | 
			
		||||
                "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d",
 | 
			
		||||
                "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d",
 | 
			
		||||
                "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c",
 | 
			
		||||
                "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3",
 | 
			
		||||
                "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39",
 | 
			
		||||
                "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213",
 | 
			
		||||
                "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330",
 | 
			
		||||
                "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428",
 | 
			
		||||
                "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1",
 | 
			
		||||
                "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846",
 | 
			
		||||
                "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153",
 | 
			
		||||
                "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3",
 | 
			
		||||
                "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9",
 | 
			
		||||
                "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==2.1.9"
 | 
			
		||||
        },
 | 
			
		||||
        "zipp": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1",
 | 
			
		||||
                "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"
 | 
			
		||||
            ],
 | 
			
		||||
            "markers": "python_version < '3.9'",
 | 
			
		||||
            "version": "==3.10.0"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										181
									
								
								docs/Makefile
									
									
									
									
									
								
							
							
						
						@ -1,181 +0,0 @@
 | 
			
		||||
# Makefile for Sphinx documentation
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# You can set these variables from the command line.
 | 
			
		||||
SPHINXOPTS    =
 | 
			
		||||
SPHINXBUILD   = sphinx-build
 | 
			
		||||
PAPER         =
 | 
			
		||||
BUILDDIR      = _build
 | 
			
		||||
 | 
			
		||||
# User-friendly check for sphinx-build
 | 
			
		||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
 | 
			
		||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Internal variables.
 | 
			
		||||
PAPEROPT_a4     = -D latex_paper_size=a4
 | 
			
		||||
PAPEROPT_letter = -D latex_paper_size=letter
 | 
			
		||||
ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 | 
			
		||||
# the i18n builder cannot share the environment and doctrees with the others
 | 
			
		||||
I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 | 
			
		||||
 | 
			
		||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
 | 
			
		||||
 | 
			
		||||
help:
 | 
			
		||||
	@echo "Please use \`make <target>' where <target> is one of"
 | 
			
		||||
	@echo "  html       to make standalone HTML files"
 | 
			
		||||
	@echo "  livehtml   to preview changes with live reload in your browser"
 | 
			
		||||
	@echo "  dirhtml    to make HTML files named index.html in directories"
 | 
			
		||||
	@echo "  singlehtml to make a single large HTML file"
 | 
			
		||||
	@echo "  pickle     to make pickle files"
 | 
			
		||||
	@echo "  json       to make JSON files"
 | 
			
		||||
	@echo "  htmlhelp   to make HTML files and a HTML help project"
 | 
			
		||||
	@echo "  qthelp     to make HTML files and a qthelp project"
 | 
			
		||||
	@echo "  devhelp    to make HTML files and a Devhelp project"
 | 
			
		||||
	@echo "  epub       to make an epub"
 | 
			
		||||
	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
 | 
			
		||||
	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
 | 
			
		||||
	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
 | 
			
		||||
	@echo "  text       to make text files"
 | 
			
		||||
	@echo "  man        to make manual pages"
 | 
			
		||||
	@echo "  texinfo    to make Texinfo files"
 | 
			
		||||
	@echo "  info       to make Texinfo files and run them through makeinfo"
 | 
			
		||||
	@echo "  gettext    to make PO message catalogs"
 | 
			
		||||
	@echo "  changes    to make an overview of all changed/added/deprecated items"
 | 
			
		||||
	@echo "  xml        to make Docutils-native XML files"
 | 
			
		||||
	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
 | 
			
		||||
	@echo "  linkcheck  to check all external links for integrity"
 | 
			
		||||
	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -rf $(BUILDDIR)/*
 | 
			
		||||
 | 
			
		||||
html:
 | 
			
		||||
	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
 | 
			
		||||
 | 
			
		||||
livehtml:
 | 
			
		||||
	sphinx-autobuild "./" "$(BUILDDIR)" $(O)
 | 
			
		||||
 | 
			
		||||
dirhtml:
 | 
			
		||||
	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
 | 
			
		||||
 | 
			
		||||
singlehtml:
 | 
			
		||||
	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
 | 
			
		||||
 | 
			
		||||
pickle:
 | 
			
		||||
	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished; now you can process the pickle files."
 | 
			
		||||
 | 
			
		||||
json:
 | 
			
		||||
	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished; now you can process the JSON files."
 | 
			
		||||
 | 
			
		||||
htmlhelp:
 | 
			
		||||
	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished; now you can run HTML Help Workshop with the" \
 | 
			
		||||
	      ".hhp project file in $(BUILDDIR)/htmlhelp."
 | 
			
		||||
 | 
			
		||||
qthelp:
 | 
			
		||||
	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
 | 
			
		||||
	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
 | 
			
		||||
	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/RIPEAtlasToolsMagellan.qhcp"
 | 
			
		||||
	@echo "To view the help file:"
 | 
			
		||||
	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/RIPEAtlasToolsMagellan.qhc"
 | 
			
		||||
 | 
			
		||||
devhelp:
 | 
			
		||||
	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished."
 | 
			
		||||
	@echo "To view the help file:"
 | 
			
		||||
	@echo "# mkdir -p $$HOME/.local/share/devhelp/RIPEAtlasToolsMagellan"
 | 
			
		||||
	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/RIPEAtlasToolsMagellan"
 | 
			
		||||
	@echo "# devhelp"
 | 
			
		||||
 | 
			
		||||
epub:
 | 
			
		||||
	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
 | 
			
		||||
 | 
			
		||||
latex:
 | 
			
		||||
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
 | 
			
		||||
	@echo "Run \`make' in that directory to run these through (pdf)latex" \
 | 
			
		||||
	      "(use \`make latexpdf' here to do that automatically)."
 | 
			
		||||
 | 
			
		||||
latexpdf:
 | 
			
		||||
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 | 
			
		||||
	@echo "Running LaTeX files through pdflatex..."
 | 
			
		||||
	$(MAKE) -C $(BUILDDIR)/latex all-pdf
 | 
			
		||||
	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
 | 
			
		||||
 | 
			
		||||
latexpdfja:
 | 
			
		||||
	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
 | 
			
		||||
	@echo "Running LaTeX files through platex and dvipdfmx..."
 | 
			
		||||
	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
 | 
			
		||||
	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
 | 
			
		||||
 | 
			
		||||
text:
 | 
			
		||||
	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The text files are in $(BUILDDIR)/text."
 | 
			
		||||
 | 
			
		||||
man:
 | 
			
		||||
	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
 | 
			
		||||
 | 
			
		||||
texinfo:
 | 
			
		||||
	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
 | 
			
		||||
	@echo "Run \`make' in that directory to run these through makeinfo" \
 | 
			
		||||
	      "(use \`make info' here to do that automatically)."
 | 
			
		||||
 | 
			
		||||
info:
 | 
			
		||||
	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
 | 
			
		||||
	@echo "Running Texinfo files through makeinfo..."
 | 
			
		||||
	make -C $(BUILDDIR)/texinfo info
 | 
			
		||||
	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
 | 
			
		||||
 | 
			
		||||
gettext:
 | 
			
		||||
	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
 | 
			
		||||
 | 
			
		||||
changes:
 | 
			
		||||
	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "The overview file is in $(BUILDDIR)/changes."
 | 
			
		||||
 | 
			
		||||
linkcheck:
 | 
			
		||||
	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Link check complete; look for any errors in the above output " \
 | 
			
		||||
	      "or in $(BUILDDIR)/linkcheck/output.txt."
 | 
			
		||||
 | 
			
		||||
doctest:
 | 
			
		||||
	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
 | 
			
		||||
	@echo "Testing of doctests in the sources finished, look at the " \
 | 
			
		||||
	      "results in $(BUILDDIR)/doctest/output.txt."
 | 
			
		||||
 | 
			
		||||
xml:
 | 
			
		||||
	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
 | 
			
		||||
 | 
			
		||||
pseudoxml:
 | 
			
		||||
	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
 | 
			
		||||
	@echo
 | 
			
		||||
	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
 | 
			
		||||
							
								
								
									
										597
									
								
								docs/_static/css/custom.css
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -1,597 +0,0 @@
 | 
			
		||||
/* Variables */
 | 
			
		||||
:root {
 | 
			
		||||
  --color-text-body: #5c5962;
 | 
			
		||||
  --color-text-body-light: #fcfcfc;
 | 
			
		||||
  --color-text-anchor: #7253ed;
 | 
			
		||||
  --color-text-alt: rgba(0, 0, 0, 0.3);
 | 
			
		||||
  --color-text-title: #27262b;
 | 
			
		||||
  --color-text-code-inline: #e74c3c;
 | 
			
		||||
  --color-text-code-nt: #062873;
 | 
			
		||||
  --color-text-selection: #b19eff;
 | 
			
		||||
  --color-bg-body: #fcfcfc;
 | 
			
		||||
  --color-bg-body-alt: #f3f6f6;
 | 
			
		||||
  --color-bg-side-nav: #f5f6fa;
 | 
			
		||||
  --color-bg-side-nav-hover: #ebedf5;
 | 
			
		||||
  --color-bg-code-block: var(--color-bg-side-nav);
 | 
			
		||||
  --color-border: #eeebee;
 | 
			
		||||
  --color-btn-neutral-bg: #f3f6f6;
 | 
			
		||||
  --color-btn-neutral-bg-hover: #e5ebeb;
 | 
			
		||||
  --color-success-title: #1abc9c;
 | 
			
		||||
  --color-success-body: #dbfaf4;
 | 
			
		||||
  --color-warning-title: #f0b37e;
 | 
			
		||||
  --color-warning-body: #ffedcc;
 | 
			
		||||
  --color-danger-title: #f29f97;
 | 
			
		||||
  --color-danger-body: #fdf3f2;
 | 
			
		||||
  --color-info-title: #6ab0de;
 | 
			
		||||
  --color-info-body: #e7f2fa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark-mode {
 | 
			
		||||
  --color-text-body: #abb2bf;
 | 
			
		||||
  --color-text-body-light: #9499a2;
 | 
			
		||||
  --color-text-alt: rgba(0255, 255, 255, 0.5);
 | 
			
		||||
  --color-text-title: var(--color-text-anchor);
 | 
			
		||||
  --color-text-code-inline: #abb2bf;
 | 
			
		||||
  --color-text-code-nt: #2063f3;
 | 
			
		||||
  --color-text-selection: #030303;
 | 
			
		||||
  --color-bg-body: #1d1d20 !important;
 | 
			
		||||
  --color-bg-body-alt: #131315;
 | 
			
		||||
  --color-bg-side-nav: #18181a;
 | 
			
		||||
  --color-bg-side-nav-hover: #101216;
 | 
			
		||||
  --color-bg-code-block: #101216;
 | 
			
		||||
  --color-border: #47494f;
 | 
			
		||||
  --color-btn-neutral-bg: #242529;
 | 
			
		||||
  --color-btn-neutral-bg-hover: #101216;
 | 
			
		||||
  --color-success-title: #02120f;
 | 
			
		||||
  --color-success-body: #041b17;
 | 
			
		||||
  --color-warning-title: #1b0e03;
 | 
			
		||||
  --color-warning-body: #371d06;
 | 
			
		||||
  --color-danger-title: #120902;
 | 
			
		||||
  --color-danger-body: #1b0503;
 | 
			
		||||
  --color-info-title: #020608;
 | 
			
		||||
  --color-info-body: #06141e;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
* {
 | 
			
		||||
  transition: background-color 0.3s ease, border-color 0.3s ease;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Typography */
 | 
			
		||||
body {
 | 
			
		||||
  font-family: system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
 | 
			
		||||
  font-size: inherit;
 | 
			
		||||
  line-height: 1.4;
 | 
			
		||||
  color: var(--color-text-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content p {
 | 
			
		||||
  word-break: break-word;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1, h2, h3, h4, h5, h6 {
 | 
			
		||||
  font-family: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .toctree-wrapper>p.caption, .rst-content h1, .rst-content h2, .rst-content h3, .rst-content h4, .rst-content h5, .rst-content h6 {
 | 
			
		||||
  padding-top: .5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p, .main-content-wrap, .rst-content .section ul, .rst-content .toctree-wrapper ul, .rst-content section ul, .wy-plain-list-disc, article ul {
 | 
			
		||||
  line-height: 1.6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pre, .code, .rst-content .linenodiv pre, .rst-content div[class^=highlight] pre, .rst-content pre.literal-block {
 | 
			
		||||
  font-family: "SFMono-Regular", Menlo,Consolas, Monospace;
 | 
			
		||||
  font-size: 0.75em;
 | 
			
		||||
  line-height: 1.8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4 {
 | 
			
		||||
  font-size: 1rem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-versions {
 | 
			
		||||
  font-family: inherit;
 | 
			
		||||
  line-height: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer, footer p {
 | 
			
		||||
  font-size: .8rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
footer .rst-footer-buttons {
 | 
			
		||||
  font-size: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 400px) {
 | 
			
		||||
  /* break code lines on mobile */
 | 
			
		||||
  pre, code {
 | 
			
		||||
    word-break: break-word;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Layout */
 | 
			
		||||
.wy-side-nav-search, .wy-menu-vertical {
 | 
			
		||||
  width: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-side {
 | 
			
		||||
  z-index: 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  background-color: var(--color-bg-side-nav);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-scroll {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  overflow-y: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 66.5rem) {
 | 
			
		||||
  .wy-side-scroll {
 | 
			
		||||
      width:264px
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 50rem) {
 | 
			
		||||
  .wy-nav-side {
 | 
			
		||||
      flex-wrap: nowrap;
 | 
			
		||||
      position: fixed;
 | 
			
		||||
      width: 248px;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      flex-direction: column;
 | 
			
		||||
      border-right: 1px solid var(--color-border);
 | 
			
		||||
      align-items:flex-end
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 66.5rem) {
 | 
			
		||||
  .wy-nav-side {
 | 
			
		||||
      width: calc((100% - 1064px) / 2 + 264px);
 | 
			
		||||
      min-width:264px
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 50rem) {
 | 
			
		||||
  .wy-nav-content-wrap {
 | 
			
		||||
      position: relative;
 | 
			
		||||
      max-width: 800px;
 | 
			
		||||
      margin-left:248px
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 66.5rem) {
 | 
			
		||||
  .wy-nav-content-wrap {
 | 
			
		||||
      margin-left:calc((100% - 1064px) / 2 + 264px)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Colors */
 | 
			
		||||
body.wy-body-for-nav,
 | 
			
		||||
.wy-nav-content {
 | 
			
		||||
  background: var(--color-bg-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-side {
 | 
			
		||||
  border-right: 1px solid var(--color-border);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-nav-search, .wy-nav-top {
 | 
			
		||||
  background: var(--color-bg-side-nav);
 | 
			
		||||
  border-bottom: 1px solid var(--color-border);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-content-wrap {
 | 
			
		||||
  background: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-nav-search > a, .wy-nav-top a, .wy-nav-top i {
 | 
			
		||||
  color: var(--color-text-title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-nav-search > a:hover, .wy-nav-top a:hover {
 | 
			
		||||
  background: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-nav-search > div.version {
 | 
			
		||||
  color: var(--color-text-alt)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-nav-search > div[role="search"] {
 | 
			
		||||
  border-top: 1px solid var(--color-border);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li.toctree-l2.current>a, .wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l3.current>a, .wy-menu-vertical li.toctree-l3.current li.toctree-l4>a {
 | 
			
		||||
  background: var(--color-bg-side-nav);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .highlighted {
 | 
			
		||||
  background: #eedd85;
 | 
			
		||||
  box-shadow: 0 0 0 2px #eedd85;
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-side-nav-search input[type=text],
 | 
			
		||||
html.writer-html5 .rst-content table.docutils th {
 | 
			
		||||
  color: var(--color-text-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,
 | 
			
		||||
.wy-table-backed,
 | 
			
		||||
.wy-table-odd td,
 | 
			
		||||
.wy-table-striped tr:nth-child(2n-1) td {
 | 
			
		||||
  background-color: var(--color-bg-body-alt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content table.docutils,
 | 
			
		||||
.wy-table-bordered-all,
 | 
			
		||||
html.writer-html5 .rst-content table.docutils th,
 | 
			
		||||
.rst-content table.docutils td,
 | 
			
		||||
.wy-table-bordered-all td,
 | 
			
		||||
hr {
 | 
			
		||||
  border-color: var(--color-border) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
::selection {
 | 
			
		||||
  background: var(--color-text-selection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ridiculous rules are taken from sphinx_rtd */
 | 
			
		||||
.rst-content .admonition-title,
 | 
			
		||||
.wy-alert-title {
 | 
			
		||||
  color: var(--color-text-body-light);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .hint,
 | 
			
		||||
.rst-content .important,
 | 
			
		||||
.rst-content .tip,
 | 
			
		||||
.rst-content .wy-alert-success,
 | 
			
		||||
.wy-alert.wy-alert-success {
 | 
			
		||||
  background: var(--color-success-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .hint .admonition-title,
 | 
			
		||||
.rst-content .hint .wy-alert-title,
 | 
			
		||||
.rst-content .important .admonition-title,
 | 
			
		||||
.rst-content .important .wy-alert-title,
 | 
			
		||||
.rst-content .tip .admonition-title,
 | 
			
		||||
.rst-content .tip .wy-alert-title,
 | 
			
		||||
.rst-content .wy-alert-success .admonition-title,
 | 
			
		||||
.rst-content .wy-alert-success .wy-alert-title,
 | 
			
		||||
.wy-alert.wy-alert-success .rst-content .admonition-title,
 | 
			
		||||
.wy-alert.wy-alert-success .wy-alert-title {
 | 
			
		||||
  background-color: var(--color-success-title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .admonition-todo,
 | 
			
		||||
.rst-content .attention,
 | 
			
		||||
.rst-content .caution,
 | 
			
		||||
.rst-content .warning,
 | 
			
		||||
.rst-content .wy-alert-warning,
 | 
			
		||||
.wy-alert.wy-alert-warning {
 | 
			
		||||
  background: var(--color-warning-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .admonition-todo .admonition-title,
 | 
			
		||||
.rst-content .admonition-todo .wy-alert-title,
 | 
			
		||||
.rst-content .attention .admonition-title,
 | 
			
		||||
.rst-content .attention .wy-alert-title,
 | 
			
		||||
.rst-content .caution .admonition-title,
 | 
			
		||||
.rst-content .caution .wy-alert-title,
 | 
			
		||||
.rst-content .warning .admonition-title,
 | 
			
		||||
.rst-content .warning .wy-alert-title,
 | 
			
		||||
.rst-content .wy-alert-warning .admonition-title,
 | 
			
		||||
.rst-content .wy-alert-warning .wy-alert-title,
 | 
			
		||||
.rst-content .wy-alert.wy-alert-warning .admonition-title,
 | 
			
		||||
.wy-alert.wy-alert-warning .rst-content .admonition-title,
 | 
			
		||||
.wy-alert.wy-alert-warning .wy-alert-title {
 | 
			
		||||
  background: var(--color-warning-title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .danger,
 | 
			
		||||
.rst-content .error,
 | 
			
		||||
.rst-content .wy-alert-danger,
 | 
			
		||||
.wy-alert.wy-alert-danger {
 | 
			
		||||
  background: var(--color-danger-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .danger .admonition-title,
 | 
			
		||||
.rst-content .danger .wy-alert-title,
 | 
			
		||||
.rst-content .error .admonition-title,
 | 
			
		||||
.rst-content .error .wy-alert-title,
 | 
			
		||||
.rst-content .wy-alert-danger .admonition-title,
 | 
			
		||||
.rst-content .wy-alert-danger .wy-alert-title,
 | 
			
		||||
.wy-alert.wy-alert-danger .rst-content .admonition-title,
 | 
			
		||||
.wy-alert.wy-alert-danger .wy-alert-title {
 | 
			
		||||
  background: var(--color-danger-title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .note,
 | 
			
		||||
.rst-content .seealso,
 | 
			
		||||
.rst-content .wy-alert-info,
 | 
			
		||||
.wy-alert.wy-alert-info {
 | 
			
		||||
  background: var(--color-info-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .note .admonition-title,
 | 
			
		||||
.rst-content .note .wy-alert-title,
 | 
			
		||||
.rst-content .seealso .admonition-title,
 | 
			
		||||
.rst-content .seealso .wy-alert-title,
 | 
			
		||||
.rst-content .wy-alert-info .admonition-title,
 | 
			
		||||
.rst-content .wy-alert-info .wy-alert-title,
 | 
			
		||||
.wy-alert.wy-alert-info .rst-content .admonition-title,
 | 
			
		||||
.wy-alert.wy-alert-info .wy-alert-title {
 | 
			
		||||
  background: var(--color-info-title);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Links */
 | 
			
		||||
a, a:visited,
 | 
			
		||||
.wy-menu-vertical a,
 | 
			
		||||
a.icon.icon-home,
 | 
			
		||||
.wy-menu-vertical li.toctree-l1.current > a.current {
 | 
			
		||||
  color: var(--color-text-anchor);
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a:hover, .wy-breadcrumbs-aside a {
 | 
			
		||||
  color: var(--color-text-anchor); /* reset */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-versions a, .rst-versions .rst-current-version {
 | 
			
		||||
  color: #var(--color-text-anchor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-content a.reference, .wy-nav-content a:not([class]) {
 | 
			
		||||
  background-image: linear-gradient(var(--color-border) 0%, var(--color-border) 100%);
 | 
			
		||||
  background-repeat: repeat-x;
 | 
			
		||||
  background-position: 0 100%;
 | 
			
		||||
  background-size: 1px 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-content a.reference:hover, .wy-nav-content a:not([class]):hover {
 | 
			
		||||
  background-image: linear-gradient(rgba(114,83,237,0.45) 0%, rgba(114,83,237,0.45) 100%);
 | 
			
		||||
  background-size: 1px 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical a:hover,
 | 
			
		||||
.wy-menu-vertical li.current a:hover,
 | 
			
		||||
.wy-menu-vertical a:active {
 | 
			
		||||
  background: var(--color-bg-side-nav-hover) !important;
 | 
			
		||||
  color: var(--color-text-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li.toctree-l1.current>a,
 | 
			
		||||
.wy-menu-vertical li.current>a,
 | 
			
		||||
.wy-menu-vertical li.on a {
 | 
			
		||||
  background-color: var(--color-bg-side-nav-hover);
 | 
			
		||||
  border: none;
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li.current {
 | 
			
		||||
  background-color: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li.current a {
 | 
			
		||||
  border-right: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li.toctree-l2 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l3 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l4 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l5 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l6 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l7 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l8 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l9 a,
 | 
			
		||||
.wy-menu-vertical li.toctree-l10 a {
 | 
			
		||||
  color: var(--color-text-body);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a.image-reference, a.image-reference:hover {
 | 
			
		||||
  background: none !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a.image-reference img {
 | 
			
		||||
  cursor: zoom-in;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Code blocks */
 | 
			
		||||
.rst-content code, .rst-content tt, code {
 | 
			
		||||
  padding: 0.25em;
 | 
			
		||||
  font-weight: 400;
 | 
			
		||||
  background-color: var(--color-bg-code-block);
 | 
			
		||||
  border: 1px solid var(--color-border);
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content div[class^=highlight], .rst-content pre.literal-block {
 | 
			
		||||
  padding: 0.7rem;
 | 
			
		||||
  margin-top: 0;
 | 
			
		||||
  margin-bottom: 0.75rem;
 | 
			
		||||
  overflow-x: auto;
 | 
			
		||||
  background-color: var(--color-bg-side-nav);
 | 
			
		||||
  border-color: var(--color-border);
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  box-shadow: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .admonition-title,
 | 
			
		||||
.rst-content div.admonition,
 | 
			
		||||
.wy-alert-title {
 | 
			
		||||
  padding: 10px 12px;
 | 
			
		||||
  border-top-left-radius: 4px;
 | 
			
		||||
  border-top-right-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.highlight .go {
 | 
			
		||||
  color: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.highlight .nt {
 | 
			
		||||
  color: var(--color-text-code-nt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content code.literal,
 | 
			
		||||
.rst-content tt.literal,
 | 
			
		||||
html.writer-html5 .rst-content dl.footnote code {
 | 
			
		||||
  border-color: var(--color-border);
 | 
			
		||||
  background-color: var(--color-border);
 | 
			
		||||
  color: var(--color-text-code-inline)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Search */
 | 
			
		||||
.wy-side-nav-search input[type=text] {
 | 
			
		||||
  border: none;
 | 
			
		||||
  border-radius: 0;
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  font-family: inherit;
 | 
			
		||||
  font-size: .85rem;
 | 
			
		||||
  box-shadow: none;
 | 
			
		||||
  padding: .7rem 1rem .7rem 2.8rem;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#rtd-search-form {
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#rtd-search-form:before {
 | 
			
		||||
  font: normal normal normal 14px/1 FontAwesome;
 | 
			
		||||
  font-size: inherit;
 | 
			
		||||
  text-rendering: auto;
 | 
			
		||||
  -webkit-font-smoothing: antialiased;
 | 
			
		||||
  -moz-osx-font-smoothing: grayscale;
 | 
			
		||||
  content: "\f002";
 | 
			
		||||
  color: var(--color-text-alt);
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  left: 1.5rem;
 | 
			
		||||
  top: .7rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Side nav */
 | 
			
		||||
.wy-side-nav-search {
 | 
			
		||||
  padding: 1rem 0 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li a button.toctree-expand {
 | 
			
		||||
  float: right;
 | 
			
		||||
  margin-right: -1.5em;
 | 
			
		||||
  padding: 0 .5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical a,
 | 
			
		||||
.wy-menu-vertical li.current>a,
 | 
			
		||||
.wy-menu-vertical li.current li>a {
 | 
			
		||||
  padding-right: 1.5em !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-menu-vertical li.current li>a.current {
 | 
			
		||||
  font-weight: 600;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Misc spacing */
 | 
			
		||||
.rst-content .admonition-title, .wy-alert-title {
 | 
			
		||||
  padding: 10px 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Buttons */
 | 
			
		||||
.btn {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  padding: 0.3em 1em;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  font-family: inherit;
 | 
			
		||||
  font-size: inherit;
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
  line-height: 1.5;
 | 
			
		||||
  color: #var(--color-text-anchor);
 | 
			
		||||
  text-decoration: none;
 | 
			
		||||
  vertical-align: baseline;
 | 
			
		||||
  background-color: #f7f7f7;
 | 
			
		||||
  border-width: 0;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  box-shadow: 0 1px 2px rgba(0,0,0,0.12),0 3px 10px rgba(0,0,0,0.08);
 | 
			
		||||
  appearance: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn:active {
 | 
			
		||||
  padding: 0.3em 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .btn:focus {
 | 
			
		||||
  outline: 1px solid #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content .btn-neutral, .rst-content .btn span.fa {
 | 
			
		||||
  color: var(--color-text-body) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-neutral {
 | 
			
		||||
  background-color: var(--color-btn-neutral-bg) !important;
 | 
			
		||||
  color: var(--color-btn-neutral-text) !important;
 | 
			
		||||
  border: 1px solid var(--color-btn-neutral-bg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn:hover, .btn-neutral:hover {
 | 
			
		||||
  background-color: var(--color-btn-neutral-bg-hover) !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Icon overrides */
 | 
			
		||||
.wy-side-nav-search a.icon-home:before {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before {
 | 
			
		||||
  content: "\f106"; /* fa-angle-up */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.fa-plus-square-o:before, .wy-menu-vertical li button.toctree-expand:before {
 | 
			
		||||
  content: "\f107"; /* fa-angle-down */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Misc */
 | 
			
		||||
.wy-nav-top {
 | 
			
		||||
  line-height: 36px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-top > i {
 | 
			
		||||
  font-size: 24px;
 | 
			
		||||
  padding: 8px 0 0 2px;
 | 
			
		||||
  color:#var(--color-text-anchor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rst-content table.docutils td,
 | 
			
		||||
.rst-content table.docutils th,
 | 
			
		||||
.rst-content table.field-list td,
 | 
			
		||||
.rst-content table.field-list th,
 | 
			
		||||
.wy-table td,
 | 
			
		||||
.wy-table th {
 | 
			
		||||
  padding: 8px 14px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark-mode-toggle {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 14px;
 | 
			
		||||
  right: 12px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
  width: 24px;
 | 
			
		||||
  z-index: 10;
 | 
			
		||||
  border: none;
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  color: inherit;
 | 
			
		||||
  opacity: 0.7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.wy-nav-content-wrap {
 | 
			
		||||
  z-index: 20;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										47
									
								
								docs/_static/js/darkmode.js
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -1,47 +0,0 @@
 | 
			
		||||
let toggleButton
 | 
			
		||||
let icon
 | 
			
		||||
 | 
			
		||||
function load() {
 | 
			
		||||
	'use strict'
 | 
			
		||||
 | 
			
		||||
	toggleButton = document.createElement('button')
 | 
			
		||||
	toggleButton.setAttribute('title', 'Toggle dark mode')
 | 
			
		||||
	toggleButton.classList.add('dark-mode-toggle')
 | 
			
		||||
	icon = document.createElement('i')
 | 
			
		||||
	icon.classList.add('fa', darkModeState ? 'fa-sun-o' : 'fa-moon-o')
 | 
			
		||||
	toggleButton.appendChild(icon)
 | 
			
		||||
	document.body.prepend(toggleButton)
 | 
			
		||||
 | 
			
		||||
	// Listen for changes in the OS settings
 | 
			
		||||
	// addListener is used because older versions of Safari don't support addEventListener
 | 
			
		||||
	// prefersDarkQuery set in <head>
 | 
			
		||||
	if (prefersDarkQuery) {
 | 
			
		||||
		prefersDarkQuery.addListener(function (evt) {
 | 
			
		||||
			toggleDarkMode(evt.matches)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Initial setting depending on the prefers-color-mode or localstorage
 | 
			
		||||
	// darkModeState should be set in the document <head> to prevent flash
 | 
			
		||||
	if (darkModeState == undefined) darkModeState = false
 | 
			
		||||
	toggleDarkMode(darkModeState)
 | 
			
		||||
 | 
			
		||||
	// Toggles the "dark-mode" class on click and sets localStorage state
 | 
			
		||||
	toggleButton.addEventListener('click', () => {
 | 
			
		||||
		darkModeState = !darkModeState
 | 
			
		||||
 | 
			
		||||
		toggleDarkMode(darkModeState)
 | 
			
		||||
		localStorage.setItem('dark-mode', darkModeState)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleDarkMode(state) {
 | 
			
		||||
	document.documentElement.classList.toggle('dark-mode', state)
 | 
			
		||||
	document.documentElement.classList.toggle('light-mode', !state)
 | 
			
		||||
	icon.classList.remove('fa-sun-o')
 | 
			
		||||
	icon.classList.remove('fa-moon-o')
 | 
			
		||||
	icon.classList.add(state ? 'fa-sun-o' : 'fa-moon-o')
 | 
			
		||||
	darkModeState = state
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.addEventListener('DOMContentLoaded', load)
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								docs/_static/screenshots/mail-rules-edited.png
									
									
									
									
										vendored
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 96 KiB  | 
							
								
								
									
										13
									
								
								docs/_templates/layout.html
									
									
									
									
										vendored
									
									
								
							
							
						
						@ -1,13 +0,0 @@
 | 
			
		||||
{% extends "!layout.html" %}
 | 
			
		||||
{% block extrahead %}
 | 
			
		||||
    <script>
 | 
			
		||||
        // MediaQueryList object
 | 
			
		||||
        const prefersDarkQuery = window.matchMedia("(prefers-color-scheme: dark)");
 | 
			
		||||
        const lsDark = localStorage.getItem("dark-mode");
 | 
			
		||||
        let darkModeState = lsDark !== null ? lsDark == "true" : prefersDarkQuery.matches;
 | 
			
		||||
 | 
			
		||||
        document.documentElement.classList.toggle("dark-mode", darkModeState);
 | 
			
		||||
        document.documentElement.classList.toggle("light-mode", !darkModeState);
 | 
			
		||||
    </script>
 | 
			
		||||
    {{ super() }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
							
								
								
									
										503
									
								
								docs/administration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,503 @@
 | 
			
		||||
# Administration
 | 
			
		||||
 | 
			
		||||
## Making backups {#backup}
 | 
			
		||||
 | 
			
		||||
Multiple options exist for making backups of your paperless instance,
 | 
			
		||||
depending on how you installed paperless.
 | 
			
		||||
 | 
			
		||||
Before making backups, make sure that paperless is not running.
 | 
			
		||||
 | 
			
		||||
Options available to any installation of paperless:
 | 
			
		||||
 | 
			
		||||
- Use the [document exporter](/administration#exporter). The document exporter exports all your documents,
 | 
			
		||||
  thumbnails and metadata to a specific folder. You may import your
 | 
			
		||||
  documents into a fresh instance of paperless again or store your
 | 
			
		||||
  documents in another DMS with this export.
 | 
			
		||||
- The document exporter is also able to update an already existing
 | 
			
		||||
  export. Therefore, incremental backups with `rsync` are entirely
 | 
			
		||||
  possible.
 | 
			
		||||
 | 
			
		||||
!!! caution
 | 
			
		||||
 | 
			
		||||
    You cannot import the export generated with one version of paperless in
 | 
			
		||||
    a different version of paperless. The export contains an exact image of
 | 
			
		||||
    the database, and migrations may change the database layout.
 | 
			
		||||
 | 
			
		||||
Options available to docker installations:
 | 
			
		||||
 | 
			
		||||
- Backup the docker volumes. These usually reside within
 | 
			
		||||
  `/var/lib/docker/volumes` on the host and you need to be root in
 | 
			
		||||
  order to access them.
 | 
			
		||||
 | 
			
		||||
  Paperless uses 4 volumes:
 | 
			
		||||
 | 
			
		||||
  - `paperless_media`: This is where your documents are stored.
 | 
			
		||||
  - `paperless_data`: This is where auxillary data is stored. This
 | 
			
		||||
    folder also contains the SQLite database, if you use it.
 | 
			
		||||
  - `paperless_pgdata`: Exists only if you use PostgreSQL and
 | 
			
		||||
    contains the database.
 | 
			
		||||
  - `paperless_dbdata`: Exists only if you use MariaDB and contains
 | 
			
		||||
    the database.
 | 
			
		||||
 | 
			
		||||
Options available to bare-metal and non-docker installations:
 | 
			
		||||
 | 
			
		||||
- Backup the entire paperless folder. This ensures that if your
 | 
			
		||||
  paperless instance crashes at some point or your disk fails, you can
 | 
			
		||||
  simply copy the folder back into place and it works.
 | 
			
		||||
 | 
			
		||||
  When using PostgreSQL or MariaDB, you'll also have to backup the
 | 
			
		||||
  database.
 | 
			
		||||
 | 
			
		||||
### Restoring {#migrating-restoring}
 | 
			
		||||
 | 
			
		||||
## Updating Paperless {#updating}
 | 
			
		||||
 | 
			
		||||
### Docker Route
 | 
			
		||||
 | 
			
		||||
If a new release of paperless-ngx is available, upgrading depends on how
 | 
			
		||||
you installed paperless-ngx in the first place. The releases are
 | 
			
		||||
available at the [release
 | 
			
		||||
page](https://github.com/paperless-ngx/paperless-ngx/releases).
 | 
			
		||||
 | 
			
		||||
First of all, ensure that paperless is stopped.
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ cd /path/to/paperless
 | 
			
		||||
$ docker-compose down
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After that, [make a backup](#backup).
 | 
			
		||||
 | 
			
		||||
A. If you pull the image from the docker hub, all you need to do is:
 | 
			
		||||
 | 
			
		||||
    ``` shell-session
 | 
			
		||||
    $ docker-compose pull
 | 
			
		||||
    $ docker-compose up
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    The docker-compose files refer to the `latest` version, which is
 | 
			
		||||
    always the latest stable release.
 | 
			
		||||
 | 
			
		||||
B. If you built the image yourself, do the following:
 | 
			
		||||
 | 
			
		||||
    ``` shell-session
 | 
			
		||||
    $ git pull
 | 
			
		||||
    $ docker-compose build
 | 
			
		||||
    $ docker-compose up
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
Running `docker-compose up` will also apply any new database migrations.
 | 
			
		||||
If you see everything working, press CTRL+C once to gracefully stop
 | 
			
		||||
paperless. Then you can start paperless-ngx with `-d` to have it run in
 | 
			
		||||
the background.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    In version 0.9.14, the update process was changed. In 0.9.13 and
 | 
			
		||||
    earlier, the docker-compose files specified exact versions and pull
 | 
			
		||||
    won't automatically update to newer versions. In order to enable
 | 
			
		||||
    updates as described above, either get the new `docker-compose.yml`
 | 
			
		||||
    file from
 | 
			
		||||
    [here](https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose)
 | 
			
		||||
    or edit the `docker-compose.yml` file, find the line that says
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    image: ghcr.io/paperless-ngx/paperless-ngx:0.9.x
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    and replace the version with `latest`:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    In version 1.7.1 and onwards, the Docker image can now be pinned to a
 | 
			
		||||
    release series. This is often combined with automatic updaters such as
 | 
			
		||||
    Watchtower to allow safer unattended upgrading to new bugfix releases
 | 
			
		||||
    only. It is still recommended to always review release notes before
 | 
			
		||||
    upgrading. To pin your install to a release series, edit the
 | 
			
		||||
    `docker-compose.yml` find the line that says
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    and replace the version with the series you want to track, for
 | 
			
		||||
    example:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    image: ghcr.io/paperless-ngx/paperless-ngx:1.7
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
### Bare Metal Route
 | 
			
		||||
 | 
			
		||||
After grabbing the new release and unpacking the contents, do the
 | 
			
		||||
following:
 | 
			
		||||
 | 
			
		||||
1.  Update dependencies. New paperless version may require additional
 | 
			
		||||
    dependencies. The dependencies required are listed in the section
 | 
			
		||||
    about
 | 
			
		||||
    [bare metal installations](/setup#bare_metal).
 | 
			
		||||
 | 
			
		||||
2.  Update python requirements. Keep in mind to activate your virtual
 | 
			
		||||
    environment before that, if you use one.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ pip install -r requirements.txt
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
3.  Migrate the database.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ cd src
 | 
			
		||||
    $ python3 manage.py migrate
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    This might not actually do anything. Not every new paperless version
 | 
			
		||||
    comes with new database migrations.
 | 
			
		||||
 | 
			
		||||
## Downgrading Paperless
 | 
			
		||||
 | 
			
		||||
Downgrades are possible. However, some updates also contain database
 | 
			
		||||
migrations (these change the layout of the database and may move data).
 | 
			
		||||
In order to move back from a version that applied database migrations,
 | 
			
		||||
you'll have to revert the database migration _before_ downgrading, and
 | 
			
		||||
then downgrade paperless.
 | 
			
		||||
 | 
			
		||||
This table lists the compatible versions for each database migration
 | 
			
		||||
number.
 | 
			
		||||
 | 
			
		||||
| Migration number | Version range   |
 | 
			
		||||
| ---------------- | --------------- |
 | 
			
		||||
| 1011             | 1.0.0           |
 | 
			
		||||
| 1012             | 1.1.0 - 1.2.1   |
 | 
			
		||||
| 1014             | 1.3.0 - 1.3.1   |
 | 
			
		||||
| 1016             | 1.3.2 - current |
 | 
			
		||||
 | 
			
		||||
Execute the following management command to migrate your database:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ python3 manage.py migrate documents <migration number>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    Some migrations cannot be undone. The command will issue errors if that
 | 
			
		||||
    happens.
 | 
			
		||||
 | 
			
		||||
## Management utilities {#management-commands}
 | 
			
		||||
 | 
			
		||||
Paperless comes with some management commands that perform various
 | 
			
		||||
maintenance tasks on your paperless instance. You can invoke these
 | 
			
		||||
commands in the following way:
 | 
			
		||||
 | 
			
		||||
With docker-compose, while paperless is running:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ cd /path/to/paperless
 | 
			
		||||
$ docker-compose exec webserver <command> <arguments>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
With docker, while paperless is running:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ docker exec -it <container-name> <command> <arguments>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Bare metal:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ cd /path/to/paperless/src
 | 
			
		||||
$ python3 manage.py <command> <arguments>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
All commands have built-in help, which can be accessed by executing them
 | 
			
		||||
with the argument `--help`.
 | 
			
		||||
 | 
			
		||||
### Document exporter {#exporter}
 | 
			
		||||
 | 
			
		||||
The document exporter exports all your data from paperless into a folder
 | 
			
		||||
for backup or migration to another DMS.
 | 
			
		||||
 | 
			
		||||
If you use the document exporter within a cronjob to backup your data
 | 
			
		||||
you might use the `-T` flag behind exec to suppress "The input device
 | 
			
		||||
is not a TTY" errors. For example:
 | 
			
		||||
`docker-compose exec -T webserver document_exporter ../export`
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_exporter target [-c] [-f] [-d]
 | 
			
		||||
 | 
			
		||||
optional arguments:
 | 
			
		||||
-c, --compare-checksums
 | 
			
		||||
-f, --use-filename-format
 | 
			
		||||
-d, --delete
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`target` is a folder to which the data gets written. This includes
 | 
			
		||||
documents, thumbnails and a `manifest.json` file. The manifest contains
 | 
			
		||||
all metadata from the database (correspondents, tags, etc).
 | 
			
		||||
 | 
			
		||||
When you use the provided docker compose script, specify `../export` as
 | 
			
		||||
the target. This path inside the container is automatically mounted on
 | 
			
		||||
your host on the folder `export`.
 | 
			
		||||
 | 
			
		||||
If the target directory already exists and contains files, paperless
 | 
			
		||||
will assume that the contents of the export directory are a previous
 | 
			
		||||
export and will attempt to update the previous export. Paperless will
 | 
			
		||||
only export changed and added files. Paperless determines whether a file
 | 
			
		||||
has changed by inspecting the file attributes "date/time modified" and
 | 
			
		||||
"size". If that does not work out for you, specify
 | 
			
		||||
`--compare-checksums` and paperless will attempt to compare file
 | 
			
		||||
checksums instead. This is slower.
 | 
			
		||||
 | 
			
		||||
Paperless will not remove any existing files in the export directory. If
 | 
			
		||||
you want paperless to also remove files that do not belong to the
 | 
			
		||||
current export such as files from deleted documents, specify `--delete`.
 | 
			
		||||
Be careful when pointing paperless to a directory that already contains
 | 
			
		||||
other files.
 | 
			
		||||
 | 
			
		||||
The filenames generated by this command follow the format
 | 
			
		||||
`[date created] [correspondent] [title].[extension]`. If you want
 | 
			
		||||
paperless to use `PAPERLESS_FILENAME_FORMAT` for exported filenames
 | 
			
		||||
instead, specify `--use-filename-format`.
 | 
			
		||||
 | 
			
		||||
### Document importer {#importer}
 | 
			
		||||
 | 
			
		||||
The document importer takes the export produced by the [Document
 | 
			
		||||
exporter](#document-exporter) and imports it into paperless.
 | 
			
		||||
 | 
			
		||||
The importer works just like the exporter. You point it at a directory,
 | 
			
		||||
and the script does the rest of the work:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_importer source
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When you use the provided docker compose script, put the export inside
 | 
			
		||||
the `export` folder in your paperless source directory. Specify
 | 
			
		||||
`../export` as the `source`.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    Importing from a previous version of Paperless may work, but for best
 | 
			
		||||
    results it is suggested to match the versions.
 | 
			
		||||
 | 
			
		||||
### Document retagger {#retagger}
 | 
			
		||||
 | 
			
		||||
Say you've imported a few hundred documents and now want to introduce a
 | 
			
		||||
tag or set up a new correspondent, and apply its matching to all of the
 | 
			
		||||
currently-imported docs. This problem is common enough that there are
 | 
			
		||||
tools for it.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_retagger [-h] [-c] [-T] [-t] [-i] [--use-first] [-f]
 | 
			
		||||
 | 
			
		||||
optional arguments:
 | 
			
		||||
-c, --correspondent
 | 
			
		||||
-T, --tags
 | 
			
		||||
-t, --document_type
 | 
			
		||||
-s, --storage_path
 | 
			
		||||
-i, --inbox-only
 | 
			
		||||
--use-first
 | 
			
		||||
-f, --overwrite
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Run this after changing or adding matching rules. It'll loop over all
 | 
			
		||||
of the documents in your database and attempt to match documents
 | 
			
		||||
according to the new rules.
 | 
			
		||||
 | 
			
		||||
Specify any combination of `-c`, `-T`, `-t` and `-s` to have the
 | 
			
		||||
retagger perform matching of the specified metadata type. If you don't
 | 
			
		||||
specify any of these options, the document retagger won't do anything.
 | 
			
		||||
 | 
			
		||||
Specify `-i` to have the document retagger work on documents tagged with
 | 
			
		||||
inbox tags only. This is useful when you don't want to mess with your
 | 
			
		||||
already processed documents.
 | 
			
		||||
 | 
			
		||||
When multiple document types or correspondents match a single document,
 | 
			
		||||
the retagger won't assign these to the document. Specify `--use-first`
 | 
			
		||||
to override this behavior and just use the first correspondent or type
 | 
			
		||||
it finds. This option does not apply to tags, since any amount of tags
 | 
			
		||||
can be applied to a document.
 | 
			
		||||
 | 
			
		||||
Finally, `-f` specifies that you wish to overwrite already assigned
 | 
			
		||||
correspondents, types and/or tags. The default behavior is to not assign
 | 
			
		||||
correspondents and types to documents that have this data already
 | 
			
		||||
assigned. `-f` works differently for tags: By default, only additional
 | 
			
		||||
tags get added to documents, no tags will be removed. With `-f`, tags
 | 
			
		||||
that don't match a document anymore get removed as well.
 | 
			
		||||
 | 
			
		||||
### Managing the Automatic matching algorithm
 | 
			
		||||
 | 
			
		||||
The _Auto_ matching algorithm requires a trained neural network to work.
 | 
			
		||||
This network needs to be updated whenever somethings in your data
 | 
			
		||||
changes. The docker image takes care of that automatically with the task
 | 
			
		||||
scheduler. You can manually renew the classifier by invoking the
 | 
			
		||||
following management command:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_create_classifier
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This command takes no arguments.
 | 
			
		||||
 | 
			
		||||
### Managing the document search index {#index}
 | 
			
		||||
 | 
			
		||||
The document search index is responsible for delivering search results
 | 
			
		||||
for the website. The document index is automatically updated whenever
 | 
			
		||||
documents get added to, changed, or removed from paperless. However, if
 | 
			
		||||
the search yields non-existing documents or won't find anything, you
 | 
			
		||||
may need to recreate the index manually.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_index {reindex,optimize}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Specify `reindex` to have the index created from scratch. This may take
 | 
			
		||||
some time.
 | 
			
		||||
 | 
			
		||||
Specify `optimize` to optimize the index. This updates certain aspects
 | 
			
		||||
of the index and usually makes queries faster and also ensures that the
 | 
			
		||||
autocompletion works properly. This command is regularly invoked by the
 | 
			
		||||
task scheduler.
 | 
			
		||||
 | 
			
		||||
### Managing filenames {#renamer}
 | 
			
		||||
 | 
			
		||||
If you use paperless' feature to
 | 
			
		||||
[assign custom filenames to your documents](/advanced_usage#file_name_handling), you can use this command to move all your files after
 | 
			
		||||
changing the naming scheme.
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    Since this command moves your documents, it is advised to do a backup
 | 
			
		||||
    beforehand. The renaming logic is robust and will never overwrite or
 | 
			
		||||
    delete a file, but you can't ever be careful enough.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_renamer
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The command takes no arguments and processes all your documents at once.
 | 
			
		||||
 | 
			
		||||
Learn how to use
 | 
			
		||||
`Management Utilities<utilities-management-commands>`{.interpreted-text
 | 
			
		||||
role="ref"}.
 | 
			
		||||
 | 
			
		||||
### Sanity checker {#sanity-checker}
 | 
			
		||||
 | 
			
		||||
Paperless has a built-in sanity checker that inspects your document
 | 
			
		||||
collection for issues.
 | 
			
		||||
 | 
			
		||||
The issues detected by the sanity checker are as follows:
 | 
			
		||||
 | 
			
		||||
- Missing original files.
 | 
			
		||||
- Missing archive files.
 | 
			
		||||
- Inaccessible original files due to improper permissions.
 | 
			
		||||
- Inaccessible archive files due to improper permissions.
 | 
			
		||||
- Corrupted original documents by comparing their checksum against
 | 
			
		||||
  what is stored in the database.
 | 
			
		||||
- Corrupted archive documents by comparing their checksum against what
 | 
			
		||||
  is stored in the database.
 | 
			
		||||
- Missing thumbnails.
 | 
			
		||||
- Inaccessible thumbnails due to improper permissions.
 | 
			
		||||
- Documents without any content (warning).
 | 
			
		||||
- Orphaned files in the media directory (warning). These are files
 | 
			
		||||
  that are not referenced by any document im paperless.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_sanity_checker
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The command takes no arguments. Depending on the size of your document
 | 
			
		||||
archive, this may take some time.
 | 
			
		||||
 | 
			
		||||
### Fetching e-mail
 | 
			
		||||
 | 
			
		||||
Paperless automatically fetches your e-mail every 10 minutes by default.
 | 
			
		||||
If you want to invoke the email consumer manually, call the following
 | 
			
		||||
management command:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
mail_fetcher
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The command takes no arguments and processes all your mail accounts and
 | 
			
		||||
rules.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    As of October 2022 Microsoft no longer supports IMAP authentication
 | 
			
		||||
    for Exchange servers, thus Exchange is no longer supported until a
 | 
			
		||||
    solution is implemented in the Python IMAP library used by Paperless.
 | 
			
		||||
    See
 | 
			
		||||
 | 
			
		||||
[learn.microsoft.com](https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online)
 | 
			
		||||
 | 
			
		||||
### Creating archived documents {#archiver}
 | 
			
		||||
 | 
			
		||||
Paperless stores archived PDF/A documents alongside your original
 | 
			
		||||
documents. These archived documents will also contain selectable text
 | 
			
		||||
for image-only originals. These documents are derived from the
 | 
			
		||||
originals, which are always stored unmodified. If coming from an earlier
 | 
			
		||||
version of paperless, your documents won't have archived versions.
 | 
			
		||||
 | 
			
		||||
This command creates PDF/A documents for your documents.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
document_archiver --overwrite --document <id>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This command will only attempt to create archived documents when no
 | 
			
		||||
archived document exists yet, unless `--overwrite` is specified. If
 | 
			
		||||
`--document <id>` is specified, the archiver will only process that
 | 
			
		||||
document.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    This command essentially performs OCR on all your documents again,
 | 
			
		||||
    according to your settings. If you run this with
 | 
			
		||||
    `PAPERLESS_OCR_MODE=redo`, it will potentially run for a very long time.
 | 
			
		||||
    You can cancel the command at any time, since this command will skip
 | 
			
		||||
    already archived versions the next time it is run.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    Some documents will cause errors and cannot be converted into PDF/A
 | 
			
		||||
    documents, such as encrypted PDF documents. The archiver will skip over
 | 
			
		||||
    these documents each time it sees them.
 | 
			
		||||
 | 
			
		||||
### Managing encryption {#encyption}
 | 
			
		||||
 | 
			
		||||
Documents can be stored in Paperless using GnuPG encryption.
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    Encryption is deprecated since paperless-ngx 0.9 and doesn't really
 | 
			
		||||
    provide any additional security, since you have to store the passphrase
 | 
			
		||||
    in a configuration file on the same system as the encrypted documents
 | 
			
		||||
    for paperless to work. Furthermore, the entire text content of the
 | 
			
		||||
    documents is stored plain in the database, even if your documents are
 | 
			
		||||
    encrypted. Filenames are not encrypted as well.
 | 
			
		||||
 | 
			
		||||
    Also, the web server provides transparent access to your encrypted
 | 
			
		||||
    documents.
 | 
			
		||||
 | 
			
		||||
    Consider running paperless on an encrypted filesystem instead, which
 | 
			
		||||
    will then at least provide security against physical hardware theft.
 | 
			
		||||
 | 
			
		||||
#### Enabling encryption
 | 
			
		||||
 | 
			
		||||
Enabling encryption is no longer supported.
 | 
			
		||||
 | 
			
		||||
#### Disabling encryption
 | 
			
		||||
 | 
			
		||||
Basic usage to disable encryption of your document store:
 | 
			
		||||
 | 
			
		||||
(Note: If `PAPERLESS_PASSPHRASE` isn't set already, you need to specify
 | 
			
		||||
it here)
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
decrypt_documents [--passphrase SECR3TP4SSPHRA$E]
 | 
			
		||||
```
 | 
			
		||||
@ -1,531 +0,0 @@
 | 
			
		||||
 | 
			
		||||
**************
 | 
			
		||||
Administration
 | 
			
		||||
**************
 | 
			
		||||
 | 
			
		||||
.. _administration-backup:
 | 
			
		||||
 | 
			
		||||
Making backups
 | 
			
		||||
##############
 | 
			
		||||
 | 
			
		||||
Multiple options exist for making backups of your paperless instance,
 | 
			
		||||
depending on how you installed paperless.
 | 
			
		||||
 | 
			
		||||
Before making backups, make sure that paperless is not running.
 | 
			
		||||
 | 
			
		||||
Options available to any installation of paperless:
 | 
			
		||||
 | 
			
		||||
*   Use the :ref:`document exporter <utilities-exporter>`.
 | 
			
		||||
    The document exporter exports all your documents, thumbnails and
 | 
			
		||||
    metadata to a specific folder. You may import your documents into a
 | 
			
		||||
    fresh instance of paperless again or store your documents in another
 | 
			
		||||
    DMS with this export.
 | 
			
		||||
*   The document exporter is also able to update an already existing export.
 | 
			
		||||
    Therefore, incremental backups with ``rsync`` are entirely possible.
 | 
			
		||||
 | 
			
		||||
.. caution::
 | 
			
		||||
 | 
			
		||||
    You cannot import the export generated with one version of paperless in a
 | 
			
		||||
    different version of paperless. The export contains an exact image of the
 | 
			
		||||
    database, and migrations may change the database layout.
 | 
			
		||||
 | 
			
		||||
Options available to docker installations:
 | 
			
		||||
 | 
			
		||||
*   Backup the docker volumes. These usually reside within
 | 
			
		||||
    ``/var/lib/docker/volumes`` on the host and you need to be root in order
 | 
			
		||||
    to access them.
 | 
			
		||||
 | 
			
		||||
    Paperless uses 4 volumes:
 | 
			
		||||
 | 
			
		||||
    *   ``paperless_media``: This is where your documents are stored.
 | 
			
		||||
    *   ``paperless_data``: This is where auxillary data is stored. This
 | 
			
		||||
        folder also contains the SQLite database, if you use it.
 | 
			
		||||
    *   ``paperless_pgdata``: Exists only if you use PostgreSQL and contains
 | 
			
		||||
        the database.
 | 
			
		||||
    *   ``paperless_dbdata``: Exists only if you use MariaDB and contains
 | 
			
		||||
        the database.
 | 
			
		||||
 | 
			
		||||
Options available to bare-metal and non-docker installations:
 | 
			
		||||
 | 
			
		||||
*   Backup the entire paperless folder. This ensures that if your paperless instance
 | 
			
		||||
    crashes at some point or your disk fails, you can simply copy the folder back
 | 
			
		||||
    into place and it works.
 | 
			
		||||
 | 
			
		||||
    When using PostgreSQL or MariaDB, you'll also have to backup the database.
 | 
			
		||||
 | 
			
		||||
.. _migrating-restoring:
 | 
			
		||||
 | 
			
		||||
Restoring
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
.. _administration-updating:
 | 
			
		||||
 | 
			
		||||
Updating Paperless
 | 
			
		||||
##################
 | 
			
		||||
 | 
			
		||||
Docker Route
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
If a new release of paperless-ngx is available, upgrading depends on how you
 | 
			
		||||
installed paperless-ngx in the first place. The releases are available at the
 | 
			
		||||
`release page <https://github.com/paperless-ngx/paperless-ngx/releases>`_.
 | 
			
		||||
 | 
			
		||||
First of all, ensure that paperless is stopped.
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ cd /path/to/paperless
 | 
			
		||||
    $ docker-compose down
 | 
			
		||||
 | 
			
		||||
After that, :ref:`make a backup <administration-backup>`.
 | 
			
		||||
 | 
			
		||||
A.  If you pull the image from the docker hub, all you need to do is:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ docker-compose pull
 | 
			
		||||
        $ docker-compose up
 | 
			
		||||
 | 
			
		||||
    The docker-compose files refer to the ``latest`` version, which is always the latest
 | 
			
		||||
    stable release.
 | 
			
		||||
 | 
			
		||||
B.  If you built the image yourself, do the following:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ git pull
 | 
			
		||||
        $ docker-compose build
 | 
			
		||||
        $ docker-compose up
 | 
			
		||||
 | 
			
		||||
Running ``docker-compose up`` will also apply any new database migrations.
 | 
			
		||||
If you see everything working, press CTRL+C once to gracefully stop paperless.
 | 
			
		||||
Then you can start paperless-ngx with ``-d`` to have it run in the background.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        In version 0.9.14, the update process was changed. In 0.9.13 and earlier, the
 | 
			
		||||
        docker-compose files specified exact versions and pull won't automatically
 | 
			
		||||
        update to newer versions. In order to enable updates as described above, either
 | 
			
		||||
        get the new ``docker-compose.yml`` file from `here <https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose>`_
 | 
			
		||||
        or edit the ``docker-compose.yml`` file, find the line that says
 | 
			
		||||
 | 
			
		||||
            .. code::
 | 
			
		||||
 | 
			
		||||
                image: ghcr.io/paperless-ngx/paperless-ngx:0.9.x
 | 
			
		||||
 | 
			
		||||
        and replace the version with ``latest``:
 | 
			
		||||
 | 
			
		||||
            .. code::
 | 
			
		||||
 | 
			
		||||
                image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
        In version 1.7.1 and onwards, the Docker image can now be pinned to a release series.
 | 
			
		||||
        This is often combined with automatic updaters such as Watchtower to allow safer
 | 
			
		||||
        unattended upgrading to new bugfix releases only.  It is still recommended to always
 | 
			
		||||
        review release notes before upgrading.  To pin your install to a release series, edit
 | 
			
		||||
        the ``docker-compose.yml`` find the line that says
 | 
			
		||||
 | 
			
		||||
            .. code::
 | 
			
		||||
 | 
			
		||||
                image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
 | 
			
		||||
        and replace the version with the series you want to track, for example:
 | 
			
		||||
 | 
			
		||||
            .. code::
 | 
			
		||||
 | 
			
		||||
                image: ghcr.io/paperless-ngx/paperless-ngx:1.7
 | 
			
		||||
 | 
			
		||||
Bare Metal Route
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
After grabbing the new release and unpacking the contents, do the following:
 | 
			
		||||
 | 
			
		||||
1.  Update dependencies. New paperless version may require additional
 | 
			
		||||
    dependencies. The dependencies required are listed in the section about
 | 
			
		||||
    :ref:`bare metal installations <setup-bare_metal>`.
 | 
			
		||||
 | 
			
		||||
2.  Update python requirements. Keep in mind to activate your virtual environment
 | 
			
		||||
    before that, if you use one.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ pip install -r requirements.txt
 | 
			
		||||
 | 
			
		||||
3.  Migrate the database.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ cd src
 | 
			
		||||
        $ python3 manage.py migrate
 | 
			
		||||
 | 
			
		||||
    This might not actually do anything. Not every new paperless version comes with new
 | 
			
		||||
    database migrations.
 | 
			
		||||
 | 
			
		||||
Downgrading Paperless
 | 
			
		||||
#####################
 | 
			
		||||
 | 
			
		||||
Downgrades are possible. However, some updates also contain database migrations (these change the layout of the database and may move data).
 | 
			
		||||
In order to move back from a version that applied database migrations, you'll have to revert the database migration *before* downgrading,
 | 
			
		||||
and then downgrade paperless.
 | 
			
		||||
 | 
			
		||||
This table lists the compatible versions for each database migration number.
 | 
			
		||||
 | 
			
		||||
+------------------+-----------------+
 | 
			
		||||
| Migration number | Version range   |
 | 
			
		||||
+------------------+-----------------+
 | 
			
		||||
| 1011             | 1.0.0           |
 | 
			
		||||
+------------------+-----------------+
 | 
			
		||||
| 1012             | 1.1.0 - 1.2.1   |
 | 
			
		||||
+------------------+-----------------+
 | 
			
		||||
| 1014             | 1.3.0 - 1.3.1   |
 | 
			
		||||
+------------------+-----------------+
 | 
			
		||||
| 1016             | 1.3.2 - current |
 | 
			
		||||
+------------------+-----------------+
 | 
			
		||||
 | 
			
		||||
Execute the following management command to migrate your database:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ python3 manage.py migrate documents <migration number>
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    Some migrations cannot be undone. The command will issue errors if that happens.
 | 
			
		||||
 | 
			
		||||
.. _utilities-management-commands:
 | 
			
		||||
 | 
			
		||||
Management utilities
 | 
			
		||||
####################
 | 
			
		||||
 | 
			
		||||
Paperless comes with some management commands that perform various maintenance
 | 
			
		||||
tasks on your paperless instance. You can invoke these commands in the following way:
 | 
			
		||||
 | 
			
		||||
With docker-compose, while paperless is running:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ cd /path/to/paperless
 | 
			
		||||
    $ docker-compose exec webserver <command> <arguments>
 | 
			
		||||
 | 
			
		||||
With docker, while paperless is running:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ docker exec -it <container-name> <command> <arguments>
 | 
			
		||||
 | 
			
		||||
Bare metal:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ cd /path/to/paperless/src
 | 
			
		||||
    $ python3 manage.py <command> <arguments>
 | 
			
		||||
 | 
			
		||||
All commands have built-in help, which can be accessed by executing them with
 | 
			
		||||
the argument ``--help``.
 | 
			
		||||
 | 
			
		||||
.. _utilities-exporter:
 | 
			
		||||
 | 
			
		||||
Document exporter
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
The document exporter exports all your data from paperless into a folder for
 | 
			
		||||
backup or migration to another DMS.
 | 
			
		||||
 | 
			
		||||
If you use the document exporter within a cronjob to backup your data you might use the ``-T`` flag behind exec to suppress "The input device is not a TTY" errors. For example: ``docker-compose exec -T webserver document_exporter ../export``
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_exporter target [-c] [-f] [-d]
 | 
			
		||||
 | 
			
		||||
    optional arguments:
 | 
			
		||||
    -c, --compare-checksums
 | 
			
		||||
    -f, --use-filename-format
 | 
			
		||||
    -d, --delete
 | 
			
		||||
 | 
			
		||||
``target`` is a folder to which the data gets written. This includes documents,
 | 
			
		||||
thumbnails and a ``manifest.json`` file. The manifest contains all metadata from
 | 
			
		||||
the database (correspondents, tags, etc).
 | 
			
		||||
 | 
			
		||||
When you use the provided docker compose script, specify ``../export`` as the
 | 
			
		||||
target. This path inside the container is automatically mounted on your host on
 | 
			
		||||
the folder ``export``.
 | 
			
		||||
 | 
			
		||||
If the target directory already exists and contains files, paperless will assume
 | 
			
		||||
that the contents of the export directory are a previous export and will attempt
 | 
			
		||||
to update the previous export. Paperless will only export changed and added files.
 | 
			
		||||
Paperless determines whether a file has changed by inspecting the file attributes
 | 
			
		||||
"date/time modified" and "size". If that does not work out for you, specify
 | 
			
		||||
``--compare-checksums`` and paperless will attempt to compare file checksums instead.
 | 
			
		||||
This is slower.
 | 
			
		||||
 | 
			
		||||
Paperless will not remove any existing files in the export directory. If you want
 | 
			
		||||
paperless to also remove files that do not belong to the current export such as files
 | 
			
		||||
from deleted documents, specify ``--delete``. Be careful when pointing paperless to
 | 
			
		||||
a directory that already contains other files.
 | 
			
		||||
 | 
			
		||||
The filenames generated by this command follow the format
 | 
			
		||||
``[date created] [correspondent] [title].[extension]``.
 | 
			
		||||
If you want paperless to use ``PAPERLESS_FILENAME_FORMAT`` for exported filenames
 | 
			
		||||
instead, specify ``--use-filename-format``.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _utilities-importer:
 | 
			
		||||
 | 
			
		||||
Document importer
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
The document importer takes the export produced by the `Document exporter`_ and
 | 
			
		||||
imports it into paperless.
 | 
			
		||||
 | 
			
		||||
The importer works just like the exporter.  You point it at a directory, and
 | 
			
		||||
the script does the rest of the work:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_importer source
 | 
			
		||||
 | 
			
		||||
When you use the provided docker compose script, put the export inside the
 | 
			
		||||
``export`` folder in your paperless source directory. Specify ``../export``
 | 
			
		||||
as the ``source``.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    Importing from a previous version of Paperless may work, but for best results
 | 
			
		||||
    it is suggested to match the versions.
 | 
			
		||||
 | 
			
		||||
.. _utilities-retagger:
 | 
			
		||||
 | 
			
		||||
Document retagger
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
Say you've imported a few hundred documents and now want to introduce
 | 
			
		||||
a tag or set up a new correspondent, and apply its matching to all of
 | 
			
		||||
the currently-imported docs. This problem is common enough that
 | 
			
		||||
there are tools for it.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_retagger [-h] [-c] [-T] [-t] [-i] [--use-first] [-f]
 | 
			
		||||
 | 
			
		||||
    optional arguments:
 | 
			
		||||
    -c, --correspondent
 | 
			
		||||
    -T, --tags
 | 
			
		||||
    -t, --document_type
 | 
			
		||||
    -s, --storage_path
 | 
			
		||||
    -i, --inbox-only
 | 
			
		||||
    --use-first
 | 
			
		||||
    -f, --overwrite
 | 
			
		||||
 | 
			
		||||
Run this after changing or adding matching rules. It'll loop over all
 | 
			
		||||
of the documents in your database and attempt to match documents
 | 
			
		||||
according to the new rules.
 | 
			
		||||
 | 
			
		||||
Specify any combination of ``-c``, ``-T``, ``-t`` and ``-s`` to have the
 | 
			
		||||
retagger perform matching of the specified metadata type. If you don't
 | 
			
		||||
specify any of these options, the document retagger won't do anything.
 | 
			
		||||
 | 
			
		||||
Specify ``-i`` to have the document retagger work on documents tagged
 | 
			
		||||
with inbox tags only. This is useful when you don't want to mess with
 | 
			
		||||
your already processed documents.
 | 
			
		||||
 | 
			
		||||
When multiple document types or correspondents match a single document,
 | 
			
		||||
the retagger won't assign these to the document. Specify ``--use-first``
 | 
			
		||||
to override this behavior and just use the first correspondent or type
 | 
			
		||||
it finds. This option does not apply to tags, since any amount of tags
 | 
			
		||||
can be applied to a document.
 | 
			
		||||
 | 
			
		||||
Finally, ``-f`` specifies that you wish to overwrite already assigned
 | 
			
		||||
correspondents, types and/or tags. The default behavior is to not
 | 
			
		||||
assign correspondents and types to documents that have this data already
 | 
			
		||||
assigned. ``-f`` works differently for tags: By default, only additional tags get
 | 
			
		||||
added to documents, no tags will be removed. With ``-f``, tags that don't
 | 
			
		||||
match a document anymore get removed as well.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Managing the Automatic matching algorithm
 | 
			
		||||
=========================================
 | 
			
		||||
 | 
			
		||||
The *Auto* matching algorithm requires a trained neural network to work.
 | 
			
		||||
This network needs to be updated whenever somethings in your data
 | 
			
		||||
changes. The docker image takes care of that automatically with the task
 | 
			
		||||
scheduler. You can manually renew the classifier by invoking the following
 | 
			
		||||
management command:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_create_classifier
 | 
			
		||||
 | 
			
		||||
This command takes no arguments.
 | 
			
		||||
 | 
			
		||||
.. _`administration-index`:
 | 
			
		||||
 | 
			
		||||
Managing the document search index
 | 
			
		||||
==================================
 | 
			
		||||
 | 
			
		||||
The document search index is responsible for delivering search results for the
 | 
			
		||||
website. The document index is automatically updated whenever documents get
 | 
			
		||||
added to, changed, or removed from paperless. However, if the search yields
 | 
			
		||||
non-existing documents or won't find anything, you may need to recreate the
 | 
			
		||||
index manually.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_index {reindex,optimize}
 | 
			
		||||
 | 
			
		||||
Specify ``reindex`` to have the index created from scratch. This may take some
 | 
			
		||||
time.
 | 
			
		||||
 | 
			
		||||
Specify ``optimize`` to optimize the index. This updates certain aspects of
 | 
			
		||||
the index and usually makes queries faster and also ensures that the
 | 
			
		||||
autocompletion works properly. This command is regularly invoked by the task
 | 
			
		||||
scheduler.
 | 
			
		||||
 | 
			
		||||
.. _utilities-renamer:
 | 
			
		||||
 | 
			
		||||
Managing filenames
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
If you use paperless' feature to
 | 
			
		||||
:ref:`assign custom filenames to your documents <advanced-file_name_handling>`,
 | 
			
		||||
you can use this command to move all your files after changing
 | 
			
		||||
the naming scheme.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    Since this command moves your documents, it is advised to do
 | 
			
		||||
    a backup beforehand. The renaming logic is robust and will never overwrite
 | 
			
		||||
    or delete a file, but you can't ever be careful enough.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_renamer
 | 
			
		||||
 | 
			
		||||
The command takes no arguments and processes all your documents at once.
 | 
			
		||||
 | 
			
		||||
Learn how to use :ref:`Management Utilities<utilities-management-commands>`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _utilities-sanity-checker:
 | 
			
		||||
 | 
			
		||||
Sanity checker
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
Paperless has a built-in sanity checker that inspects your document collection for issues.
 | 
			
		||||
 | 
			
		||||
The issues detected by the sanity checker are as follows:
 | 
			
		||||
 | 
			
		||||
* Missing original files.
 | 
			
		||||
* Missing archive files.
 | 
			
		||||
* Inaccessible original files due to improper permissions.
 | 
			
		||||
* Inaccessible archive files due to improper permissions.
 | 
			
		||||
* Corrupted original documents by comparing their checksum against what is stored in the database.
 | 
			
		||||
* Corrupted archive documents by comparing their checksum against what is stored in the database.
 | 
			
		||||
* Missing thumbnails.
 | 
			
		||||
* Inaccessible thumbnails due to improper permissions.
 | 
			
		||||
* Documents without any content (warning).
 | 
			
		||||
* Orphaned files in the media directory (warning). These are files that are not referenced by any document im paperless.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_sanity_checker
 | 
			
		||||
 | 
			
		||||
The command takes no arguments. Depending on the size of your document archive, this may take some time.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Fetching e-mail
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
Paperless automatically fetches your e-mail every 10 minutes by default. If
 | 
			
		||||
you want to invoke the email consumer manually, call the following management
 | 
			
		||||
command:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    mail_fetcher
 | 
			
		||||
 | 
			
		||||
The command takes no arguments and processes all your mail accounts and rules.
 | 
			
		||||
 | 
			
		||||
 .. note::
 | 
			
		||||
 | 
			
		||||
    As of October 2022 Microsoft no longer supports IMAP authentication for Exchange
 | 
			
		||||
    servers, thus Exchange is no longer supported until a solution is implemented in
 | 
			
		||||
    the Python IMAP library used by Paperless. See  `learn.microsoft.com`_
 | 
			
		||||
 | 
			
		||||
.. _learn.microsoft.com: https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online
 | 
			
		||||
 | 
			
		||||
.. _utilities-archiver:
 | 
			
		||||
 | 
			
		||||
Creating archived documents
 | 
			
		||||
===========================
 | 
			
		||||
 | 
			
		||||
Paperless stores archived PDF/A documents alongside your original documents.
 | 
			
		||||
These archived documents will also contain selectable text for image-only
 | 
			
		||||
originals.
 | 
			
		||||
These documents are derived from the originals, which are always stored
 | 
			
		||||
unmodified. If coming from an earlier version of paperless, your documents
 | 
			
		||||
won't have archived versions.
 | 
			
		||||
 | 
			
		||||
This command creates PDF/A documents for your documents.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    document_archiver --overwrite --document <id>
 | 
			
		||||
 | 
			
		||||
This command will only attempt to create archived documents when no archived
 | 
			
		||||
document exists yet, unless ``--overwrite`` is specified. If ``--document <id>``
 | 
			
		||||
is specified, the archiver will only process that document.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    This command essentially performs OCR on all your documents again,
 | 
			
		||||
    according to your settings. If you run this with ``PAPERLESS_OCR_MODE=redo``,
 | 
			
		||||
    it will potentially run for a very long time. You can cancel the command
 | 
			
		||||
    at any time, since this command will skip already archived versions the next time
 | 
			
		||||
    it is run.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    Some documents will cause errors and cannot be converted into PDF/A documents,
 | 
			
		||||
    such as encrypted PDF documents. The archiver will skip over these documents
 | 
			
		||||
    each time it sees them.
 | 
			
		||||
 | 
			
		||||
.. _utilities-encyption:
 | 
			
		||||
 | 
			
		||||
Managing encryption
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
Documents can be stored in Paperless using GnuPG encryption.
 | 
			
		||||
 | 
			
		||||
.. danger::
 | 
			
		||||
 | 
			
		||||
    Encryption is deprecated since paperless-ngx 0.9 and doesn't really provide any
 | 
			
		||||
    additional security, since you have to store the passphrase in a configuration
 | 
			
		||||
    file on the same system as the encrypted documents for paperless to work.
 | 
			
		||||
    Furthermore, the entire text content of the documents is stored plain in the
 | 
			
		||||
    database, even if your documents are encrypted. Filenames are not encrypted as
 | 
			
		||||
    well.
 | 
			
		||||
 | 
			
		||||
    Also, the web server provides transparent access to your encrypted documents.
 | 
			
		||||
 | 
			
		||||
    Consider running paperless on an encrypted filesystem instead, which will then
 | 
			
		||||
    at least provide security against physical hardware theft.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Enabling encryption
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
Enabling encryption is no longer supported.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Disabling encryption
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
Basic usage to disable encryption of your document store:
 | 
			
		||||
 | 
			
		||||
(Note: If ``PAPERLESS_PASSPHRASE`` isn't set already, you need to specify it here)
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    decrypt_documents [--passphrase SECR3TP4SSPHRA$E]
 | 
			
		||||
							
								
								
									
										468
									
								
								docs/advanced_usage.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,468 @@
 | 
			
		||||
# Advanced Topics
 | 
			
		||||
 | 
			
		||||
Paperless offers a couple features that automate certain tasks and make
 | 
			
		||||
your life easier.
 | 
			
		||||
 | 
			
		||||
## Matching tags, correspondents, document types, and storage paths {#matching}
 | 
			
		||||
 | 
			
		||||
Paperless will compare the matching algorithms defined by every tag,
 | 
			
		||||
correspondent, document type, and storage path in your database to see
 | 
			
		||||
if they apply to the text in a document. In other words, if you define a
 | 
			
		||||
tag called `Home Utility` that had a `match` property of `bc hydro` and
 | 
			
		||||
a `matching_algorithm` of `literal`, Paperless will automatically tag
 | 
			
		||||
your newly-consumed document with your `Home Utility` tag so long as the
 | 
			
		||||
text `bc hydro` appears in the body of the document somewhere.
 | 
			
		||||
 | 
			
		||||
The matching logic is quite powerful. It supports searching the text of
 | 
			
		||||
your document with different algorithms, and as such, some
 | 
			
		||||
experimentation may be necessary to get things right.
 | 
			
		||||
 | 
			
		||||
In order to have a tag, correspondent, document type, or storage path
 | 
			
		||||
assigned automatically to newly consumed documents, assign a match and
 | 
			
		||||
matching algorithm using the web interface. These settings define when
 | 
			
		||||
to assign tags, correspondents, document types, and storage paths to
 | 
			
		||||
documents.
 | 
			
		||||
 | 
			
		||||
The following algorithms are available:
 | 
			
		||||
 | 
			
		||||
- **Any:** Looks for any occurrence of any word provided in match in
 | 
			
		||||
  the PDF. If you define the match as `Bank1 Bank2`, it will match
 | 
			
		||||
  documents containing either of these terms.
 | 
			
		||||
- **All:** Requires that every word provided appears in the PDF,
 | 
			
		||||
  albeit not in the order provided.
 | 
			
		||||
- **Literal:** Matches only if the match appears exactly as provided
 | 
			
		||||
  (i.e. preserve ordering) in the PDF.
 | 
			
		||||
- **Regular expression:** Parses the match as a regular expression and
 | 
			
		||||
  tries to find a match within the document.
 | 
			
		||||
- **Fuzzy match:** I don't know. Look at the source.
 | 
			
		||||
- **Auto:** Tries to automatically match new documents. This does not
 | 
			
		||||
  require you to set a match. See the notes below.
 | 
			
		||||
 | 
			
		||||
When using the _any_ or _all_ matching algorithms, you can search for
 | 
			
		||||
terms that consist of multiple words by enclosing them in double quotes.
 | 
			
		||||
For example, defining a match text of `"Bank of America" BofA` using the
 | 
			
		||||
_any_ algorithm, will match documents that contain either "Bank of
 | 
			
		||||
America" or "BofA", but will not match documents containing "Bank of
 | 
			
		||||
South America".
 | 
			
		||||
 | 
			
		||||
Then just save your tag, correspondent, document type, or storage path
 | 
			
		||||
and run another document through the consumer. Once complete, you should
 | 
			
		||||
see the newly-created document, automatically tagged with the
 | 
			
		||||
appropriate data.
 | 
			
		||||
 | 
			
		||||
### Automatic matching {#automatic_matching}
 | 
			
		||||
 | 
			
		||||
Paperless-ngx comes with a new matching algorithm called _Auto_. This
 | 
			
		||||
matching algorithm tries to assign tags, correspondents, document types,
 | 
			
		||||
and storage paths to your documents based on how you have already
 | 
			
		||||
assigned these on existing documents. It uses a neural network under the
 | 
			
		||||
hood.
 | 
			
		||||
 | 
			
		||||
If, for example, all your bank statements of your account 123 at the
 | 
			
		||||
Bank of America are tagged with the tag "bofa*123" and the matching
 | 
			
		||||
algorithm of this tag is set to \_Auto*, this neural network will examine
 | 
			
		||||
your documents and automatically learn when to assign this tag.
 | 
			
		||||
 | 
			
		||||
Paperless tries to hide much of the involved complexity with this
 | 
			
		||||
approach. However, there are a couple caveats you need to keep in mind
 | 
			
		||||
when using this feature:
 | 
			
		||||
 | 
			
		||||
- Changes to your documents are not immediately reflected by the
 | 
			
		||||
  matching algorithm. The neural network needs to be _trained_ on your
 | 
			
		||||
  documents after changes. Paperless periodically (default: once each
 | 
			
		||||
  hour) checks for changes and does this automatically for you.
 | 
			
		||||
- The Auto matching algorithm only takes documents into account which
 | 
			
		||||
  are NOT placed in your inbox (i.e. have any inbox tags assigned to
 | 
			
		||||
  them). This ensures that the neural network only learns from
 | 
			
		||||
  documents which you have correctly tagged before.
 | 
			
		||||
- The matching algorithm can only work if there is a correlation
 | 
			
		||||
  between the tag, correspondent, document type, or storage path and
 | 
			
		||||
  the document itself. Your bank statements usually contain your bank
 | 
			
		||||
  account number and the name of the bank, so this works reasonably
 | 
			
		||||
  well, However, tags such as "TODO" cannot be automatically
 | 
			
		||||
  assigned.
 | 
			
		||||
- The matching algorithm needs a reasonable number of documents to
 | 
			
		||||
  identify when to assign tags, correspondents, storage paths, and
 | 
			
		||||
  types. If one out of a thousand documents has the correspondent
 | 
			
		||||
  "Very obscure web shop I bought something five years ago", it will
 | 
			
		||||
  probably not assign this correspondent automatically if you buy
 | 
			
		||||
  something from them again. The more documents, the better.
 | 
			
		||||
- Paperless also needs a reasonable amount of negative examples to
 | 
			
		||||
  decide when not to assign a certain tag, correspondent, document
 | 
			
		||||
  type, or storage path. This will usually be the case as you start
 | 
			
		||||
  filling up paperless with documents. Example: If all your documents
 | 
			
		||||
  are either from "Webshop" and "Bank", paperless will assign one
 | 
			
		||||
  of these correspondents to ANY new document, if both are set to
 | 
			
		||||
  automatic matching.
 | 
			
		||||
 | 
			
		||||
## Hooking into the consumption process
 | 
			
		||||
 | 
			
		||||
Sometimes you may want to do something arbitrary whenever a document is
 | 
			
		||||
consumed. Rather than try to predict what you may want to do, Paperless
 | 
			
		||||
lets you execute scripts of your own choosing just before or after a
 | 
			
		||||
document is consumed using a couple simple hooks.
 | 
			
		||||
 | 
			
		||||
Just write a script, put it somewhere that Paperless can read & execute,
 | 
			
		||||
and then put the path to that script in `paperless.conf` or
 | 
			
		||||
`docker-compose.env` with the variable name of either
 | 
			
		||||
`PAPERLESS_PRE_CONSUME_SCRIPT` or `PAPERLESS_POST_CONSUME_SCRIPT`.
 | 
			
		||||
 | 
			
		||||
!!! info
 | 
			
		||||
 | 
			
		||||
    These scripts are executed in a **blocking** process, which means that
 | 
			
		||||
    if a script takes a long time to run, it can significantly slow down
 | 
			
		||||
    your document consumption flow. If you want things to run
 | 
			
		||||
    asynchronously, you'll have to fork the process in your script and
 | 
			
		||||
    exit.
 | 
			
		||||
 | 
			
		||||
### Pre-consumption script
 | 
			
		||||
 | 
			
		||||
Executed after the consumer sees a new document in the consumption
 | 
			
		||||
folder, but before any processing of the document is performed. This
 | 
			
		||||
script can access the following relevant environment variables set:
 | 
			
		||||
 | 
			
		||||
- `DOCUMENT_SOURCE_PATH`
 | 
			
		||||
 | 
			
		||||
A simple but common example for this would be creating a simple script
 | 
			
		||||
like this:
 | 
			
		||||
 | 
			
		||||
`/usr/local/bin/ocr-pdf`
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
pdf2pdfocr.py -i ${DOCUMENT_SOURCE_PATH}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`/etc/paperless.conf`
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
...
 | 
			
		||||
PAPERLESS_PRE_CONSUME_SCRIPT="/usr/local/bin/ocr-pdf"
 | 
			
		||||
...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This will pass the path to the document about to be consumed to
 | 
			
		||||
`/usr/local/bin/ocr-pdf`, which will in turn call
 | 
			
		||||
[pdf2pdfocr.py](https://github.com/LeoFCardoso/pdf2pdfocr) on your
 | 
			
		||||
document, which will then overwrite the file with an OCR'd version of
 | 
			
		||||
the file and exit. At which point, the consumption process will begin
 | 
			
		||||
with the newly modified file.
 | 
			
		||||
 | 
			
		||||
The script's stdout and stderr will be logged line by line to the
 | 
			
		||||
webserver log, along with the exit code of the script.
 | 
			
		||||
 | 
			
		||||
### Post-consumption script {#post_consume_script}
 | 
			
		||||
 | 
			
		||||
Executed after the consumer has successfully processed a document and
 | 
			
		||||
has moved it into paperless. It receives the following environment
 | 
			
		||||
variables:
 | 
			
		||||
 | 
			
		||||
- `DOCUMENT_ID`
 | 
			
		||||
- `DOCUMENT_FILE_NAME`
 | 
			
		||||
- `DOCUMENT_CREATED`
 | 
			
		||||
- `DOCUMENT_MODIFIED`
 | 
			
		||||
- `DOCUMENT_ADDED`
 | 
			
		||||
- `DOCUMENT_SOURCE_PATH`
 | 
			
		||||
- `DOCUMENT_ARCHIVE_PATH`
 | 
			
		||||
- `DOCUMENT_THUMBNAIL_PATH`
 | 
			
		||||
- `DOCUMENT_DOWNLOAD_URL`
 | 
			
		||||
- `DOCUMENT_THUMBNAIL_URL`
 | 
			
		||||
- `DOCUMENT_CORRESPONDENT`
 | 
			
		||||
- `DOCUMENT_TAGS`
 | 
			
		||||
- `DOCUMENT_ORIGINAL_FILENAME`
 | 
			
		||||
 | 
			
		||||
The script can be in any language, but for a simple shell script
 | 
			
		||||
example, you can take a look at
 | 
			
		||||
[post-consumption-example.sh](https://github.com/paperless-ngx/paperless-ngx/blob/main/scripts/post-consumption-example.sh)
 | 
			
		||||
in this project.
 | 
			
		||||
 | 
			
		||||
The post consumption script cannot cancel the consumption process.
 | 
			
		||||
 | 
			
		||||
The script's stdout and stderr will be logged line by line to the
 | 
			
		||||
webserver log, along with the exit code of the script.
 | 
			
		||||
 | 
			
		||||
#### Docker
 | 
			
		||||
 | 
			
		||||
Assumed you have
 | 
			
		||||
`/home/foo/paperless-ngx/scripts/post-consumption-example.sh`.
 | 
			
		||||
 | 
			
		||||
You can pass that script into the consumer container via a host mount in
 | 
			
		||||
your `docker-compose.yml`.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
...
 | 
			
		||||
consumer:
 | 
			
		||||
  ...
 | 
			
		||||
  volumes:
 | 
			
		||||
    ...
 | 
			
		||||
    - /home/paperless-ngx/scripts:/path/in/container/scripts/
 | 
			
		||||
...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Example (docker-compose.yml):
 | 
			
		||||
`- /home/foo/paperless-ngx/scripts:/usr/src/paperless/scripts`
 | 
			
		||||
 | 
			
		||||
which in turn requires the variable `PAPERLESS_POST_CONSUME_SCRIPT` in
 | 
			
		||||
`docker-compose.env` to point to
 | 
			
		||||
`/path/in/container/scripts/post-consumption-example.sh`.
 | 
			
		||||
 | 
			
		||||
Example (docker-compose.env):
 | 
			
		||||
`PAPERLESS_POST_CONSUME_SCRIPT=/usr/src/paperless/scripts/post-consumption-example.sh`
 | 
			
		||||
 | 
			
		||||
Troubleshooting:
 | 
			
		||||
 | 
			
		||||
- Monitor the docker-compose log
 | 
			
		||||
  `cd ~/paperless-ngx; docker-compose logs -f`
 | 
			
		||||
- Check your script's permission e.g. in case of permission error
 | 
			
		||||
  `sudo chmod 755 post-consumption-example.sh`
 | 
			
		||||
- Pipe your scripts's output to a log file e.g.
 | 
			
		||||
  `echo "${DOCUMENT_ID}" | tee --append /usr/src/paperless/scripts/post-consumption-example.log`
 | 
			
		||||
 | 
			
		||||
## File name handling {#file_name_handling}
 | 
			
		||||
 | 
			
		||||
By default, paperless stores your documents in the media directory and
 | 
			
		||||
renames them using the identifier which it has assigned to each
 | 
			
		||||
document. You will end up getting files like `0000123.pdf` in your media
 | 
			
		||||
directory. This isn't necessarily a bad thing, because you normally
 | 
			
		||||
don't have to access these files manually. However, if you wish to name
 | 
			
		||||
your files differently, you can do that by adjusting the
 | 
			
		||||
`PAPERLESS_FILENAME_FORMAT` configuration option. Paperless adds the
 | 
			
		||||
correct file extension e.g. `.pdf`, `.jpg` automatically.
 | 
			
		||||
 | 
			
		||||
This variable allows you to configure the filename (folders are allowed)
 | 
			
		||||
using placeholders. For example, configuring this to
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
PAPERLESS_FILENAME_FORMAT={created_year}/{correspondent}/{title}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
will create a directory structure as follows:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
2019/
 | 
			
		||||
  My bank/
 | 
			
		||||
    Statement January.pdf
 | 
			
		||||
    Statement February.pdf
 | 
			
		||||
2020/
 | 
			
		||||
  My bank/
 | 
			
		||||
    Statement January.pdf
 | 
			
		||||
    Letter.pdf
 | 
			
		||||
    Letter_01.pdf
 | 
			
		||||
  Shoe store/
 | 
			
		||||
    My new shoes.pdf
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    Do not manually move your files in the media folder. Paperless remembers
 | 
			
		||||
    the last filename a document was stored as. If you do rename a file,
 | 
			
		||||
    paperless will report your files as missing and won't be able to find
 | 
			
		||||
    them.
 | 
			
		||||
 | 
			
		||||
Paperless provides the following placeholders within filenames:
 | 
			
		||||
 | 
			
		||||
- `{asn}`: The archive serial number of the document, or "none".
 | 
			
		||||
- `{correspondent}`: The name of the correspondent, or "none".
 | 
			
		||||
- `{document_type}`: The name of the document type, or "none".
 | 
			
		||||
- `{tag_list}`: A comma separated list of all tags assigned to the
 | 
			
		||||
  document.
 | 
			
		||||
- `{title}`: The title of the document.
 | 
			
		||||
- `{created}`: The full date (ISO format) the document was created.
 | 
			
		||||
- `{created_year}`: Year created only, formatted as the year with
 | 
			
		||||
  century.
 | 
			
		||||
- `{created_year_short}`: Year created only, formatted as the year
 | 
			
		||||
  without century, zero padded.
 | 
			
		||||
- `{created_month}`: Month created only (number 01-12).
 | 
			
		||||
- `{created_month_name}`: Month created name, as per locale
 | 
			
		||||
- `{created_month_name_short}`: Month created abbreviated name, as per
 | 
			
		||||
  locale
 | 
			
		||||
- `{created_day}`: Day created only (number 01-31).
 | 
			
		||||
- `{added}`: The full date (ISO format) the document was added to
 | 
			
		||||
  paperless.
 | 
			
		||||
- `{added_year}`: Year added only.
 | 
			
		||||
- `{added_year_short}`: Year added only, formatted as the year without
 | 
			
		||||
  century, zero padded.
 | 
			
		||||
- `{added_month}`: Month added only (number 01-12).
 | 
			
		||||
- `{added_month_name}`: Month added name, as per locale
 | 
			
		||||
- `{added_month_name_short}`: Month added abbreviated name, as per
 | 
			
		||||
  locale
 | 
			
		||||
- `{added_day}`: Day added only (number 01-31).
 | 
			
		||||
 | 
			
		||||
Paperless will try to conserve the information from your database as
 | 
			
		||||
much as possible. However, some characters that you can use in document
 | 
			
		||||
titles and correspondent names (such as `: \ /` and a couple more) are
 | 
			
		||||
not allowed in filenames and will be replaced with dashes.
 | 
			
		||||
 | 
			
		||||
If paperless detects that two documents share the same filename,
 | 
			
		||||
paperless will automatically append `_01`, `_02`, etc to the filename.
 | 
			
		||||
This happens if all the placeholders in a filename evaluate to the same
 | 
			
		||||
value.
 | 
			
		||||
 | 
			
		||||
!!! tip
 | 
			
		||||
 | 
			
		||||
    You can affect how empty placeholders are treated by changing the
 | 
			
		||||
    following setting to [true]{.title-ref}.
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    PAPERLESS_FILENAME_FORMAT_REMOVE_NONE=True
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    Doing this results in all empty placeholders resolving to "" instead
 | 
			
		||||
    of "none" as stated above. Spaces before empty placeholders are
 | 
			
		||||
    removed as well, empty directories are omitted.
 | 
			
		||||
 | 
			
		||||
!!! tip
 | 
			
		||||
 | 
			
		||||
    Paperless checks the filename of a document whenever it is saved.
 | 
			
		||||
    Therefore, you need to update the filenames of your documents and move
 | 
			
		||||
    them after altering this setting by invoking the
 | 
			
		||||
    [`document renamer <utilities-renamer>`]().
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    Make absolutely sure you get the spelling of the placeholders right, or
 | 
			
		||||
    else paperless will use the default naming scheme instead.
 | 
			
		||||
 | 
			
		||||
!!! caution
 | 
			
		||||
 | 
			
		||||
    As of now, you could totally tell paperless to store your files anywhere
 | 
			
		||||
    outside the media directory by setting
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    PAPERLESS_FILENAME_FORMAT=../../my/custom/location/{title}
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    However, keep in mind that inside docker, if files get stored outside of
 | 
			
		||||
    the predefined volumes, they will be lost after a restart of paperless.
 | 
			
		||||
 | 
			
		||||
## Storage paths
 | 
			
		||||
 | 
			
		||||
One of the best things in Paperless is that you can not only access the
 | 
			
		||||
documents via the web interface, but also via the file system.
 | 
			
		||||
 | 
			
		||||
When as single storage layout is not sufficient for your use case,
 | 
			
		||||
storage paths come to the rescue. Storage paths allow you to configure
 | 
			
		||||
more precisely where each document is stored in the file system.
 | 
			
		||||
 | 
			
		||||
- Each storage path is a [PAPERLESS_FILENAME_FORMAT]{.title-ref} and
 | 
			
		||||
  follows the rules described above
 | 
			
		||||
- Each document is assigned a storage path using the matching
 | 
			
		||||
  algorithms described above, but can be overwritten at any time
 | 
			
		||||
 | 
			
		||||
For example, you could define the following two storage paths:
 | 
			
		||||
 | 
			
		||||
1.  Normal communications are put into a folder structure sorted by
 | 
			
		||||
    [year/correspondent]{.title-ref}
 | 
			
		||||
2.  Communications with insurance companies are stored in a flat
 | 
			
		||||
    structure with longer file names, but containing the full date of
 | 
			
		||||
    the correspondence.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
By Year = {created_year}/{correspondent}/{title}
 | 
			
		||||
Insurances = Insurances/{correspondent}/{created_year}-{created_month}-{created_day} {title}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If you then map these storage paths to the documents, you might get the
 | 
			
		||||
following result. For simplicity, [By Year]{.title-ref} defines the same
 | 
			
		||||
structure as in the previous example above.
 | 
			
		||||
 | 
			
		||||
```text
 | 
			
		||||
2019/                                   # By Year
 | 
			
		||||
   My bank/
 | 
			
		||||
     Statement January.pdf
 | 
			
		||||
     Statement February.pdf
 | 
			
		||||
 | 
			
		||||
 Insurances/                           # Insurances
 | 
			
		||||
   Healthcare 123/
 | 
			
		||||
     2022-01-01 Statement January.pdf
 | 
			
		||||
     2022-02-02 Letter.pdf
 | 
			
		||||
     2022-02-03 Letter.pdf
 | 
			
		||||
   Dental 456/
 | 
			
		||||
     2021-12-01 New Conditions.pdf
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
!!! tip
 | 
			
		||||
 | 
			
		||||
    Defining a storage path is optional. If no storage path is defined for a
 | 
			
		||||
    document, the global [PAPERLESS_FILENAME_FORMAT]{.title-ref} is applied.
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    If you adjust the format of an existing storage path, old documents
 | 
			
		||||
    don't get relocated automatically. You need to run the
 | 
			
		||||
    [document renamer](/administration#renamer) to
 | 
			
		||||
    adjust their pathes.
 | 
			
		||||
 | 
			
		||||
## Celery Monitoring {#celery-monitoring}
 | 
			
		||||
 | 
			
		||||
The monitoring tool
 | 
			
		||||
[Flower](https://flower.readthedocs.io/en/latest/index.html) can be used
 | 
			
		||||
to view more detailed information about the health of the celery workers
 | 
			
		||||
used for asynchronous tasks. This includes details on currently running,
 | 
			
		||||
queued and completed tasks, timing and more. Flower can also be used
 | 
			
		||||
with Prometheus, as it exports metrics. For details on its capabilities,
 | 
			
		||||
refer to the Flower documentation.
 | 
			
		||||
 | 
			
		||||
To configure Flower further, create a [flowerconfig.py]{.title-ref} and
 | 
			
		||||
place it into the [src/paperless]{.title-ref} directory. For a Docker
 | 
			
		||||
installation, you can use volumes to accomplish this:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
services:
 | 
			
		||||
  # ...
 | 
			
		||||
  webserver:
 | 
			
		||||
    # ...
 | 
			
		||||
    volumes:
 | 
			
		||||
      - /path/to/my/flowerconfig.py:/usr/src/paperless/src/paperless/flowerconfig.py:ro
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Custom Container Initialization
 | 
			
		||||
 | 
			
		||||
The Docker image includes the ability to run custom user scripts during
 | 
			
		||||
startup. This could be utilized for installing additional tools or
 | 
			
		||||
Python packages, for example.
 | 
			
		||||
 | 
			
		||||
To utilize this, mount a folder containing your scripts to the custom
 | 
			
		||||
initialization directory, [/custom-cont-init.d]{.title-ref} and place
 | 
			
		||||
scripts you wish to run inside. For security, the folder must be owned
 | 
			
		||||
by `root` and should have permissions of `a=rx`. Additionally, scripts
 | 
			
		||||
must only be writable by `root`.
 | 
			
		||||
 | 
			
		||||
Your scripts will be run directly before the webserver completes
 | 
			
		||||
startup. Scripts will be run by the [root]{.title-ref} user.
 | 
			
		||||
If you would like to switch users, the utility `gosu` is available and
 | 
			
		||||
preferred over `sudo`.
 | 
			
		||||
 | 
			
		||||
This is an advanced functionality with which you could break functionality
 | 
			
		||||
or lose data. If you experience issues, please disable any custom scripts
 | 
			
		||||
and try again before reporting an issue.
 | 
			
		||||
 | 
			
		||||
For example, using Docker Compose:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
services:
 | 
			
		||||
  # ...
 | 
			
		||||
  webserver:
 | 
			
		||||
    # ...
 | 
			
		||||
    volumes:
 | 
			
		||||
      - /path/to/my/scripts:/custom-cont-init.d:ro
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## MySQL Caveats {#mysql-caveats}
 | 
			
		||||
 | 
			
		||||
### Case Sensitivity
 | 
			
		||||
 | 
			
		||||
The database interface does not provide a method to configure a MySQL
 | 
			
		||||
database to be case sensitive. This would prevent a user from creating a
 | 
			
		||||
tag `Name` and `NAME` as they are considered the same.
 | 
			
		||||
 | 
			
		||||
Per Django documentation, to enable this requires manual intervention.
 | 
			
		||||
To enable case sensetive tables, you can execute the following command
 | 
			
		||||
against each table:
 | 
			
		||||
 | 
			
		||||
`ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`
 | 
			
		||||
 | 
			
		||||
You can also set the default for new tables (this does NOT affect
 | 
			
		||||
existing tables) with:
 | 
			
		||||
 | 
			
		||||
`ALTER DATABASE <db_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`
 | 
			
		||||
@ -1,451 +0,0 @@
 | 
			
		||||
***************
 | 
			
		||||
Advanced topics
 | 
			
		||||
***************
 | 
			
		||||
 | 
			
		||||
Paperless offers a couple features that automate certain tasks and make your life
 | 
			
		||||
easier.
 | 
			
		||||
 | 
			
		||||
.. _advanced-matching:
 | 
			
		||||
 | 
			
		||||
Matching tags, correspondents, document types, and storage paths
 | 
			
		||||
################################################################
 | 
			
		||||
 | 
			
		||||
Paperless will compare the matching algorithms defined by every tag, correspondent,
 | 
			
		||||
document type, and storage path in your database to see if they apply to the text
 | 
			
		||||
in a document. In other words, if you define a tag called ``Home Utility``
 | 
			
		||||
that had a ``match`` property of ``bc hydro`` and a ``matching_algorithm`` of
 | 
			
		||||
``literal``, Paperless will automatically tag your newly-consumed document with
 | 
			
		||||
your ``Home Utility`` tag so long as the text ``bc hydro`` appears in the body
 | 
			
		||||
of the document somewhere.
 | 
			
		||||
 | 
			
		||||
The matching logic is quite powerful. It supports searching the text of your
 | 
			
		||||
document with different algorithms, and as such, some experimentation may be
 | 
			
		||||
necessary to get things right.
 | 
			
		||||
 | 
			
		||||
In order to have a tag, correspondent, document type, or storage path assigned
 | 
			
		||||
automatically to newly consumed documents, assign a match and matching algorithm
 | 
			
		||||
using the web interface. These settings define when to assign tags, correspondents,
 | 
			
		||||
document types, and storage paths to documents.
 | 
			
		||||
 | 
			
		||||
The following algorithms are available:
 | 
			
		||||
 | 
			
		||||
* **Any:** Looks for any occurrence of any word provided in match in the PDF.
 | 
			
		||||
  If you define the match as ``Bank1 Bank2``, it will match documents containing
 | 
			
		||||
  either of these terms.
 | 
			
		||||
* **All:** Requires that every word provided appears in the PDF, albeit not in the
 | 
			
		||||
  order provided.
 | 
			
		||||
* **Literal:** Matches only if the match appears exactly as provided (i.e. preserve ordering) in the PDF.
 | 
			
		||||
* **Regular expression:** Parses the match as a regular expression and tries to
 | 
			
		||||
  find a match within the document.
 | 
			
		||||
* **Fuzzy match:** I don't know. Look at the source.
 | 
			
		||||
* **Auto:** Tries to automatically match new documents. This does not require you
 | 
			
		||||
  to set a match. See the notes below.
 | 
			
		||||
 | 
			
		||||
When using the *any* or *all* matching algorithms, you can search for terms
 | 
			
		||||
that consist of multiple words by enclosing them in double quotes. For example,
 | 
			
		||||
defining a match text of ``"Bank of America" BofA`` using the *any* algorithm,
 | 
			
		||||
will match documents that contain either "Bank of America" or "BofA", but will
 | 
			
		||||
not match documents containing "Bank of South America".
 | 
			
		||||
 | 
			
		||||
Then just save your tag, correspondent, document type, or storage path and run
 | 
			
		||||
another document through the consumer.  Once complete, you should see the
 | 
			
		||||
newly-created document, automatically tagged with the appropriate data.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _advanced-automatic_matching:
 | 
			
		||||
 | 
			
		||||
Automatic matching
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
Paperless-ngx comes with a new matching algorithm called *Auto*. This matching
 | 
			
		||||
algorithm tries to assign tags, correspondents, document types, and storage paths
 | 
			
		||||
to your documents based on how you have already assigned these on existing documents.
 | 
			
		||||
It uses a neural network under the hood.
 | 
			
		||||
 | 
			
		||||
If, for example, all your bank statements of your account 123 at the Bank of
 | 
			
		||||
America are tagged with the tag "bofa_123" and the matching algorithm of this
 | 
			
		||||
tag is set to *Auto*, this neural network will examine your documents and
 | 
			
		||||
automatically learn when to assign this tag.
 | 
			
		||||
 | 
			
		||||
Paperless tries to hide much of the involved complexity with this approach.
 | 
			
		||||
However, there are a couple caveats you need to keep in mind when using this
 | 
			
		||||
feature:
 | 
			
		||||
 | 
			
		||||
* Changes to your documents are not immediately reflected by the matching
 | 
			
		||||
  algorithm. The neural network needs to be *trained* on your documents after
 | 
			
		||||
  changes. Paperless periodically (default: once each hour) checks for changes
 | 
			
		||||
  and does this automatically for you.
 | 
			
		||||
* The Auto matching algorithm only takes documents into account which are NOT
 | 
			
		||||
  placed in your inbox (i.e. have any inbox tags assigned to them). This ensures
 | 
			
		||||
  that the neural network only learns from documents which you have correctly
 | 
			
		||||
  tagged before.
 | 
			
		||||
* The matching algorithm can only work if there is a correlation between the
 | 
			
		||||
  tag, correspondent, document type, or storage path and the document itself.
 | 
			
		||||
  Your bank statements usually contain your bank account number and the name
 | 
			
		||||
  of the bank, so this works reasonably well, However, tags such as "TODO"
 | 
			
		||||
  cannot be automatically assigned.
 | 
			
		||||
* The matching algorithm needs a reasonable number of documents to identify when
 | 
			
		||||
  to assign tags, correspondents, storage paths, and types. If one out of a
 | 
			
		||||
  thousand documents has the correspondent "Very obscure web shop I bought
 | 
			
		||||
  something five years ago", it will probably not assign this correspondent
 | 
			
		||||
  automatically if you buy something from them again. The more documents, the better.
 | 
			
		||||
* Paperless also needs a reasonable amount of negative examples to decide when
 | 
			
		||||
  not to assign a certain tag, correspondent, document type, or storage path. This will
 | 
			
		||||
  usually be the case as you start filling up paperless with documents.
 | 
			
		||||
  Example: If all your documents are either from "Webshop" and "Bank", paperless
 | 
			
		||||
  will assign one of these correspondents to ANY new document, if both are set
 | 
			
		||||
  to automatic matching.
 | 
			
		||||
 | 
			
		||||
Hooking into the consumption process
 | 
			
		||||
####################################
 | 
			
		||||
 | 
			
		||||
Sometimes you may want to do something arbitrary whenever a document is
 | 
			
		||||
consumed.  Rather than try to predict what you may want to do, Paperless lets
 | 
			
		||||
you execute scripts of your own choosing just before or after a document is
 | 
			
		||||
consumed using a couple simple hooks.
 | 
			
		||||
 | 
			
		||||
Just write a script, put it somewhere that Paperless can read & execute, and
 | 
			
		||||
then put the path to that script in ``paperless.conf`` or ``docker-compose.env`` with the variable name
 | 
			
		||||
of either ``PAPERLESS_PRE_CONSUME_SCRIPT`` or
 | 
			
		||||
``PAPERLESS_POST_CONSUME_SCRIPT``.
 | 
			
		||||
 | 
			
		||||
.. important::
 | 
			
		||||
 | 
			
		||||
    These scripts are executed in a **blocking** process, which means that if
 | 
			
		||||
    a script takes a long time to run, it can significantly slow down your
 | 
			
		||||
    document consumption flow.  If you want things to run asynchronously,
 | 
			
		||||
    you'll have to fork the process in your script and exit.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Pre-consumption script
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
Executed after the consumer sees a new document in the consumption folder, but
 | 
			
		||||
before any processing of the document is performed. This script can access the
 | 
			
		||||
following relevant environment variables set:
 | 
			
		||||
 | 
			
		||||
* ``DOCUMENT_SOURCE_PATH``
 | 
			
		||||
 | 
			
		||||
A simple but common example for this would be creating a simple script like
 | 
			
		||||
this:
 | 
			
		||||
 | 
			
		||||
``/usr/local/bin/ocr-pdf``
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
    #!/usr/bin/env bash
 | 
			
		||||
    pdf2pdfocr.py -i ${DOCUMENT_SOURCE_PATH}
 | 
			
		||||
 | 
			
		||||
``/etc/paperless.conf``
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
    ...
 | 
			
		||||
    PAPERLESS_PRE_CONSUME_SCRIPT="/usr/local/bin/ocr-pdf"
 | 
			
		||||
    ...
 | 
			
		||||
 | 
			
		||||
This will pass the path to the document about to be consumed to ``/usr/local/bin/ocr-pdf``,
 | 
			
		||||
which will in turn call `pdf2pdfocr.py`_ on your document, which will then
 | 
			
		||||
overwrite the file with an OCR'd version of the file and exit.  At which point,
 | 
			
		||||
the consumption process will begin with the newly modified file.
 | 
			
		||||
 | 
			
		||||
The script's stdout and stderr will be logged line by line to the webserver log, along
 | 
			
		||||
with the exit code of the script.
 | 
			
		||||
 | 
			
		||||
.. _pdf2pdfocr.py: https://github.com/LeoFCardoso/pdf2pdfocr
 | 
			
		||||
 | 
			
		||||
.. _advanced-post_consume_script:
 | 
			
		||||
 | 
			
		||||
Post-consumption script
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
Executed after the consumer has successfully processed a document and has moved it
 | 
			
		||||
into paperless. It receives the following environment variables:
 | 
			
		||||
 | 
			
		||||
* ``DOCUMENT_ID``
 | 
			
		||||
* ``DOCUMENT_FILE_NAME``
 | 
			
		||||
* ``DOCUMENT_CREATED``
 | 
			
		||||
* ``DOCUMENT_MODIFIED``
 | 
			
		||||
* ``DOCUMENT_ADDED``
 | 
			
		||||
* ``DOCUMENT_SOURCE_PATH``
 | 
			
		||||
* ``DOCUMENT_ARCHIVE_PATH``
 | 
			
		||||
* ``DOCUMENT_THUMBNAIL_PATH``
 | 
			
		||||
* ``DOCUMENT_DOWNLOAD_URL``
 | 
			
		||||
* ``DOCUMENT_THUMBNAIL_URL``
 | 
			
		||||
* ``DOCUMENT_CORRESPONDENT``
 | 
			
		||||
* ``DOCUMENT_TAGS``
 | 
			
		||||
* ``DOCUMENT_ORIGINAL_FILENAME``
 | 
			
		||||
 | 
			
		||||
The script can be in any language, but for a simple shell script
 | 
			
		||||
example, you can take a look at `post-consumption-example.sh`_ in this project.
 | 
			
		||||
 | 
			
		||||
The post consumption script cannot cancel the consumption process.
 | 
			
		||||
 | 
			
		||||
The script's stdout and stderr will be logged line by line to the webserver log, along
 | 
			
		||||
with the exit code of the script.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Docker
 | 
			
		||||
------
 | 
			
		||||
Assumed you have ``/home/foo/paperless-ngx/scripts/post-consumption-example.sh``.
 | 
			
		||||
 | 
			
		||||
You can pass that script into the consumer container via a host mount in your ``docker-compose.yml``.
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
  ...
 | 
			
		||||
  consumer:
 | 
			
		||||
    ...
 | 
			
		||||
    volumes:
 | 
			
		||||
      ...
 | 
			
		||||
      - /home/paperless-ngx/scripts:/path/in/container/scripts/
 | 
			
		||||
  ...
 | 
			
		||||
 | 
			
		||||
Example (docker-compose.yml): ``- /home/foo/paperless-ngx/scripts:/usr/src/paperless/scripts``
 | 
			
		||||
 | 
			
		||||
which in turn requires the variable ``PAPERLESS_POST_CONSUME_SCRIPT`` in ``docker-compose.env``  to point to ``/path/in/container/scripts/post-consumption-example.sh``.
 | 
			
		||||
 | 
			
		||||
Example (docker-compose.env): ``PAPERLESS_POST_CONSUME_SCRIPT=/usr/src/paperless/scripts/post-consumption-example.sh``
 | 
			
		||||
 | 
			
		||||
Troubleshooting:
 | 
			
		||||
 | 
			
		||||
- Monitor the docker-compose log ``cd ~/paperless-ngx; docker-compose logs -f``
 | 
			
		||||
- Check your script's permission e.g. in case of permission error ``sudo chmod 755 post-consumption-example.sh``
 | 
			
		||||
- Pipe your scripts's output to a log file e.g. ``echo "${DOCUMENT_ID}" | tee --append /usr/src/paperless/scripts/post-consumption-example.log``
 | 
			
		||||
 | 
			
		||||
.. _post-consumption-example.sh: https://github.com/paperless-ngx/paperless-ngx/blob/main/scripts/post-consumption-example.sh
 | 
			
		||||
 | 
			
		||||
.. _advanced-file_name_handling:
 | 
			
		||||
 | 
			
		||||
File name handling
 | 
			
		||||
##################
 | 
			
		||||
 | 
			
		||||
By default, paperless stores your documents in the media directory and renames them
 | 
			
		||||
using the identifier which it has assigned to each document. You will end up getting
 | 
			
		||||
files like ``0000123.pdf`` in your media directory. This isn't necessarily a bad
 | 
			
		||||
thing, because you normally don't have to access these files manually. However, if
 | 
			
		||||
you wish to name your files differently, you can do that by adjusting the
 | 
			
		||||
``PAPERLESS_FILENAME_FORMAT`` configuration option. Paperless adds the correct
 | 
			
		||||
file extension e.g. ``.pdf``, ``.jpg`` automatically.
 | 
			
		||||
 | 
			
		||||
This variable allows you to configure the filename (folders are allowed) using
 | 
			
		||||
placeholders. For example, configuring this to
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
    PAPERLESS_FILENAME_FORMAT={created_year}/{correspondent}/{title}
 | 
			
		||||
 | 
			
		||||
will create a directory structure as follows:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    2019/
 | 
			
		||||
      My bank/
 | 
			
		||||
        Statement January.pdf
 | 
			
		||||
        Statement February.pdf
 | 
			
		||||
    2020/
 | 
			
		||||
      My bank/
 | 
			
		||||
        Statement January.pdf
 | 
			
		||||
        Letter.pdf
 | 
			
		||||
        Letter_01.pdf
 | 
			
		||||
      Shoe store/
 | 
			
		||||
        My new shoes.pdf
 | 
			
		||||
 | 
			
		||||
.. danger::
 | 
			
		||||
 | 
			
		||||
    Do not manually move your files in the media folder. Paperless remembers the
 | 
			
		||||
    last filename a document was stored as. If you do rename a file, paperless will
 | 
			
		||||
    report your files as missing and won't be able to find them.
 | 
			
		||||
 | 
			
		||||
Paperless provides the following placeholders within filenames:
 | 
			
		||||
 | 
			
		||||
* ``{asn}``: The archive serial number of the document, or "none".
 | 
			
		||||
* ``{correspondent}``: The name of the correspondent, or "none".
 | 
			
		||||
* ``{document_type}``: The name of the document type, or "none".
 | 
			
		||||
* ``{tag_list}``: A comma separated list of all tags assigned to the document.
 | 
			
		||||
* ``{title}``: The title of the document.
 | 
			
		||||
* ``{created}``: The full date (ISO format) the document was created.
 | 
			
		||||
* ``{created_year}``: Year created only, formatted as the year with century.
 | 
			
		||||
* ``{created_year_short}``: Year created only, formatted as the year without century, zero padded.
 | 
			
		||||
* ``{created_month}``: Month created only (number 01-12).
 | 
			
		||||
* ``{created_month_name}``: Month created name, as per locale
 | 
			
		||||
* ``{created_month_name_short}``: Month created abbreviated name, as per locale
 | 
			
		||||
* ``{created_day}``: Day created only (number 01-31).
 | 
			
		||||
* ``{added}``: The full date (ISO format) the document was added to paperless.
 | 
			
		||||
* ``{added_year}``: Year added only.
 | 
			
		||||
* ``{added_year_short}``: Year added only, formatted as the year without century, zero padded.
 | 
			
		||||
* ``{added_month}``: Month added only (number 01-12).
 | 
			
		||||
* ``{added_month_name}``: Month added name, as per locale
 | 
			
		||||
* ``{added_month_name_short}``: Month added abbreviated name, as per locale
 | 
			
		||||
* ``{added_day}``: Day added only (number 01-31).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Paperless will try to conserve the information from your database as much as possible.
 | 
			
		||||
However, some characters that you can use in document titles and correspondent names (such
 | 
			
		||||
as ``: \ /`` and a couple more) are not allowed in filenames and will be replaced with dashes.
 | 
			
		||||
 | 
			
		||||
If paperless detects that two documents share the same filename, paperless will automatically
 | 
			
		||||
append ``_01``, ``_02``, etc to the filename. This happens if all the placeholders in a filename
 | 
			
		||||
evaluate to the same value.
 | 
			
		||||
 | 
			
		||||
.. hint::
 | 
			
		||||
    You can affect how empty placeholders are treated by changing the following setting to
 | 
			
		||||
    `true`.
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        PAPERLESS_FILENAME_FORMAT_REMOVE_NONE=True
 | 
			
		||||
 | 
			
		||||
    Doing this results in all empty placeholders resolving to "" instead of "none" as stated above.
 | 
			
		||||
    Spaces before empty placeholders are removed as well, empty directories are omitted.
 | 
			
		||||
 | 
			
		||||
.. hint::
 | 
			
		||||
 | 
			
		||||
    Paperless checks the filename of a document whenever it is saved. Therefore,
 | 
			
		||||
    you need to update the filenames of your documents and move them after altering
 | 
			
		||||
    this setting by invoking the :ref:`document renamer <utilities-renamer>`.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    Make absolutely sure you get the spelling of the placeholders right, or else
 | 
			
		||||
    paperless will use the default naming scheme instead.
 | 
			
		||||
 | 
			
		||||
.. caution::
 | 
			
		||||
 | 
			
		||||
    As of now, you could totally tell paperless to store your files anywhere outside
 | 
			
		||||
    the media directory by setting
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        PAPERLESS_FILENAME_FORMAT=../../my/custom/location/{title}
 | 
			
		||||
 | 
			
		||||
    However, keep in mind that inside docker, if files get stored outside of the
 | 
			
		||||
    predefined volumes, they will be lost after a restart of paperless.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Storage paths
 | 
			
		||||
#############
 | 
			
		||||
 | 
			
		||||
One of the best things in Paperless is that you can not only access the documents via the
 | 
			
		||||
web interface, but also via the file system.
 | 
			
		||||
 | 
			
		||||
When as single storage layout is not sufficient for your use case, storage paths come to
 | 
			
		||||
the rescue. Storage paths allow you to configure more precisely where each document is stored
 | 
			
		||||
in the file system.
 | 
			
		||||
 | 
			
		||||
- Each storage path is a `PAPERLESS_FILENAME_FORMAT` and follows the rules described above
 | 
			
		||||
- Each document is assigned a storage path using the matching algorithms described above, but
 | 
			
		||||
  can be overwritten at any time
 | 
			
		||||
 | 
			
		||||
For example, you could define the following two storage paths:
 | 
			
		||||
 | 
			
		||||
1. Normal communications are put into a folder structure sorted by `year/correspondent`
 | 
			
		||||
2. Communications with insurance companies are stored in a flat structure with longer file names,
 | 
			
		||||
   but containing the full date of the correspondence.
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    By Year = {created_year}/{correspondent}/{title}
 | 
			
		||||
    Insurances = Insurances/{correspondent}/{created_year}-{created_month}-{created_day} {title}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
If you then map these storage paths to the documents, you might get the following result.
 | 
			
		||||
For simplicity, `By Year` defines the same structure as in the previous example above.
 | 
			
		||||
 | 
			
		||||
.. code:: text
 | 
			
		||||
 | 
			
		||||
   2019/                                   # By Year
 | 
			
		||||
      My bank/
 | 
			
		||||
        Statement January.pdf
 | 
			
		||||
        Statement February.pdf
 | 
			
		||||
 | 
			
		||||
    Insurances/                           # Insurances
 | 
			
		||||
      Healthcare 123/
 | 
			
		||||
        2022-01-01 Statement January.pdf
 | 
			
		||||
        2022-02-02 Letter.pdf
 | 
			
		||||
        2022-02-03 Letter.pdf
 | 
			
		||||
      Dental 456/
 | 
			
		||||
        2021-12-01 New Conditions.pdf
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. hint::
 | 
			
		||||
 | 
			
		||||
    Defining a storage path is optional. If no storage path is defined for a document, the global
 | 
			
		||||
    `PAPERLESS_FILENAME_FORMAT` is applied.
 | 
			
		||||
 | 
			
		||||
.. caution::
 | 
			
		||||
 | 
			
		||||
    If you adjust the format of an existing storage path, old documents don't get relocated automatically.
 | 
			
		||||
    You need to run the :ref:`document renamer <utilities-renamer>` to adjust their pathes.
 | 
			
		||||
 | 
			
		||||
.. _advanced-celery-monitoring:
 | 
			
		||||
 | 
			
		||||
Celery Monitoring
 | 
			
		||||
#################
 | 
			
		||||
 | 
			
		||||
The monitoring tool `Flower <https://flower.readthedocs.io/en/latest/index.html>`_ can be used to view more
 | 
			
		||||
detailed information about the health of the celery workers used for asynchronous tasks.  This includes details
 | 
			
		||||
on currently running, queued and completed tasks, timing and more.  Flower can also be used with Prometheus, as it
 | 
			
		||||
exports metrics.  For details on its capabilities, refer to the Flower documentation.
 | 
			
		||||
 | 
			
		||||
To configure Flower further, create a `flowerconfig.py` and place it into the `src/paperless` directory.  For
 | 
			
		||||
a Docker installation, you can use volumes to accomplish this:
 | 
			
		||||
 | 
			
		||||
.. code:: yaml
 | 
			
		||||
 | 
			
		||||
    services:
 | 
			
		||||
      # ...
 | 
			
		||||
      webserver:
 | 
			
		||||
        # ...
 | 
			
		||||
        volumes:
 | 
			
		||||
          - /path/to/my/flowerconfig.py:/usr/src/paperless/src/paperless/flowerconfig.py:ro
 | 
			
		||||
 | 
			
		||||
Custom Container Initialization
 | 
			
		||||
###############################
 | 
			
		||||
 | 
			
		||||
The Docker image includes the ability to run custom user scripts during startup.  This could be
 | 
			
		||||
utilized for installing additional tools or Python packages, for example.
 | 
			
		||||
 | 
			
		||||
To utilize this, mount a folder containing your scripts to the custom initialization directory, `/custom-cont-init.d`
 | 
			
		||||
and place scripts you wish to run inside.  For security, the folder must be owned by `root` and should have permissions
 | 
			
		||||
of `a=rx`.  Additionally, scripts must only be writable by `root`.
 | 
			
		||||
 | 
			
		||||
Your scripts will be run directly before the webserver completes startup.  Scripts will be run by the `root` user.
 | 
			
		||||
If you would like to switch users, the utility `gosu` is available and preferred over `sudo`.
 | 
			
		||||
 | 
			
		||||
This is an advanced functionality with which you could break functionality or lose data.  If you experience issues,
 | 
			
		||||
please disable any custom scripts and try again before reporting an issue.
 | 
			
		||||
 | 
			
		||||
For example, using Docker Compose:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code:: yaml
 | 
			
		||||
 | 
			
		||||
    services:
 | 
			
		||||
      # ...
 | 
			
		||||
      webserver:
 | 
			
		||||
        # ...
 | 
			
		||||
        volumes:
 | 
			
		||||
          - /path/to/my/scripts:/custom-cont-init.d:ro
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _advanced-mysql-caveats:
 | 
			
		||||
 | 
			
		||||
MySQL Caveats
 | 
			
		||||
#############
 | 
			
		||||
 | 
			
		||||
Case Sensitivity
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
The database interface does not provide a method to configure a MySQL database to
 | 
			
		||||
be case sensitive.  This would prevent a user from creating a tag ``Name`` and ``NAME``
 | 
			
		||||
as they are considered the same.
 | 
			
		||||
 | 
			
		||||
Per Django documentation, to enable this requires manual intervention.  To enable
 | 
			
		||||
case sensetive tables, you can execute the following command against each table:
 | 
			
		||||
 | 
			
		||||
``ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;``
 | 
			
		||||
 | 
			
		||||
You can also set the default for new tables (this does NOT affect existing tables) with:
 | 
			
		||||
 | 
			
		||||
``ALTER DATABASE <db_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;``
 | 
			
		||||
							
								
								
									
										319
									
								
								docs/api.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,319 @@
 | 
			
		||||
# The REST API
 | 
			
		||||
 | 
			
		||||
Paperless makes use of the [Django REST
 | 
			
		||||
Framework](http://django-rest-framework.org/) standard API interface. It
 | 
			
		||||
provides a browsable API for most of its endpoints, which you can
 | 
			
		||||
inspect at `http://<paperless-host>:<port>/api/`. This also documents
 | 
			
		||||
most of the available filters and ordering fields.
 | 
			
		||||
 | 
			
		||||
The API provides 5 main endpoints:
 | 
			
		||||
 | 
			
		||||
- `/api/documents/`: Full CRUD support, except POSTing new documents.
 | 
			
		||||
  See below.
 | 
			
		||||
- `/api/correspondents/`: Full CRUD support.
 | 
			
		||||
- `/api/document_types/`: Full CRUD support.
 | 
			
		||||
- `/api/logs/`: Read-Only.
 | 
			
		||||
- `/api/tags/`: Full CRUD support.
 | 
			
		||||
- `/api/mail_accounts/`: Full CRUD support.
 | 
			
		||||
- `/api/mail_rules/`: Full CRUD support.
 | 
			
		||||
 | 
			
		||||
All of these endpoints except for the logging endpoint allow you to
 | 
			
		||||
fetch, edit and delete individual objects by appending their primary key
 | 
			
		||||
to the path, for example `/api/documents/454/`.
 | 
			
		||||
 | 
			
		||||
The objects served by the document endpoint contain the following
 | 
			
		||||
fields:
 | 
			
		||||
 | 
			
		||||
- `id`: ID of the document. Read-only.
 | 
			
		||||
- `title`: Title of the document.
 | 
			
		||||
- `content`: Plain text content of the document.
 | 
			
		||||
- `tags`: List of IDs of tags assigned to this document, or empty
 | 
			
		||||
  list.
 | 
			
		||||
- `document_type`: Document type of this document, or null.
 | 
			
		||||
- `correspondent`: Correspondent of this document or null.
 | 
			
		||||
- `created`: The date time at which this document was created.
 | 
			
		||||
- `created_date`: The date (YYYY-MM-DD) at which this document was
 | 
			
		||||
  created. Optional. If also passed with created, this is ignored.
 | 
			
		||||
- `modified`: The date at which this document was last edited in
 | 
			
		||||
  paperless. Read-only.
 | 
			
		||||
- `added`: The date at which this document was added to paperless.
 | 
			
		||||
  Read-only.
 | 
			
		||||
- `archive_serial_number`: The identifier of this document in a
 | 
			
		||||
  physical document archive.
 | 
			
		||||
- `original_file_name`: Verbose filename of the original document.
 | 
			
		||||
  Read-only.
 | 
			
		||||
- `archived_file_name`: Verbose filename of the archived document.
 | 
			
		||||
  Read-only. Null if no archived document is available.
 | 
			
		||||
 | 
			
		||||
## Downloading documents
 | 
			
		||||
 | 
			
		||||
In addition to that, the document endpoint offers these additional
 | 
			
		||||
actions on individual documents:
 | 
			
		||||
 | 
			
		||||
- `/api/documents/<pk>/download/`: Download the document.
 | 
			
		||||
- `/api/documents/<pk>/preview/`: Display the document inline, without
 | 
			
		||||
  downloading it.
 | 
			
		||||
- `/api/documents/<pk>/thumb/`: Download the PNG thumbnail of a
 | 
			
		||||
  document.
 | 
			
		||||
 | 
			
		||||
Paperless generates archived PDF/A documents from consumed files and
 | 
			
		||||
stores both the original files as well as the archived files. By
 | 
			
		||||
default, the endpoints for previews and downloads serve the archived
 | 
			
		||||
file, if it is available. Otherwise, the original file is served. Some
 | 
			
		||||
document cannot be archived.
 | 
			
		||||
 | 
			
		||||
The endpoints correctly serve the response header fields
 | 
			
		||||
`Content-Disposition` and `Content-Type` to indicate the filename for
 | 
			
		||||
download and the type of content of the document.
 | 
			
		||||
 | 
			
		||||
In order to download or preview the original document when an archived
 | 
			
		||||
document is available, supply the query parameter `original=true`.
 | 
			
		||||
 | 
			
		||||
!!! tip
 | 
			
		||||
 | 
			
		||||
    Paperless used to provide these functionality at `/fetch/<pk>/preview`,
 | 
			
		||||
    `/fetch/<pk>/thumb` and `/fetch/<pk>/doc`. Redirects to the new URLs are
 | 
			
		||||
    in place. However, if you use these old URLs to access documents, you
 | 
			
		||||
    should update your app or script to use the new URLs.
 | 
			
		||||
 | 
			
		||||
## Getting document metadata
 | 
			
		||||
 | 
			
		||||
The api also has an endpoint to retrieve read-only metadata about
 | 
			
		||||
specific documents. this information is not served along with the
 | 
			
		||||
document objects, since it requires reading files and would therefore
 | 
			
		||||
slow down document lists considerably.
 | 
			
		||||
 | 
			
		||||
Access the metadata of a document with an ID `id` at
 | 
			
		||||
`/api/documents/<id>/metadata/`.
 | 
			
		||||
 | 
			
		||||
The endpoint reports the following data:
 | 
			
		||||
 | 
			
		||||
- `original_checksum`: MD5 checksum of the original document.
 | 
			
		||||
- `original_size`: Size of the original document, in bytes.
 | 
			
		||||
- `original_mime_type`: Mime type of the original document.
 | 
			
		||||
- `media_filename`: Current filename of the document, under which it
 | 
			
		||||
  is stored inside the media directory.
 | 
			
		||||
- `has_archive_version`: True, if this document is archived, false
 | 
			
		||||
  otherwise.
 | 
			
		||||
- `original_metadata`: A list of metadata associated with the original
 | 
			
		||||
  document. See below.
 | 
			
		||||
- `archive_checksum`: MD5 checksum of the archived document, or null.
 | 
			
		||||
- `archive_size`: Size of the archived document in bytes, or null.
 | 
			
		||||
- `archive_metadata`: Metadata associated with the archived document,
 | 
			
		||||
  or null. See below.
 | 
			
		||||
 | 
			
		||||
File metadata is reported as a list of objects in the following form:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "namespace": "http://ns.adobe.com/pdf/1.3/",
 | 
			
		||||
    "prefix": "pdf",
 | 
			
		||||
    "key": "Producer",
 | 
			
		||||
    "value": "SparklePDF, Fancy edition"
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`namespace` and `prefix` can be null. The actual metadata reported
 | 
			
		||||
depends on the file type and the metadata available in that specific
 | 
			
		||||
document. Paperless only reports PDF metadata at this point.
 | 
			
		||||
 | 
			
		||||
## Authorization
 | 
			
		||||
 | 
			
		||||
The REST api provides three different forms of authentication.
 | 
			
		||||
 | 
			
		||||
1.  Basic authentication
 | 
			
		||||
 | 
			
		||||
    Authorize by providing a HTTP header in the form
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    Authorization: Basic <credentials>
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    where `credentials` is a base64-encoded string of
 | 
			
		||||
    `<username>:<password>`
 | 
			
		||||
 | 
			
		||||
2.  Session authentication
 | 
			
		||||
 | 
			
		||||
    When you're logged into paperless in your browser, you're
 | 
			
		||||
    automatically logged into the API as well and don't need to provide
 | 
			
		||||
    any authorization headers.
 | 
			
		||||
 | 
			
		||||
3.  Token authentication
 | 
			
		||||
 | 
			
		||||
    Paperless also offers an endpoint to acquire authentication tokens.
 | 
			
		||||
 | 
			
		||||
    POST a username and password as a form or json string to
 | 
			
		||||
    `/api/token/` and paperless will respond with a token, if the login
 | 
			
		||||
    data is correct. This token can be used to authenticate other
 | 
			
		||||
    requests with the following HTTP header:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    Authorization: Token <token>
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    Tokens can be managed and revoked in the paperless admin.
 | 
			
		||||
 | 
			
		||||
## Searching for documents
 | 
			
		||||
 | 
			
		||||
Full text searching is available on the `/api/documents/` endpoint. Two
 | 
			
		||||
specific query parameters cause the API to return full text search
 | 
			
		||||
results:
 | 
			
		||||
 | 
			
		||||
- `/api/documents/?query=your%20search%20query`: Search for a document
 | 
			
		||||
  using a full text query. For details on the syntax, see
 | 
			
		||||
  `basic-usage_searching`{.interpreted-text role="ref"}.
 | 
			
		||||
- `/api/documents/?more_like=1234`: Search for documents similar to
 | 
			
		||||
  the document with id 1234.
 | 
			
		||||
 | 
			
		||||
Pagination works exactly the same as it does for normal requests on this
 | 
			
		||||
endpoint.
 | 
			
		||||
 | 
			
		||||
Certain limitations apply to full text queries:
 | 
			
		||||
 | 
			
		||||
- Results are always sorted by search score. The results matching the
 | 
			
		||||
  query best will show up first.
 | 
			
		||||
- Only a small subset of filtering parameters are supported.
 | 
			
		||||
 | 
			
		||||
Furthermore, each returned document has an additional `__search_hit__`
 | 
			
		||||
attribute with various information about the search results:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
    "count": 31,
 | 
			
		||||
    "next": "http://localhost:8000/api/documents/?page=2&query=test",
 | 
			
		||||
    "previous": null,
 | 
			
		||||
    "results": [
 | 
			
		||||
 | 
			
		||||
        ...
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            "id": 123,
 | 
			
		||||
            "title": "title",
 | 
			
		||||
            "content": "content",
 | 
			
		||||
 | 
			
		||||
            ...
 | 
			
		||||
 | 
			
		||||
            "__search_hit__": {
 | 
			
		||||
                "score": 0.343,
 | 
			
		||||
                "highlights": "text <span class="match">Test</span> text",
 | 
			
		||||
                "rank": 23
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        ...
 | 
			
		||||
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- `score` is an indication how well this document matches the query
 | 
			
		||||
  relative to the other search results.
 | 
			
		||||
- `highlights` is an excerpt from the document content and highlights
 | 
			
		||||
  the search terms with `<span>` tags as shown above.
 | 
			
		||||
- `rank` is the index of the search results. The first result will
 | 
			
		||||
  have rank 0.
 | 
			
		||||
 | 
			
		||||
### `/api/search/autocomplete/`
 | 
			
		||||
 | 
			
		||||
Get auto completions for a partial search term.
 | 
			
		||||
 | 
			
		||||
Query parameters:
 | 
			
		||||
 | 
			
		||||
- `term`: The incomplete term.
 | 
			
		||||
- `limit`: Amount of results. Defaults to 10.
 | 
			
		||||
 | 
			
		||||
Results returned by the endpoint are ordered by importance of the term
 | 
			
		||||
in the document index. The first result is the term that has the highest
 | 
			
		||||
Tf/Idf score in the index.
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
["term1", "term3", "term6", "term4"]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## POSTing documents {#api-file_uploads}
 | 
			
		||||
 | 
			
		||||
The API provides a special endpoint for file uploads:
 | 
			
		||||
 | 
			
		||||
`/api/documents/post_document/`
 | 
			
		||||
 | 
			
		||||
POST a multipart form to this endpoint, where the form field `document`
 | 
			
		||||
contains the document that you want to upload to paperless. The filename
 | 
			
		||||
is sanitized and then used to store the document in a temporary
 | 
			
		||||
directory, and the consumer will be instructed to consume the document
 | 
			
		||||
from there.
 | 
			
		||||
 | 
			
		||||
The endpoint supports the following optional form fields:
 | 
			
		||||
 | 
			
		||||
- `title`: Specify a title that the consumer should use for the
 | 
			
		||||
  document.
 | 
			
		||||
- `created`: Specify a DateTime where the document was created (e.g.
 | 
			
		||||
  "2016-04-19" or "2016-04-19 06:15:00+02:00").
 | 
			
		||||
- `correspondent`: Specify the ID of a correspondent that the consumer
 | 
			
		||||
  should use for the document.
 | 
			
		||||
- `document_type`: Similar to correspondent.
 | 
			
		||||
- `tags`: Similar to correspondent. Specify this multiple times to
 | 
			
		||||
  have multiple tags added to the document.
 | 
			
		||||
 | 
			
		||||
The endpoint will immediately return "OK" if the document consumption
 | 
			
		||||
process was started successfully. No additional status information about
 | 
			
		||||
the consumption process itself is available, since that happens in a
 | 
			
		||||
different process.
 | 
			
		||||
 | 
			
		||||
## API Versioning
 | 
			
		||||
 | 
			
		||||
The REST API is versioned since Paperless-ngx 1.3.0.
 | 
			
		||||
 | 
			
		||||
- Versioning ensures that changes to the API don't break older
 | 
			
		||||
  clients.
 | 
			
		||||
- Clients specify the specific version of the API they wish to use
 | 
			
		||||
  with every request and Paperless will handle the request using the
 | 
			
		||||
  specified API version.
 | 
			
		||||
- Even if the underlying data model changes, older API versions will
 | 
			
		||||
  always serve compatible data.
 | 
			
		||||
- If no version is specified, Paperless will serve version 1 to ensure
 | 
			
		||||
  compatibility with older clients that do not request a specific API
 | 
			
		||||
  version.
 | 
			
		||||
 | 
			
		||||
API versions are specified by submitting an additional HTTP `Accept`
 | 
			
		||||
header with every request:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
Accept: application/json; version=6
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If an invalid version is specified, Paperless 1.3.0 will respond with
 | 
			
		||||
"406 Not Acceptable" and an error message in the body. Earlier
 | 
			
		||||
versions of Paperless will serve API version 1 regardless of whether a
 | 
			
		||||
version is specified via the `Accept` header.
 | 
			
		||||
 | 
			
		||||
If a client wishes to verify whether it is compatible with any given
 | 
			
		||||
server, the following procedure should be performed:
 | 
			
		||||
 | 
			
		||||
1.  Perform an _authenticated_ request against any API endpoint. If the
 | 
			
		||||
    server is on version 1.3.0 or newer, the server will add two custom
 | 
			
		||||
    headers to the response:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    X-Api-Version: 2
 | 
			
		||||
    X-Version: 1.3.0
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
2.  Determine whether the client is compatible with this server based on
 | 
			
		||||
    the presence/absence of these headers and their values if present.
 | 
			
		||||
 | 
			
		||||
### API Changelog
 | 
			
		||||
 | 
			
		||||
#### Version 1
 | 
			
		||||
 | 
			
		||||
Initial API version.
 | 
			
		||||
 | 
			
		||||
#### Version 2
 | 
			
		||||
 | 
			
		||||
- Added field `Tag.color`. This read/write string field contains a hex
 | 
			
		||||
  color such as `#a6cee3`.
 | 
			
		||||
- Added read-only field `Tag.text_color`. This field contains the text
 | 
			
		||||
  color to use for a specific tag, which is either black or white
 | 
			
		||||
  depending on the brightness of `Tag.color`.
 | 
			
		||||
- Removed field `Tag.colour`.
 | 
			
		||||
							
								
								
									
										303
									
								
								docs/api.rst
									
									
									
									
									
								
							
							
						
						@ -1,303 +0,0 @@
 | 
			
		||||
 | 
			
		||||
************
 | 
			
		||||
The REST API
 | 
			
		||||
************
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Paperless makes use of the `Django REST Framework`_ standard API interface.
 | 
			
		||||
It provides a browsable API for most of its endpoints, which you can inspect
 | 
			
		||||
at ``http://<paperless-host>:<port>/api/``. This also documents most of the
 | 
			
		||||
available filters and ordering fields.
 | 
			
		||||
 | 
			
		||||
.. _Django REST Framework: http://django-rest-framework.org/
 | 
			
		||||
 | 
			
		||||
The API provides 5 main endpoints:
 | 
			
		||||
 | 
			
		||||
*   ``/api/documents/``: Full CRUD support, except POSTing new documents. See below.
 | 
			
		||||
*   ``/api/correspondents/``: Full CRUD support.
 | 
			
		||||
*   ``/api/document_types/``: Full CRUD support.
 | 
			
		||||
*   ``/api/logs/``: Read-Only.
 | 
			
		||||
*   ``/api/tags/``: Full CRUD support.
 | 
			
		||||
 | 
			
		||||
All of these endpoints except for the logging endpoint
 | 
			
		||||
allow you to fetch, edit and delete individual objects
 | 
			
		||||
by appending their primary key to the path, for example ``/api/documents/454/``.
 | 
			
		||||
 | 
			
		||||
The objects served by the document endpoint contain the following fields:
 | 
			
		||||
 | 
			
		||||
*   ``id``: ID of the document. Read-only.
 | 
			
		||||
*   ``title``: Title of the document.
 | 
			
		||||
*   ``content``: Plain text content of the document.
 | 
			
		||||
*   ``tags``: List of IDs of tags assigned to this document, or empty list.
 | 
			
		||||
*   ``document_type``: Document type of this document, or null.
 | 
			
		||||
*   ``correspondent``:  Correspondent of this document or null.
 | 
			
		||||
*   ``created``: The date time at which this document was created.
 | 
			
		||||
*   ``created_date``: The date (YYYY-MM-DD) at which this document was created. Optional. If also passed with created, this is ignored.
 | 
			
		||||
*   ``modified``: The date at which this document was last edited in paperless. Read-only.
 | 
			
		||||
*   ``added``: The date at which this document was added to paperless. Read-only.
 | 
			
		||||
*   ``archive_serial_number``: The identifier of this document in a physical document archive.
 | 
			
		||||
*   ``original_file_name``: Verbose filename of the original document. Read-only.
 | 
			
		||||
*   ``archived_file_name``: Verbose filename of the archived document. Read-only. Null if no archived document is available.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Downloading documents
 | 
			
		||||
#####################
 | 
			
		||||
 | 
			
		||||
In addition to that, the document endpoint offers these additional actions on
 | 
			
		||||
individual documents:
 | 
			
		||||
 | 
			
		||||
*   ``/api/documents/<pk>/download/``: Download the document.
 | 
			
		||||
*   ``/api/documents/<pk>/preview/``: Display the document inline,
 | 
			
		||||
    without downloading it.
 | 
			
		||||
*   ``/api/documents/<pk>/thumb/``: Download the PNG thumbnail of a document.
 | 
			
		||||
 | 
			
		||||
Paperless generates archived PDF/A documents from consumed files and stores both
 | 
			
		||||
the original files as well as the archived files. By default, the endpoints
 | 
			
		||||
for previews and downloads serve the archived file, if it is available.
 | 
			
		||||
Otherwise, the original file is served.
 | 
			
		||||
Some document cannot be archived.
 | 
			
		||||
 | 
			
		||||
The endpoints correctly serve the response header fields ``Content-Disposition``
 | 
			
		||||
and ``Content-Type`` to indicate the filename for download and the type of content of
 | 
			
		||||
the document.
 | 
			
		||||
 | 
			
		||||
In order to download or preview the original document when an archived document is available,
 | 
			
		||||
supply the query parameter ``original=true``.
 | 
			
		||||
 | 
			
		||||
.. hint::
 | 
			
		||||
 | 
			
		||||
    Paperless used to provide these functionality at ``/fetch/<pk>/preview``,
 | 
			
		||||
    ``/fetch/<pk>/thumb`` and ``/fetch/<pk>/doc``. Redirects to the new URLs
 | 
			
		||||
    are in place. However, if you use these old URLs to access documents, you
 | 
			
		||||
    should update your app or script to use the new URLs.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Getting document metadata
 | 
			
		||||
#########################
 | 
			
		||||
 | 
			
		||||
The api also has an endpoint to retrieve read-only metadata about specific documents. this
 | 
			
		||||
information is not served along with the document objects, since it requires reading
 | 
			
		||||
files and would therefore slow down document lists considerably.
 | 
			
		||||
 | 
			
		||||
Access the metadata of a document with an ID ``id`` at ``/api/documents/<id>/metadata/``.
 | 
			
		||||
 | 
			
		||||
The endpoint reports the following data:
 | 
			
		||||
 | 
			
		||||
*   ``original_checksum``: MD5 checksum of the original document.
 | 
			
		||||
*   ``original_size``: Size of the original document, in bytes.
 | 
			
		||||
*   ``original_mime_type``: Mime type of the original document.
 | 
			
		||||
*   ``media_filename``: Current filename of the document, under which it is stored inside the media directory.
 | 
			
		||||
*   ``has_archive_version``: True, if this document is archived, false otherwise.
 | 
			
		||||
*   ``original_metadata``: A list of metadata associated with the original document. See below.
 | 
			
		||||
*   ``archive_checksum``: MD5 checksum of the archived document, or null.
 | 
			
		||||
*   ``archive_size``: Size of the archived document in bytes, or null.
 | 
			
		||||
*   ``archive_metadata``: Metadata associated with the archived document, or null. See below.
 | 
			
		||||
 | 
			
		||||
File metadata is reported as a list of objects in the following form:
 | 
			
		||||
 | 
			
		||||
.. code:: json
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
        {
 | 
			
		||||
            "namespace": "http://ns.adobe.com/pdf/1.3/",
 | 
			
		||||
            "prefix": "pdf",
 | 
			
		||||
            "key": "Producer",
 | 
			
		||||
            "value": "SparklePDF, Fancy edition"
 | 
			
		||||
        },
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
``namespace`` and ``prefix`` can be null. The actual metadata reported depends on the file type and the metadata
 | 
			
		||||
available in that specific document. Paperless only reports PDF metadata at this point.
 | 
			
		||||
 | 
			
		||||
Authorization
 | 
			
		||||
#############
 | 
			
		||||
 | 
			
		||||
The REST api provides three different forms of authentication.
 | 
			
		||||
 | 
			
		||||
1.  Basic authentication
 | 
			
		||||
 | 
			
		||||
    Authorize by providing a HTTP header in the form
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        Authorization: Basic <credentials>
 | 
			
		||||
 | 
			
		||||
    where ``credentials`` is a base64-encoded string of ``<username>:<password>``
 | 
			
		||||
 | 
			
		||||
2.  Session authentication
 | 
			
		||||
 | 
			
		||||
    When you're logged into paperless in your browser, you're automatically
 | 
			
		||||
    logged into the API as well and don't need to provide any authorization
 | 
			
		||||
    headers.
 | 
			
		||||
 | 
			
		||||
3.  Token authentication
 | 
			
		||||
 | 
			
		||||
    Paperless also offers an endpoint to acquire authentication tokens.
 | 
			
		||||
 | 
			
		||||
    POST a username and password as a form or json string to ``/api/token/``
 | 
			
		||||
    and paperless will respond with a token, if the login data is correct.
 | 
			
		||||
    This token can be used to authenticate other requests with the
 | 
			
		||||
    following HTTP header:
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        Authorization: Token <token>
 | 
			
		||||
 | 
			
		||||
    Tokens can be managed and revoked in the paperless admin.
 | 
			
		||||
 | 
			
		||||
Searching for documents
 | 
			
		||||
#######################
 | 
			
		||||
 | 
			
		||||
Full text searching is available on the ``/api/documents/`` endpoint. Two specific
 | 
			
		||||
query parameters cause the API to return full text search results:
 | 
			
		||||
 | 
			
		||||
*   ``/api/documents/?query=your%20search%20query``: Search for a document using a full text query.
 | 
			
		||||
    For details on the syntax, see :ref:`basic-usage_searching`.
 | 
			
		||||
 | 
			
		||||
*   ``/api/documents/?more_like=1234``: Search for documents similar to the document with id 1234.
 | 
			
		||||
 | 
			
		||||
Pagination works exactly the same as it does for normal requests on this endpoint.
 | 
			
		||||
 | 
			
		||||
Certain limitations apply to full text queries:
 | 
			
		||||
 | 
			
		||||
*   Results are always sorted by search score. The results matching the query best will show up first.
 | 
			
		||||
 | 
			
		||||
*   Only a small subset of filtering parameters are supported.
 | 
			
		||||
 | 
			
		||||
Furthermore, each returned document has an additional ``__search_hit__`` attribute with various information
 | 
			
		||||
about the search results:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        "count": 31,
 | 
			
		||||
        "next": "http://localhost:8000/api/documents/?page=2&query=test",
 | 
			
		||||
        "previous": null,
 | 
			
		||||
        "results": [
 | 
			
		||||
 | 
			
		||||
            ...
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
                "id": 123,
 | 
			
		||||
                "title": "title",
 | 
			
		||||
                "content": "content",
 | 
			
		||||
 | 
			
		||||
                ...
 | 
			
		||||
 | 
			
		||||
                "__search_hit__": {
 | 
			
		||||
                    "score": 0.343,
 | 
			
		||||
                    "highlights": "text <span class=\"match\">Test</span> text",
 | 
			
		||||
                    "rank": 23
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            ...
 | 
			
		||||
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
*   ``score`` is an indication how well this document matches the query relative to the other search results.
 | 
			
		||||
*   ``highlights`` is an excerpt from the document content and highlights the search terms with ``<span>`` tags as shown above.
 | 
			
		||||
*   ``rank`` is the index of the search results. The first result will have rank 0.
 | 
			
		||||
 | 
			
		||||
``/api/search/autocomplete/``
 | 
			
		||||
=============================
 | 
			
		||||
 | 
			
		||||
Get auto completions for a partial search term.
 | 
			
		||||
 | 
			
		||||
Query parameters:
 | 
			
		||||
 | 
			
		||||
*   ``term``: The incomplete term.
 | 
			
		||||
*   ``limit``: Amount of results. Defaults to 10.
 | 
			
		||||
 | 
			
		||||
Results returned by the endpoint are ordered by importance of the term in the
 | 
			
		||||
document index. The first result is the term that has the highest Tf/Idf score
 | 
			
		||||
in the index.
 | 
			
		||||
 | 
			
		||||
.. code:: json
 | 
			
		||||
 | 
			
		||||
    [
 | 
			
		||||
        "term1",
 | 
			
		||||
        "term3",
 | 
			
		||||
        "term6",
 | 
			
		||||
        "term4"
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _api-file_uploads:
 | 
			
		||||
 | 
			
		||||
POSTing documents
 | 
			
		||||
#################
 | 
			
		||||
 | 
			
		||||
The API provides a special endpoint for file uploads:
 | 
			
		||||
 | 
			
		||||
``/api/documents/post_document/``
 | 
			
		||||
 | 
			
		||||
POST a multipart form to this endpoint, where the form field ``document`` contains
 | 
			
		||||
the document that you want to upload to paperless. The filename is sanitized and
 | 
			
		||||
then used to store the document in a temporary directory, and the consumer will
 | 
			
		||||
be instructed to consume the document from there.
 | 
			
		||||
 | 
			
		||||
The endpoint supports the following optional form fields:
 | 
			
		||||
 | 
			
		||||
*   ``title``: Specify a title that the consumer should use for the document.
 | 
			
		||||
*   ``created``: Specify a DateTime where the document was created (e.g. "2016-04-19" or "2016-04-19 06:15:00+02:00").
 | 
			
		||||
*   ``correspondent``: Specify the ID of a correspondent that the consumer should use for the document.
 | 
			
		||||
*   ``document_type``: Similar to correspondent.
 | 
			
		||||
*   ``tags``: Similar to correspondent. Specify this multiple times to have multiple tags added
 | 
			
		||||
    to the document.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The endpoint will immediately return "OK" if the document consumption process
 | 
			
		||||
was started successfully. No additional status information about the consumption
 | 
			
		||||
process itself is available, since that happens in a different process.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _api-versioning:
 | 
			
		||||
 | 
			
		||||
API Versioning
 | 
			
		||||
##############
 | 
			
		||||
 | 
			
		||||
The REST API is versioned since Paperless-ngx 1.3.0.
 | 
			
		||||
 | 
			
		||||
* Versioning ensures that changes to the API don't break older clients.
 | 
			
		||||
* Clients specify the specific version of the API they wish to use with every request and Paperless will handle the request using the specified API version.
 | 
			
		||||
* Even if the underlying data model changes, older API versions will always serve compatible data.
 | 
			
		||||
* If no version is specified, Paperless will serve version 1 to ensure compatibility with older clients that do not request a specific API version.
 | 
			
		||||
 | 
			
		||||
API versions are specified by submitting an additional HTTP ``Accept`` header with every request:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    Accept: application/json; version=6
 | 
			
		||||
 | 
			
		||||
If an invalid version is specified, Paperless 1.3.0 will respond with "406 Not Acceptable" and an error message in the body.
 | 
			
		||||
Earlier versions of Paperless will serve API version 1 regardless of whether a version is specified via the ``Accept`` header.
 | 
			
		||||
 | 
			
		||||
If a client wishes to verify whether it is compatible with any given server, the following procedure should be performed:
 | 
			
		||||
 | 
			
		||||
1.  Perform an *authenticated* request against any API endpoint. If the server is on version 1.3.0 or newer, the server will
 | 
			
		||||
    add two custom headers to the response:
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        X-Api-Version: 2
 | 
			
		||||
        X-Version: 1.3.0
 | 
			
		||||
 | 
			
		||||
2.  Determine whether the client is compatible with this server based on the presence/absence of these headers and their values if present.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
API Changelog
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
Version 1
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
Initial API version.
 | 
			
		||||
 | 
			
		||||
Version 2
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
* Added field ``Tag.color``. This read/write string field contains a hex color such as ``#a6cee3``.
 | 
			
		||||
* Added read-only field ``Tag.text_color``. This field contains the text color to use for a specific tag, which is either black or white depending on the brightness of ``Tag.color``.
 | 
			
		||||
* Removed field ``Tag.colour``.
 | 
			
		||||
							
								
								
									
										36
									
								
								docs/assets/extra.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,36 @@
 | 
			
		||||
:root > * {
 | 
			
		||||
    --md-primary-fg-color: #17541f;
 | 
			
		||||
    --md-primary-fg-color--dark: #17541f;
 | 
			
		||||
    --md-primary-fg-color--light: #17541f;
 | 
			
		||||
    --md-accent-fg-color: #2b8a38;
 | 
			
		||||
    --md-typeset-a-color: #21652a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[data-md-color-scheme="slate"] {
 | 
			
		||||
    --md-hue: 222;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 400px) {
 | 
			
		||||
    .grid-left {
 | 
			
		||||
        width: 33%;
 | 
			
		||||
        float: left;
 | 
			
		||||
    }
 | 
			
		||||
    .grid-right {
 | 
			
		||||
        width: 62%;
 | 
			
		||||
        margin-left: 4%;
 | 
			
		||||
        float: left;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grid-left > p {
 | 
			
		||||
    margin-bottom: 2rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.grid-right p {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.index-callout {
 | 
			
		||||
    margin-right: .5rem;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								docs/assets/logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,12 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
	.st0{fill:#FFFFFF;}
 | 
			
		||||
</style>
 | 
			
		||||
<path class="st0" d="M299,891.7c-4.2-19.8-12.5-59.6-13.6-59.6c-176.7-105.7-155.8-288.7-97.3-393.4
 | 
			
		||||
	c12.5,131.8,245.8,222.8,109.8,383.9c-1.1,2,6.2,27.2,12.5,50.2c27.2-46,68-101.4,65.8-106.7C208.9,358.2,731.9,326.9,840.6,73.7
 | 
			
		||||
	c49.1,244.8-25.1,623.5-445.5,719.7c-2,1.1-76.3,131.8-79.5,132.9c0-2-31.4-1.1-27.2-11.5C290.7,908.4,294.8,900.1,299,891.7
 | 
			
		||||
	L299,891.7z M293.8,793.4c53.3-61.8-9.4-167.4-47.1-201.9C310.5,701.3,306.3,765.1,293.8,793.4L293.8,793.4z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 869 B  | 
							
								
								
									
										68
									
								
								docs/assets/logo_full_black.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,68 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 2962.2 860.2" style="enable-background:new 0 0 2962.2 860.2;" xml:space="preserve">
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
	.st0{fill:#17541F;stroke:#000000;stroke-miterlimit:10;}
 | 
			
		||||
</style>
 | 
			
		||||
<path d="M1055.6,639.7v-20.6c-18,20-43.1,30.1-75.4,30.1c-22.4,0-42.8-5.8-61-17.5c-18.3-11.7-32.5-27.8-42.9-48.3
 | 
			
		||||
	c-10.3-20.5-15.5-43.3-15.5-68.4c0-25.1,5.2-48,15.5-68.5s24.6-36.6,42.9-48.3s38.6-17.5,61-17.5c32.3,0,57.5,10,75.4,30.1v-20.6
 | 
			
		||||
	h85.3v249.6L1055.6,639.7L1055.6,639.7z M1059.1,514.9c0-17.4-5.2-31.9-15.5-43.8c-10.3-11.8-23.9-17.7-40.6-17.7
 | 
			
		||||
	c-16.8,0-30.2,5.9-40.4,17.7c-10.2,11.8-15.3,26.4-15.3,43.8c0,17.4,5.1,31.9,15.3,43.8c10.2,11.8,23.6,17.7,40.4,17.7
 | 
			
		||||
	c16.8,0,30.3-5.9,40.6-17.7C1054,546.9,1059.1,532.3,1059.1,514.9z"/>
 | 
			
		||||
<path d="M1417.8,398.2c18.3,11.7,32.5,27.8,42.9,48.3c10.3,20.5,15.5,43.3,15.5,68.5c0,25.1-5.2,48-15.5,68.4
 | 
			
		||||
	c-10.3,20.5-24.6,36.6-42.9,48.3s-38.6,17.5-61,17.5c-32.3,0-57.5-10-75.4-30.1v165.6h-85.3V390.2h85.3v20.6
 | 
			
		||||
	c18-20,43.1-30.1,75.4-30.1C1379.2,380.7,1399.5,386.6,1417.8,398.2z M1389.5,514.9c0-17.4-5.1-31.9-15.3-43.8
 | 
			
		||||
	c-10.2-11.8-23.6-17.7-40.4-17.7s-30.2,5.9-40.4,17.7c-10.2,11.8-15.3,26.4-15.3,43.8c0,17.4,5.1,31.9,15.3,43.8
 | 
			
		||||
	c10.2,11.8,23.6,17.7,40.4,17.7s30.2-5.9,40.4-17.7S1389.5,532.3,1389.5,514.9z"/>
 | 
			
		||||
<path d="M1713.6,555.3l53,49.4c-28.1,29.6-66.7,44.4-115.8,44.4c-28.1,0-53-5.8-74.5-17.5s-38.2-27.7-49.8-48
 | 
			
		||||
	c-11.7-20.3-17.7-43.2-18-68.7c0-24.8,5.9-47.5,17.7-68c11.8-20.5,28.1-36.7,48.7-48.5s43.5-17.7,68.7-17.7
 | 
			
		||||
	c24.8,0,47.6,6.1,68.2,18.2s37,29.5,49.1,52.3c12.1,22.7,18.2,49.1,18.2,79l-0.4,11.7h-181.8c3.6,11.4,10.5,20.7,20.9,28.1
 | 
			
		||||
	c10.3,7.3,21.3,11,33,11c14.4,0,26.3-2.2,35.7-6.5C1695.8,570.1,1704.9,563.7,1713.6,555.3z M1596.9,486.2h92.9
 | 
			
		||||
	c-2.1-12.3-7.5-22.1-16.2-29.4s-18.7-11-30.1-11s-21.5,3.7-30.3,11S1599,473.9,1596.9,486.2z"/>
 | 
			
		||||
<path d="M1908.8,418.4c7.8-10.8,17.2-19,28.3-24.7s22-8.5,32.8-8.5c11.4,0,20,1.6,26,4.9l-10.8,72.7c-8.4-2.1-15.7-3.1-22-3.1
 | 
			
		||||
	c-17.1,0-30.4,4.3-39.9,12.8c-9.6,8.5-14.4,24.2-14.4,46.9v120.3h-85.3V390.2h85.3V418.4L1908.8,418.4z"/>
 | 
			
		||||
<path d="M2113,258.2v381.5h-85.3V258.2H2113z"/>
 | 
			
		||||
<path d="M2360.8,555.3l53,49.4c-28.1,29.6-66.7,44.4-115.8,44.4c-28.1,0-53-5.8-74.5-17.5s-38.2-27.7-49.8-48
 | 
			
		||||
	c-11.7-20.3-17.7-43.2-18-68.7c0-24.8,5.9-47.5,17.7-68s28.1-36.7,48.7-48.5c20.6-11.8,43.5-17.7,68.7-17.7
 | 
			
		||||
	c24.8,0,47.6,6.1,68.2,18.2c20.6,12.1,37,29.5,49.1,52.3c12.1,22.7,18.2,49.1,18.2,79l-0.4,11.7h-181.8
 | 
			
		||||
	c3.6,11.4,10.5,20.7,20.9,28.1c10.3,7.3,21.3,11,33,11c14.4,0,26.3-2.2,35.7-6.5C2343.1,570.1,2352.1,563.7,2360.8,555.3z
 | 
			
		||||
	 M2244.1,486.2h92.9c-2.1-12.3-7.5-22.1-16.2-29.4s-18.7-11-30.1-11s-21.5,3.7-30.3,11C2251.7,464.1,2246.2,473.9,2244.1,486.2z"/>
 | 
			
		||||
<path d="M2565.9,446.3c-9.9,0-17.1,1.1-21.5,3.4c-4.5,2.2-6.7,5.9-6.7,11s3.4,8.8,10.3,11.2c6.9,2.4,18,4.9,33.2,7.6
 | 
			
		||||
	c20,3,37,6.7,50.9,11.2s26,12.1,36.1,22.9c10.2,10.8,15.3,25.9,15.3,45.3c0,29.9-10.9,52.4-32.8,67.6
 | 
			
		||||
	c-21.8,15.1-50.3,22.7-85.3,22.7c-25.7,0-49.5-3.7-71.4-11c-21.8-7.3-37.4-14.7-46.7-22.2l33.7-60.6c10.2,9,23.4,15.8,39.7,20.4
 | 
			
		||||
	c16.3,4.6,31.3,7,45.1,7c19.7,0,29.6-5.2,29.6-15.7c0-5.4-3.3-9.4-9.9-11.9c-6.6-2.5-17.2-5.2-31.9-7.9c-18.9-3.3-34.9-7.2-48-11.7
 | 
			
		||||
	c-13.2-4.5-24.6-12.2-34.3-23.1c-9.7-10.9-14.6-26-14.6-45.1c0-27.2,9.7-48.5,29-63.7c19.3-15.3,46-22.9,80.1-22.9
 | 
			
		||||
	c23.3,0,44.4,3.6,63.3,10.8c18.9,7.2,34,14.5,45.3,22l-32.8,58.8c-10.8-7.5-23.2-13.7-37.3-18.6
 | 
			
		||||
	C2590.5,448.7,2577.6,446.3,2565.9,446.3z"/>
 | 
			
		||||
<path d="M2817.3,446.3c-9.9,0-17.1,1.1-21.5,3.4c-4.5,2.2-6.7,5.9-6.7,11s3.4,8.8,10.3,11.2c6.9,2.4,18,4.9,33.2,7.6
 | 
			
		||||
	c20,3,37,6.7,50.9,11.2s26,12.1,36.1,22.9c10.2,10.8,15.3,25.9,15.3,45.3c0,29.9-10.9,52.4-32.8,67.6
 | 
			
		||||
	c-21.8,15.1-50.3,22.7-85.3,22.7c-25.7,0-49.5-3.7-71.4-11c-21.8-7.3-37.4-14.7-46.7-22.2l33.7-60.6c10.2,9,23.4,15.8,39.7,20.4
 | 
			
		||||
	c16.3,4.6,31.3,7,45.1,7c19.8,0,29.6-5.2,29.6-15.7c0-5.4-3.3-9.4-9.9-11.9c-6.6-2.5-17.2-5.2-31.9-7.9c-18.9-3.3-34.9-7.2-48-11.7
 | 
			
		||||
	c-13.2-4.5-24.6-12.2-34.3-23.1c-9.7-10.9-14.6-26-14.6-45.1c0-27.2,9.7-48.5,29-63.7c19.3-15.3,46-22.9,80.1-22.9
 | 
			
		||||
	c23.3,0,44.4,3.6,63.3,10.8c18.9,7.2,34,14.5,45.3,22l-32.8,58.8c-10.8-7.5-23.2-13.7-37.3-18.6
 | 
			
		||||
	C2841.8,448.7,2828.9,446.3,2817.3,446.3z"/>
 | 
			
		||||
<g>
 | 
			
		||||
	<path d="M2508,724h60.2v17.3H2508V724z"/>
 | 
			
		||||
	<path d="M2629.2,694.4c4.9-2,10.2-3.1,16-3.1c10.9,0,19.5,3.4,25.9,10.2s9.6,16.7,9.6,29.6v57.3h-19.6v-52.6
 | 
			
		||||
		c0-9.3-1.7-16.2-5.1-20.7c-3.4-4.5-9.1-6.7-17-6.7c-6.5,0-11.8,2.4-16.1,7.1c-4.3,4.8-6.4,11.5-6.4,20.2v52.6h-19.6v-94.6h19.6v9.5
 | 
			
		||||
		C2620.2,699.4,2624.4,696.4,2629.2,694.4z"/>
 | 
			
		||||
	<path d="M2790.3,833.2c-8.6,6.8-19.4,10.2-32.3,10.2c-7.9,0-15.2-1.4-21.9-4.1s-12.1-6.8-16.3-12.2s-6.6-11.9-7.1-19.6h19.6
 | 
			
		||||
		c0.7,6.1,3.5,10.8,8.4,13.9c4.9,3.2,10.7,4.8,17.4,4.8c7,0,13.1-2,18.2-6c5.1-4,7.7-10.3,7.7-18.9v-24.7c-3.6,3.4-8,6.2-13.3,8.2
 | 
			
		||||
		c-5.2,2.1-10.7,3.1-16.3,3.1c-8.7,0-16.6-2.1-23.7-6.4c-7.1-4.3-12.6-10-16.7-17.3c-4-7.3-6-15.5-6-24.6s2-17.3,6-24.7
 | 
			
		||||
		s9.6-13.2,16.7-17.4c7.1-4.3,15-6.4,23.7-6.4c5.7,0,11.1,1,16.3,3.1s9.6,4.8,13.3,8.2v-8.8h19.4v107.8
 | 
			
		||||
		C2803.2,815.9,2798.9,826.4,2790.3,833.2z M2782.2,755.7c2.6-4.7,3.8-10,3.8-15.9s-1.3-11.2-3.8-16c-2.6-4.8-6.1-8.5-10.5-11.1
 | 
			
		||||
		c-4.5-2.7-9.5-4-15.1-4c-5.8,0-10.9,1.4-15.4,4.3c-4.5,2.8-7.9,6.6-10.3,11.4c-2.4,4.8-3.6,9.9-3.6,15.5c0,5.4,1.2,10.5,3.6,15.3
 | 
			
		||||
		c2.4,4.8,5.8,8.6,10.3,11.5s9.6,4.3,15.4,4.3c5.6,0,10.6-1.4,15.1-4.1C2776.1,764.1,2779.6,760.4,2782.2,755.7z"/>
 | 
			
		||||
	<path d="M2843.5,788.4h-21.6l37.9-48l-36.4-46.6h22.6l25.7,33.3l25.8-33.3h21.6l-36.2,45.9l37.9,48.6h-22.6l-27.4-35L2843.5,788.4z
 | 
			
		||||
		"/>
 | 
			
		||||
</g>
 | 
			
		||||
<path d="M835.8,319.2c-11.5-18.9-27.4-33.7-47.6-44.7c-20.2-10.9-43-16.4-68.5-16.4h-90.6c-8.6,39.6-21.3,77.2-38,112.4
 | 
			
		||||
	c-10,21-21.3,41-33.9,59.9v209.2H647v-135h72.7c25.4,0,48.3-5.5,68.5-16.4s36.1-25.8,47.6-44.7c11.5-18.9,17.3-39.5,17.3-61.9
 | 
			
		||||
	C853.1,358.9,847.4,338.1,835.8,319.2z M747,416.6c-9.4,9-21.8,13.5-37,13.5l-62.8,0.4v-93.4l62.8-0.4c15.3,0,27.6,4.5,37,13.5
 | 
			
		||||
	s14.1,20,14.1,33.2C761.1,396.6,756.4,407.7,747,416.6z"/>
 | 
			
		||||
<path class="st0" d="M164.7,698.7c-3.5-16.5-10.4-49.6-11.3-49.6c-147.1-88-129.7-240.3-81-327.4C82.8,431.4,277,507.1,163.8,641.2
 | 
			
		||||
	c-0.9,1.7,5.2,22.6,10.4,41.8c22.6-38.3,56.6-84.4,54.8-88.8C89.7,254.7,525,228.6,615.5,17.9c40.9,203.7-20.9,518.9-370.8,599
 | 
			
		||||
	c-1.7,0.9-63.5,109.7-66.2,110.6c0-1.7-26.1-0.9-22.6-9.6C157.8,712.6,161.2,705.7,164.7,698.7L164.7,698.7z M160.4,616.9
 | 
			
		||||
	c44.4-51.4-7.8-139.3-39.2-168C174.3,540.2,170.8,593.3,160.4,616.9L160.4,616.9z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 6.3 KiB  | 
							
								
								
									
										69
									
								
								docs/assets/logo_full_white.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,69 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 | 
			
		||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
	 viewBox="0 0 2962.2 860.2" style="enable-background:new 0 0 2962.2 860.2;" xml:space="preserve">
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
	.st0{fill:#FFFFFF;stroke:#000000;stroke-miterlimit:10;}
 | 
			
		||||
	.st1{fill:#17541F;stroke:#000000;stroke-miterlimit:10;}
 | 
			
		||||
</style>
 | 
			
		||||
<path class="st0" d="M1055.6,639.7v-20.6c-18,20-43.1,30.1-75.4,30.1c-22.4,0-42.8-5.8-61-17.5c-18.3-11.7-32.5-27.8-42.9-48.3
 | 
			
		||||
	c-10.3-20.5-15.5-43.3-15.5-68.4c0-25.1,5.2-48,15.5-68.5s24.6-36.6,42.9-48.3s38.6-17.5,61-17.5c32.3,0,57.5,10,75.4,30.1v-20.6
 | 
			
		||||
	h85.3v249.6L1055.6,639.7L1055.6,639.7z M1059.1,514.9c0-17.4-5.2-31.9-15.5-43.8c-10.3-11.8-23.9-17.7-40.6-17.7
 | 
			
		||||
	c-16.8,0-30.2,5.9-40.4,17.7c-10.2,11.8-15.3,26.4-15.3,43.8c0,17.4,5.1,31.9,15.3,43.8c10.2,11.8,23.6,17.7,40.4,17.7
 | 
			
		||||
	c16.8,0,30.3-5.9,40.6-17.7C1054,546.9,1059.1,532.3,1059.1,514.9z"/>
 | 
			
		||||
<path class="st0" d="M1417.8,398.2c18.3,11.7,32.5,27.8,42.9,48.3c10.3,20.5,15.5,43.3,15.5,68.5c0,25.1-5.2,48-15.5,68.4
 | 
			
		||||
	c-10.3,20.5-24.6,36.6-42.9,48.3s-38.6,17.5-61,17.5c-32.3,0-57.5-10-75.4-30.1v165.6h-85.3V390.2h85.3v20.6
 | 
			
		||||
	c18-20,43.1-30.1,75.4-30.1C1379.2,380.7,1399.5,386.6,1417.8,398.2z M1389.5,514.9c0-17.4-5.1-31.9-15.3-43.8
 | 
			
		||||
	c-10.2-11.8-23.6-17.7-40.4-17.7s-30.2,5.9-40.4,17.7c-10.2,11.8-15.3,26.4-15.3,43.8c0,17.4,5.1,31.9,15.3,43.8
 | 
			
		||||
	c10.2,11.8,23.6,17.7,40.4,17.7s30.2-5.9,40.4-17.7S1389.5,532.3,1389.5,514.9z"/>
 | 
			
		||||
<path class="st0" d="M1713.6,555.3l53,49.4c-28.1,29.6-66.7,44.4-115.8,44.4c-28.1,0-53-5.8-74.5-17.5s-38.2-27.7-49.8-48
 | 
			
		||||
	c-11.7-20.3-17.7-43.2-18-68.7c0-24.8,5.9-47.5,17.7-68c11.8-20.5,28.1-36.7,48.7-48.5s43.5-17.7,68.7-17.7
 | 
			
		||||
	c24.8,0,47.6,6.1,68.2,18.2s37,29.5,49.1,52.3c12.1,22.7,18.2,49.1,18.2,79l-0.4,11.7h-181.8c3.6,11.4,10.5,20.7,20.9,28.1
 | 
			
		||||
	c10.3,7.3,21.3,11,33,11c14.4,0,26.3-2.2,35.7-6.5C1695.8,570.1,1704.9,563.7,1713.6,555.3z M1596.9,486.2h92.9
 | 
			
		||||
	c-2.1-12.3-7.5-22.1-16.2-29.4s-18.7-11-30.1-11s-21.5,3.7-30.3,11S1599,473.9,1596.9,486.2z"/>
 | 
			
		||||
<path class="st0" d="M1908.8,418.4c7.8-10.8,17.2-19,28.3-24.7s22-8.5,32.8-8.5c11.4,0,20,1.6,26,4.9l-10.8,72.7
 | 
			
		||||
	c-8.4-2.1-15.7-3.1-22-3.1c-17.1,0-30.4,4.3-39.9,12.8c-9.6,8.5-14.4,24.2-14.4,46.9v120.3h-85.3V390.2h85.3V418.4L1908.8,418.4z"/>
 | 
			
		||||
<path class="st0" d="M2113,258.2v381.5h-85.3V258.2H2113z"/>
 | 
			
		||||
<path class="st0" d="M2360.8,555.3l53,49.4c-28.1,29.6-66.7,44.4-115.8,44.4c-28.1,0-53-5.8-74.5-17.5s-38.2-27.7-49.8-48
 | 
			
		||||
	c-11.7-20.3-17.7-43.2-18-68.7c0-24.8,5.9-47.5,17.7-68s28.1-36.7,48.7-48.5c20.6-11.8,43.5-17.7,68.7-17.7
 | 
			
		||||
	c24.8,0,47.6,6.1,68.2,18.2c20.6,12.1,37,29.5,49.1,52.3c12.1,22.7,18.2,49.1,18.2,79l-0.4,11.7h-181.8
 | 
			
		||||
	c3.6,11.4,10.5,20.7,20.9,28.1c10.3,7.3,21.3,11,33,11c14.4,0,26.3-2.2,35.7-6.5C2343.1,570.1,2352.1,563.7,2360.8,555.3z
 | 
			
		||||
	 M2244.1,486.2h92.9c-2.1-12.3-7.5-22.1-16.2-29.4s-18.7-11-30.1-11s-21.5,3.7-30.3,11C2251.7,464.1,2246.2,473.9,2244.1,486.2z"/>
 | 
			
		||||
<path class="st0" d="M2565.9,446.3c-9.9,0-17.1,1.1-21.5,3.4c-4.5,2.2-6.7,5.9-6.7,11s3.4,8.8,10.3,11.2c6.9,2.4,18,4.9,33.2,7.6
 | 
			
		||||
	c20,3,37,6.7,50.9,11.2s26,12.1,36.1,22.9c10.2,10.8,15.3,25.9,15.3,45.3c0,29.9-10.9,52.4-32.8,67.6
 | 
			
		||||
	c-21.8,15.1-50.3,22.7-85.3,22.7c-25.7,0-49.5-3.7-71.4-11c-21.8-7.3-37.4-14.7-46.7-22.2l33.7-60.6c10.2,9,23.4,15.8,39.7,20.4
 | 
			
		||||
	c16.3,4.6,31.3,7,45.1,7c19.7,0,29.6-5.2,29.6-15.7c0-5.4-3.3-9.4-9.9-11.9c-6.6-2.5-17.2-5.2-31.9-7.9c-18.9-3.3-34.9-7.2-48-11.7
 | 
			
		||||
	c-13.2-4.5-24.6-12.2-34.3-23.1c-9.7-10.9-14.6-26-14.6-45.1c0-27.2,9.7-48.5,29-63.7c19.3-15.3,46-22.9,80.1-22.9
 | 
			
		||||
	c23.3,0,44.4,3.6,63.3,10.8c18.9,7.2,34,14.5,45.3,22l-32.8,58.8c-10.8-7.5-23.2-13.7-37.3-18.6
 | 
			
		||||
	C2590.5,448.7,2577.6,446.3,2565.9,446.3z"/>
 | 
			
		||||
<path class="st0" d="M2817.3,446.3c-9.9,0-17.1,1.1-21.5,3.4c-4.5,2.2-6.7,5.9-6.7,11s3.4,8.8,10.3,11.2c6.9,2.4,18,4.9,33.2,7.6
 | 
			
		||||
	c20,3,37,6.7,50.9,11.2s26,12.1,36.1,22.9c10.2,10.8,15.3,25.9,15.3,45.3c0,29.9-10.9,52.4-32.8,67.6
 | 
			
		||||
	c-21.8,15.1-50.3,22.7-85.3,22.7c-25.7,0-49.5-3.7-71.4-11c-21.8-7.3-37.4-14.7-46.7-22.2l33.7-60.6c10.2,9,23.4,15.8,39.7,20.4
 | 
			
		||||
	c16.3,4.6,31.3,7,45.1,7c19.8,0,29.6-5.2,29.6-15.7c0-5.4-3.3-9.4-9.9-11.9c-6.6-2.5-17.2-5.2-31.9-7.9c-18.9-3.3-34.9-7.2-48-11.7
 | 
			
		||||
	c-13.2-4.5-24.6-12.2-34.3-23.1c-9.7-10.9-14.6-26-14.6-45.1c0-27.2,9.7-48.5,29-63.7c19.3-15.3,46-22.9,80.1-22.9
 | 
			
		||||
	c23.3,0,44.4,3.6,63.3,10.8c18.9,7.2,34,14.5,45.3,22l-32.8,58.8c-10.8-7.5-23.2-13.7-37.3-18.6
 | 
			
		||||
	C2841.8,448.7,2828.9,446.3,2817.3,446.3z"/>
 | 
			
		||||
<g>
 | 
			
		||||
	<path class="st0" d="M2508,724h60.2v17.3H2508V724z"/>
 | 
			
		||||
	<path class="st0" d="M2629.2,694.4c4.9-2,10.2-3.1,16-3.1c10.9,0,19.5,3.4,25.9,10.2s9.6,16.7,9.6,29.6v57.3h-19.6v-52.6
 | 
			
		||||
		c0-9.3-1.7-16.2-5.1-20.7c-3.4-4.5-9.1-6.7-17-6.7c-6.5,0-11.8,2.4-16.1,7.1c-4.3,4.8-6.4,11.5-6.4,20.2v52.6h-19.6v-94.6h19.6v9.5
 | 
			
		||||
		C2620.2,699.4,2624.4,696.4,2629.2,694.4z"/>
 | 
			
		||||
	<path class="st0" d="M2790.3,833.2c-8.6,6.8-19.4,10.2-32.3,10.2c-7.9,0-15.2-1.4-21.9-4.1s-12.1-6.8-16.3-12.2s-6.6-11.9-7.1-19.6
 | 
			
		||||
		h19.6c0.7,6.1,3.5,10.8,8.4,13.9c4.9,3.2,10.7,4.8,17.4,4.8c7,0,13.1-2,18.2-6c5.1-4,7.7-10.3,7.7-18.9v-24.7
 | 
			
		||||
		c-3.6,3.4-8,6.2-13.3,8.2c-5.2,2.1-10.7,3.1-16.3,3.1c-8.7,0-16.6-2.1-23.7-6.4c-7.1-4.3-12.6-10-16.7-17.3c-4-7.3-6-15.5-6-24.6
 | 
			
		||||
		s2-17.3,6-24.7s9.6-13.2,16.7-17.4c7.1-4.3,15-6.4,23.7-6.4c5.7,0,11.1,1,16.3,3.1s9.6,4.8,13.3,8.2v-8.8h19.4v107.8
 | 
			
		||||
		C2803.2,815.9,2798.9,826.4,2790.3,833.2z M2782.2,755.7c2.6-4.7,3.8-10,3.8-15.9s-1.3-11.2-3.8-16c-2.6-4.8-6.1-8.5-10.5-11.1
 | 
			
		||||
		c-4.5-2.7-9.5-4-15.1-4c-5.8,0-10.9,1.4-15.4,4.3c-4.5,2.8-7.9,6.6-10.3,11.4c-2.4,4.8-3.6,9.9-3.6,15.5c0,5.4,1.2,10.5,3.6,15.3
 | 
			
		||||
		c2.4,4.8,5.8,8.6,10.3,11.5s9.6,4.3,15.4,4.3c5.6,0,10.6-1.4,15.1-4.1C2776.1,764.1,2779.6,760.4,2782.2,755.7z"/>
 | 
			
		||||
	<path class="st0" d="M2843.5,788.4h-21.6l37.9-48l-36.4-46.6h22.6l25.7,33.3l25.8-33.3h21.6l-36.2,45.9l37.9,48.6h-22.6l-27.4-35
 | 
			
		||||
		L2843.5,788.4z"/>
 | 
			
		||||
</g>
 | 
			
		||||
<path class="st0" d="M835.8,319.2c-11.5-18.9-27.4-33.7-47.6-44.7c-20.2-10.9-43-16.4-68.5-16.4h-90.6c-8.6,39.6-21.3,77.2-38,112.4
 | 
			
		||||
	c-10,21-21.3,41-33.9,59.9v209.2H647v-135h72.7c25.4,0,48.3-5.5,68.5-16.4s36.1-25.8,47.6-44.7c11.5-18.9,17.3-39.5,17.3-61.9
 | 
			
		||||
	C853.1,358.9,847.4,338.1,835.8,319.2z M747,416.6c-9.4,9-21.8,13.5-37,13.5l-62.8,0.4v-93.4l62.8-0.4c15.3,0,27.6,4.5,37,13.5
 | 
			
		||||
	s14.1,20,14.1,33.2C761.1,396.6,756.4,407.7,747,416.6z"/>
 | 
			
		||||
<path class="st1" d="M164.7,698.7c-3.5-16.5-10.4-49.6-11.3-49.6c-147.1-88-129.7-240.3-81-327.4C82.8,431.4,277,507.1,163.8,641.2
 | 
			
		||||
	c-0.9,1.7,5.2,22.6,10.4,41.8c22.6-38.3,56.6-84.4,54.8-88.8C89.7,254.7,525,228.6,615.5,17.9c40.9,203.7-20.9,518.9-370.8,599
 | 
			
		||||
	c-1.7,0.9-63.5,109.7-66.2,110.6c0-1.7-26.1-0.9-22.6-9.6C157.8,712.6,161.2,705.7,164.7,698.7L164.7,698.7z M160.4,616.9
 | 
			
		||||
	c44.4-51.4-7.8-139.3-39.2-168C174.3,540.2,170.8,593.3,160.4,616.9L160.4,616.9z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 6.5 KiB  | 
| 
		 Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB  | 
| 
		 Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 661 KiB  | 
| 
		 Before Width: | Height: | Size: 457 KiB After Width: | Height: | Size: 457 KiB  | 
| 
		 Before Width: | Height: | Size: 436 KiB After Width: | Height: | Size: 436 KiB  | 
| 
		 Before Width: | Height: | Size: 462 KiB After Width: | Height: | Size: 462 KiB  | 
| 
		 Before Width: | Height: | Size: 608 KiB After Width: | Height: | Size: 608 KiB  | 
| 
		 Before Width: | Height: | Size: 698 KiB After Width: | Height: | Size: 698 KiB  | 
| 
		 Before Width: | Height: | Size: 706 KiB After Width: | Height: | Size: 706 KiB  | 
| 
		 Before Width: | Height: | Size: 480 KiB After Width: | Height: | Size: 480 KiB  | 
| 
		 Before Width: | Height: | Size: 680 KiB After Width: | Height: | Size: 680 KiB  | 
| 
		 Before Width: | Height: | Size: 686 KiB After Width: | Height: | Size: 686 KiB  | 
| 
		 Before Width: | Height: | Size: 848 KiB After Width: | Height: | Size: 848 KiB  | 
| 
		 Before Width: | Height: | Size: 703 KiB After Width: | Height: | Size: 703 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/assets/screenshots/mail-rules-edited.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 76 KiB  | 
| 
		 Before Width: | Height: | Size: 388 KiB After Width: | Height: | Size: 388 KiB  | 
| 
		 Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB  | 
| 
		 Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB  | 
| 
		 Before Width: | Height: | Size: 517 KiB After Width: | Height: | Size: 517 KiB  | 
@ -1,5 +1,46 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## paperless-ngx 1.10.1
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
- Feature: Allows documents in WebP format [@stumpylog](https://github.com/stumpylog) ([#1984](https://github.com/paperless-ngx/paperless-ngx/pull/1984))
 | 
			
		||||
 | 
			
		||||
### Bug Fixes
 | 
			
		||||
 | 
			
		||||
- Fix: frontend tasks display in 1.10.0 [@shamoon](https://github.com/shamoon) ([#2073](https://github.com/paperless-ngx/paperless-ngx/pull/2073))
 | 
			
		||||
- Bugfix: Custom startup commands weren't run as root [@stumpylog](https://github.com/stumpylog) ([#2069](https://github.com/paperless-ngx/paperless-ngx/pull/2069))
 | 
			
		||||
- Bugfix: Add libatomic for armv7 compatibility [@stumpylog](https://github.com/stumpylog) ([#2066](https://github.com/paperless-ngx/paperless-ngx/pull/2066))
 | 
			
		||||
- Bugfix: Don't silence an exception when trying to handle file naming [@stumpylog](https://github.com/stumpylog) ([#2062](https://github.com/paperless-ngx/paperless-ngx/pull/2062))
 | 
			
		||||
- Bugfix: Some tesseract languages aren't detected as installed. [@stumpylog](https://github.com/stumpylog) ([#2057](https://github.com/paperless-ngx/paperless-ngx/pull/2057))
 | 
			
		||||
 | 
			
		||||
### Maintenance
 | 
			
		||||
 | 
			
		||||
- Chore: Use a maintained upload-release-asset [@stumpylog](https://github.com/stumpylog) ([#2055](https://github.com/paperless-ngx/paperless-ngx/pull/2055))
 | 
			
		||||
 | 
			
		||||
### Dependencies
 | 
			
		||||
 | 
			
		||||
  <details>
 | 
			
		||||
  <summary>5 changes</summary>
 | 
			
		||||
 | 
			
		||||
- Bump tslib from 2.4.0 to 2.4.1 in /src-ui @dependabot ([#2076](https://github.com/paperless-ngx/paperless-ngx/pull/2076))
 | 
			
		||||
- Bump @<!---->angular-builders/jest from 14.0.1 to 14.1.0 in /src-ui @dependabot ([#2079](https://github.com/paperless-ngx/paperless-ngx/pull/2079))
 | 
			
		||||
- Bump jest-preset-angular from 12.2.2 to 12.2.3 in /src-ui @dependabot ([#2078](https://github.com/paperless-ngx/paperless-ngx/pull/2078))
 | 
			
		||||
- Bump ngx-file-drop from 14.0.1 to 14.0.2 in /src-ui @dependabot ([#2080](https://github.com/paperless-ngx/paperless-ngx/pull/2080))
 | 
			
		||||
- Bump @<!---->ngneat/dirty-check-forms from 3.0.2 to 3.0.3 in /src-ui @dependabot ([#2077](https://github.com/paperless-ngx/paperless-ngx/pull/2077))
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
### All App Changes
 | 
			
		||||
 | 
			
		||||
- Bump tslib from 2.4.0 to 2.4.1 in /src-ui @dependabot ([#2076](https://github.com/paperless-ngx/paperless-ngx/pull/2076))
 | 
			
		||||
- Bump @<!---->angular-builders/jest from 14.0.1 to 14.1.0 in /src-ui @dependabot ([#2079](https://github.com/paperless-ngx/paperless-ngx/pull/2079))
 | 
			
		||||
- Bump jest-preset-angular from 12.2.2 to 12.2.3 in /src-ui @dependabot ([#2078](https://github.com/paperless-ngx/paperless-ngx/pull/2078))
 | 
			
		||||
- Bump ngx-file-drop from 14.0.1 to 14.0.2 in /src-ui @dependabot ([#2080](https://github.com/paperless-ngx/paperless-ngx/pull/2080))
 | 
			
		||||
- Bump @<!---->ngneat/dirty-check-forms from 3.0.2 to 3.0.3 in /src-ui @dependabot ([#2077](https://github.com/paperless-ngx/paperless-ngx/pull/2077))
 | 
			
		||||
- Fix: frontend tasks display in 1.10.0 [@shamoon](https://github.com/shamoon) ([#2073](https://github.com/paperless-ngx/paperless-ngx/pull/2073))
 | 
			
		||||
- Bugfix: Don't silence an exception when trying to handle file naming [@stumpylog](https://github.com/stumpylog) ([#2062](https://github.com/paperless-ngx/paperless-ngx/pull/2062))
 | 
			
		||||
- Bugfix: Some tesseract languages aren't detected as installed. [@stumpylog](https://github.com/stumpylog) ([#2057](https://github.com/paperless-ngx/paperless-ngx/pull/2057))
 | 
			
		||||
 | 
			
		||||
## paperless-ngx 1.10.0
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
@ -577,7 +618,7 @@
 | 
			
		||||
 | 
			
		||||
- Allow setting more than one tag in mail rules
 | 
			
		||||
  [\@jonasc](https://github.com/jonasc) ([\#270](https://github.com/paperless-ngx/paperless-ngx/pull/270))
 | 
			
		||||
- Global drag\'n\'drop [\@shamoon](https://github.com/shamoon)
 | 
			
		||||
- Global drag'n'drop [\@shamoon](https://github.com/shamoon)
 | 
			
		||||
  ([\#283](https://github.com/paperless-ngx/paperless-ngx/pull/283))
 | 
			
		||||
- Fix: download buttons should disable while waiting
 | 
			
		||||
  [\@shamoon](https://github.com/shamoon) ([\#630](https://github.com/paperless-ngx/paperless-ngx/pull/630))
 | 
			
		||||
@ -607,7 +648,7 @@
 | 
			
		||||
 | 
			
		||||
### Bug Fixes
 | 
			
		||||
 | 
			
		||||
- Add \"localhost\" to ALLOWED_HOSTS
 | 
			
		||||
- Add "localhost" to ALLOWED_HOSTS
 | 
			
		||||
  [\@gador](https://github.com/gador) ([\#700](https://github.com/paperless-ngx/paperless-ngx/pull/700))
 | 
			
		||||
- Fix: scanners table [\@qcasey](https://github.com/qcasey) ([\#690](https://github.com/paperless-ngx/paperless-ngx/pull/690))
 | 
			
		||||
- Adds wait for file before consuming
 | 
			
		||||
@ -644,7 +685,7 @@
 | 
			
		||||
  ([\#393](https://github.com/paperless-ngx/paperless-ngx/pull/393))
 | 
			
		||||
- Fix filterable dropdown buttons arent translated
 | 
			
		||||
  [\@shamoon](https://github.com/shamoon) ([\#366](https://github.com/paperless-ngx/paperless-ngx/pull/366))
 | 
			
		||||
- Fix 224: \"Auto-detected date is day before receipt date\"
 | 
			
		||||
- Fix 224: "Auto-detected date is day before receipt date"
 | 
			
		||||
  [\@a17t](https://github.com/a17t) ([\#246](https://github.com/paperless-ngx/paperless-ngx/pull/246))
 | 
			
		||||
- Fix minor sphinx errors [\@shamoon](https://github.com/shamoon)
 | 
			
		||||
  ([\#322](https://github.com/paperless-ngx/paperless-ngx/pull/322))
 | 
			
		||||
@ -706,7 +747,7 @@ This is the first release of the revived paperless-ngx project 🎉. Thank
 | 
			
		||||
you to everyone on the paperless-ngx team for your initiative and
 | 
			
		||||
excellent teamwork!
 | 
			
		||||
 | 
			
		||||
Version 1.6.0 merges several pending PRs from jonaswinkler\'s repo and
 | 
			
		||||
Version 1.6.0 merges several pending PRs from jonaswinkler's repo and
 | 
			
		||||
includes new feature updates and bug fixes. Major backend and UI changes
 | 
			
		||||
include:
 | 
			
		||||
 | 
			
		||||
@ -726,14 +767,14 @@ include:
 | 
			
		||||
  when document list is reloading ([jonaswinkler\#1297](https://github.com/jonaswinkler/paperless-ng/pull/1297)).
 | 
			
		||||
- [\@shamoon](https://github.com/shamoon) improved the PDF viewer on
 | 
			
		||||
  mobile ([\#2](https://github.com/paperless-ngx/paperless-ngx/pull/2)).
 | 
			
		||||
- [\@shamoon](https://github.com/shamoon) added \'any\' / \'all\' and
 | 
			
		||||
  \'not\' filtering with tags ([\#10](https://github.com/paperless-ngx/paperless-ngx/pull/10)).
 | 
			
		||||
- [\@shamoon](https://github.com/shamoon) added 'any' / 'all' and
 | 
			
		||||
  'not' filtering with tags ([\#10](https://github.com/paperless-ngx/paperless-ngx/pull/10)).
 | 
			
		||||
- [\@shamoon](https://github.com/shamoon) added warnings for unsaved
 | 
			
		||||
  changes, with smart edit buttons ([\#13](https://github.com/paperless-ngx/paperless-ngx/pull/13)).
 | 
			
		||||
- [\@benjaminfrank](https://github.com/benjaminfrank) enabled a
 | 
			
		||||
  non-root access to port 80 via systemd ([\#18](https://github.com/paperless-ngx/paperless-ngx/pull/18)).
 | 
			
		||||
- [\@tribut](https://github.com/tribut) added simple \"delete to
 | 
			
		||||
  trash\" functionality ([\#24](https://github.com/paperless-ngx/paperless-ngx/pull/24)). See `PAPERLESS_TRASH_DIR`.
 | 
			
		||||
- [\@tribut](https://github.com/tribut) added simple "delete to
 | 
			
		||||
  trash" functionality ([\#24](https://github.com/paperless-ngx/paperless-ngx/pull/24)). See `PAPERLESS_TRASH_DIR`.
 | 
			
		||||
- [\@amenk](https://github.com/amenk) fixed the search box overlay
 | 
			
		||||
  menu on mobile ([\#32](https://github.com/paperless-ngx/paperless-ngx/pull/32)).
 | 
			
		||||
- [\@dblitt](https://github.com/dblitt) updated the login form to not
 | 
			
		||||
@ -909,26 +950,22 @@ This is a maintenance release.
 | 
			
		||||
- Changes
 | 
			
		||||
  - Firefox only: Highlight search query in PDF previews.
 | 
			
		||||
  - New URL pattern for accessing documents by ASN directly
 | 
			
		||||
    (<http://>\<paperless\>/asn/123)
 | 
			
		||||
    (<http://><paperless>/asn/123)
 | 
			
		||||
  - Added logging when executing pre\* and post-consume scripts.
 | 
			
		||||
  - Better error logging during document consumption.
 | 
			
		||||
  - Updated python dependencies.
 | 
			
		||||
  - Automatically inserts typed text when opening \"Create new\"
 | 
			
		||||
  - Automatically inserts typed text when opening "Create new"
 | 
			
		||||
    dialogs on the document details page.
 | 
			
		||||
- Fixes
 | 
			
		||||
  - Fixed an issue with null characters in the document content.
 | 
			
		||||
 | 
			
		||||
::: {.note}
 | 
			
		||||
::: {.title}
 | 
			
		||||
Note
 | 
			
		||||
:::
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
The changed to the full text searching require you to reindex your
 | 
			
		||||
documents. _The docker image does this automatically, you don\'t need to
 | 
			
		||||
documents. _The docker image does this automatically, you don't need to
 | 
			
		||||
do anything._ To do this, execute the `document_index reindex`
 | 
			
		||||
management command (see `administration-index`{.interpreted-text
 | 
			
		||||
role="ref"}).
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### paperless-ng 1.3.2
 | 
			
		||||
 | 
			
		||||
@ -1031,7 +1068,7 @@ This release contains new database migrations.
 | 
			
		||||
  worker processes of the web server. See
 | 
			
		||||
  `configuration-docker`{.interpreted-text role="ref"}.
 | 
			
		||||
- Some more memory usage optimizations.
 | 
			
		||||
- Don\'t show inbox statistics if no inbox tag is defined.
 | 
			
		||||
- Don't show inbox statistics if no inbox tag is defined.
 | 
			
		||||
 | 
			
		||||
### paperless-ng 1.1.2
 | 
			
		||||
 | 
			
		||||
@ -1051,8 +1088,8 @@ This release contains new database migrations.
 | 
			
		||||
 | 
			
		||||
This release contains new database migrations.
 | 
			
		||||
 | 
			
		||||
- Fixed a bug in the sanity checker that would cause it to display \"x
 | 
			
		||||
  not in list\" errors instead of actual issues.
 | 
			
		||||
- Fixed a bug in the sanity checker that would cause it to display "x
 | 
			
		||||
  not in list" errors instead of actual issues.
 | 
			
		||||
- Fixed a bug with filename generation for archive filenames that
 | 
			
		||||
  would cause the archive files of two documents to overlap.
 | 
			
		||||
  - This happened when `PAPERLESS_FILENAME_FORMAT` is used and the
 | 
			
		||||
@ -1061,8 +1098,8 @@ This release contains new database migrations.
 | 
			
		||||
  - Paperless will now store the archive filename in the database as
 | 
			
		||||
    well instead of deriving it from the original filename, and use
 | 
			
		||||
    the same logic for detecting and avoiding filename clashes
 | 
			
		||||
    that\'s also used for original filenames.
 | 
			
		||||
  - The migrations will repair any missing archive files. If you\'re
 | 
			
		||||
    that's also used for original filenames.
 | 
			
		||||
  - The migrations will repair any missing archive files. If you're
 | 
			
		||||
    using tika, ensure that tika is running while performing the
 | 
			
		||||
    migration. Docker-compose will take care of that.
 | 
			
		||||
- Fixed a bug with thumbnail regeneration when TIKA integration was
 | 
			
		||||
@ -1070,8 +1107,8 @@ This release contains new database migrations.
 | 
			
		||||
- Added ASN as a placeholder field to the filename format.
 | 
			
		||||
- The docker image now comes with built-in shortcuts for most
 | 
			
		||||
  management commands. These are now the recommended way to execute
 | 
			
		||||
  management commands, since these also ensure that they\'re always
 | 
			
		||||
  executed as the paperless user and you\'re less likely to run into
 | 
			
		||||
  management commands, since these also ensure that they're always
 | 
			
		||||
  executed as the paperless user and you're less likely to run into
 | 
			
		||||
  permission issues. See
 | 
			
		||||
  `utilities-management-commands`{.interpreted-text role="ref"}.
 | 
			
		||||
 | 
			
		||||
@ -1093,10 +1130,7 @@ This release contains new database migrations.
 | 
			
		||||
- Live updates to document lists and saved views when new documents
 | 
			
		||||
  are added.
 | 
			
		||||
 | 
			
		||||
  ::: {.hint}
 | 
			
		||||
  ::: {.title}
 | 
			
		||||
  Hint
 | 
			
		||||
  :::
 | 
			
		||||
  !!! tip
 | 
			
		||||
 | 
			
		||||
  For status notifications and live updates to work, paperless now
 | 
			
		||||
  requires an [ASGI](https://asgi.readthedocs.io/en/latest/)-enabled
 | 
			
		||||
@ -1119,7 +1153,6 @@ This release contains new database migrations.
 | 
			
		||||
 | 
			
		||||
  Apache `mod_wsgi` users, see
 | 
			
		||||
  `this note <faq-mod_wsgi>`{.interpreted-text role="ref"}.
 | 
			
		||||
  :::
 | 
			
		||||
 | 
			
		||||
- Paperless now offers suggestions for tags, correspondents and types
 | 
			
		||||
  on the document detail page.
 | 
			
		||||
@ -1143,8 +1176,8 @@ This release contains new database migrations.
 | 
			
		||||
  - Better icon for document previews.
 | 
			
		||||
  - Better info section in the side bar.
 | 
			
		||||
  - Paperless no longer logs to the database. Instead, logs are
 | 
			
		||||
    written to rotating log files. This solves many \"database is
 | 
			
		||||
    locked\" issues on Raspberry Pi, especially when SQLite is used.
 | 
			
		||||
    written to rotating log files. This solves many "database is
 | 
			
		||||
    locked" issues on Raspberry Pi, especially when SQLite is used.
 | 
			
		||||
  - By default, log files are written to `PAPERLESS_DATA_DIR/log/`.
 | 
			
		||||
    Logging settings can be adjusted with `PAPERLESS_LOGGING_DIR`,
 | 
			
		||||
    `PAPERLESS_LOGROTATE_MAX_SIZE` and
 | 
			
		||||
@ -1173,7 +1206,7 @@ bug reports coming in, I think that this is reasonably stable.
 | 
			
		||||
  - Range selection with shift clicking is now possible in the
 | 
			
		||||
    document list.
 | 
			
		||||
  - Filtering correspondent, type and tag management pages by name.
 | 
			
		||||
  - Focus \"Name\" field in dialogs by default.
 | 
			
		||||
  - Focus "Name" field in dialogs by default.
 | 
			
		||||
 | 
			
		||||
### paperless-ng 0.9.14
 | 
			
		||||
 | 
			
		||||
@ -1235,8 +1268,8 @@ paperless.
 | 
			
		||||
  - Fixed an issue with filenames of downloaded files: Dates where
 | 
			
		||||
    off by one day due to timezone issues.
 | 
			
		||||
  - Searching will continue to work even when the index returns
 | 
			
		||||
    non-existing documents. This resulted in \"Document does not
 | 
			
		||||
    exist\" errors before. Instead, a warning is logged, indicating
 | 
			
		||||
    non-existing documents. This resulted in "Document does not
 | 
			
		||||
    exist" errors before. Instead, a warning is logged, indicating
 | 
			
		||||
    the issue.
 | 
			
		||||
  - An issue with the consumer crashing when invalid regular
 | 
			
		||||
    expression were used was fixed.
 | 
			
		||||
@ -1277,11 +1310,11 @@ paperless.
 | 
			
		||||
    new ASN to a document.
 | 
			
		||||
  - Form field validation: When providing invalid input in a form
 | 
			
		||||
    (such as a duplicate ASN or no name), paperless now has visual
 | 
			
		||||
    indicators and clearer error messages about what\'s wrong.
 | 
			
		||||
    indicators and clearer error messages about what's wrong.
 | 
			
		||||
  - Paperless disables buttons with network actions (such as save
 | 
			
		||||
    and delete) when a network action is active. This indicates that
 | 
			
		||||
    something is happening and prevents double clicking.
 | 
			
		||||
  - When using \"Save & next\", the title field is focussed
 | 
			
		||||
  - When using "Save & next", the title field is focussed
 | 
			
		||||
    automatically to better support keyboard editing.
 | 
			
		||||
  - E-Mail: Added filter rule parameters to allow inline attachments
 | 
			
		||||
    (watch out for mails with inlined images!) and attachment
 | 
			
		||||
@ -1290,11 +1323,11 @@ paperless.
 | 
			
		||||
    Shamoon](https://github.com/shamoon). This is useful for hiding
 | 
			
		||||
    Paperless behind single sign on applications such as
 | 
			
		||||
    [authelia](https://www.authelia.com/).
 | 
			
		||||
  - \"Clear filters\" has been renamed to \"Reset filters\" and now
 | 
			
		||||
  - "Clear filters" has been renamed to "Reset filters" and now
 | 
			
		||||
    correctly restores the default filters on saved views. Thanks to
 | 
			
		||||
    [Michael Shamoon](https://github.com/shamoon)
 | 
			
		||||
- Fixes
 | 
			
		||||
  - Paperless was unable to save views when \"Not assigned\" was
 | 
			
		||||
  - Paperless was unable to save views when "Not assigned" was
 | 
			
		||||
    chosen in one of the filter dropdowns.
 | 
			
		||||
  - Clearer error messages when pre and post consumption scripts do
 | 
			
		||||
    not exist.
 | 
			
		||||
@ -1310,7 +1343,7 @@ paperless.
 | 
			
		||||
### paperless-ng 0.9.10
 | 
			
		||||
 | 
			
		||||
- Bulk editing
 | 
			
		||||
  - Thanks to [Michael Shamoon](https://github.com/shamoon), we\'ve
 | 
			
		||||
  - Thanks to [Michael Shamoon](https://github.com/shamoon), we've
 | 
			
		||||
    got a new interface for the bulk editor.
 | 
			
		||||
  - There are some configuration options in the settings to alter
 | 
			
		||||
    the behavior.
 | 
			
		||||
@ -1319,7 +1352,7 @@ paperless.
 | 
			
		||||
    publishes a webmanifest, which is useful for adding the
 | 
			
		||||
    application to home screens on mobile devices.
 | 
			
		||||
  - The Paperless-ng logo now navigates to the dashboard.
 | 
			
		||||
  - Filter for documents that don\'t have any correspondents, types
 | 
			
		||||
  - Filter for documents that don't have any correspondents, types
 | 
			
		||||
    or tags assigned.
 | 
			
		||||
  - Tags, types and correspondents are now sorted case insensitive.
 | 
			
		||||
  - Lots of preparation work for localization support.
 | 
			
		||||
@ -1333,10 +1366,7 @@ paperless.
 | 
			
		||||
  - The consumer used to stop working when encountering an
 | 
			
		||||
    incomplete classifier model file.
 | 
			
		||||
 | 
			
		||||
::: {.note}
 | 
			
		||||
::: {.title}
 | 
			
		||||
Note
 | 
			
		||||
:::
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
The bulk delete operations did not update the search index. Therefore,
 | 
			
		||||
documents that you deleted remained in the index and caused the search
 | 
			
		||||
@ -1347,7 +1377,6 @@ However, this change is not retroactive: If you used the delete method
 | 
			
		||||
of the bulk editor, you need to reindex your search index by
 | 
			
		||||
`running the management command document_index with the argument reindex <administration-index>`{.interpreted-text
 | 
			
		||||
role="ref"}.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### paperless-ng 0.9.9
 | 
			
		||||
 | 
			
		||||
@ -1358,18 +1387,18 @@ Christmas release!
 | 
			
		||||
  - The following operations are available: Add and remove
 | 
			
		||||
    correspondents, tags, document types from selected documents, as
 | 
			
		||||
    well as mass-deleting documents.
 | 
			
		||||
  - We\'ve got a more fancy UI in the works that makes these
 | 
			
		||||
    features more accessible, but that\'s not quite ready yet.
 | 
			
		||||
  - We've got a more fancy UI in the works that makes these
 | 
			
		||||
    features more accessible, but that's not quite ready yet.
 | 
			
		||||
- Searching
 | 
			
		||||
  - Paperless now supports searching for similar documents (\"More
 | 
			
		||||
    like this\") both from the document detail page as well as from
 | 
			
		||||
  - Paperless now supports searching for similar documents ("More
 | 
			
		||||
    like this") both from the document detail page as well as from
 | 
			
		||||
    individual search results.
 | 
			
		||||
  - A search score indicates how well a document matches the search
 | 
			
		||||
    query, or how similar a document is to a given reference
 | 
			
		||||
    document.
 | 
			
		||||
- Other additions and changes
 | 
			
		||||
  - Clarification in the UI that the fields \"Match\" and \"Is
 | 
			
		||||
    insensitive\" are not relevant for the Auto matching algorithm.
 | 
			
		||||
  - Clarification in the UI that the fields "Match" and "Is
 | 
			
		||||
    insensitive" are not relevant for the Auto matching algorithm.
 | 
			
		||||
  - New select interface for tags, types and correspondents allows
 | 
			
		||||
    filtering. This also improves tag selection. Thanks again to
 | 
			
		||||
    [Michael Shamoon](https://github.com/shamoon)!
 | 
			
		||||
@ -1450,11 +1479,11 @@ This release focusses primarily on many small issues with the UI.
 | 
			
		||||
  - Paperless now has proper window titles.
 | 
			
		||||
  - Fixed an issue with the small cards when more than 7 tags were
 | 
			
		||||
    used.
 | 
			
		||||
  - Navigation of the \"Show all\" links adjusted. They navigate to
 | 
			
		||||
  - Navigation of the "Show all" links adjusted. They navigate to
 | 
			
		||||
    the saved view now, if available in the sidebar.
 | 
			
		||||
  - Some indication on the document lists that a filter is active
 | 
			
		||||
    was added.
 | 
			
		||||
  - There\'s a new filter to filter for documents that do _not_ have
 | 
			
		||||
  - There's a new filter to filter for documents that do _not_ have
 | 
			
		||||
    a certain tag.
 | 
			
		||||
  - The file upload box now shows upload progress.
 | 
			
		||||
  - The document edit page was reorganized.
 | 
			
		||||
@ -1479,15 +1508,11 @@ This release focusses primarily on many small issues with the UI.
 | 
			
		||||
    filenames anymore. It will rather append `_01`, `_02`, etc when
 | 
			
		||||
    it detects duplicate filenames.
 | 
			
		||||
 | 
			
		||||
::: {.note}
 | 
			
		||||
::: {.title}
 | 
			
		||||
Note
 | 
			
		||||
:::
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
The changes to the filename format will apply to newly added documents
 | 
			
		||||
and changed documents. If you want all files to reflect these changes,
 | 
			
		||||
execute the `document_renamer` management command.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### paperless-ng 0.9.5
 | 
			
		||||
 | 
			
		||||
@ -1570,7 +1595,7 @@ primarily.
 | 
			
		||||
    need to do this once, since the schema of the search index
 | 
			
		||||
    changed. Paperless keeps the index updated after that whenever
 | 
			
		||||
    something changes.
 | 
			
		||||
  - Paperless now has spelling corrections (\"Did you mean\") for
 | 
			
		||||
  - Paperless now has spelling corrections ("Did you mean") for
 | 
			
		||||
    miss-typed queries.
 | 
			
		||||
  - The documentation contains
 | 
			
		||||
    `information about the query syntax <basic-searching>`{.interpreted-text
 | 
			
		||||
@ -1640,7 +1665,7 @@ primarily.
 | 
			
		||||
  role="ref"} This features will most likely be removed in future
 | 
			
		||||
  versions.
 | 
			
		||||
- **Added:** New frontend. Features:
 | 
			
		||||
  - Single page application: It\'s much more responsive than the
 | 
			
		||||
  - Single page application: It's much more responsive than the
 | 
			
		||||
    django admin pages.
 | 
			
		||||
  - Dashboard. Shows recently scanned documents, or todo notes, or
 | 
			
		||||
    other documents at wish. Allows uploading of documents. Shows
 | 
			
		||||
@ -1662,7 +1687,7 @@ primarily.
 | 
			
		||||
- **Added:** Archive serial numbers. Assign these to quickly find
 | 
			
		||||
  documents stored in physical binders.
 | 
			
		||||
- **Added:** Enabled the internal user management of django. This
 | 
			
		||||
  isn\'t really a multi user solution, however, it allows more than
 | 
			
		||||
  isn't really a multi user solution, however, it allows more than
 | 
			
		||||
  one user to access the website and set some basic permissions /
 | 
			
		||||
  renew passwords.
 | 
			
		||||
- **Modified \[breaking\]:** All new mail consumer with customizable
 | 
			
		||||
@ -1717,7 +1742,7 @@ primarily.
 | 
			
		||||
- **Settings:**
 | 
			
		||||
  - `PAPERLESS_FORGIVING_OCR` is now default and gone. Reason: Even
 | 
			
		||||
    if `langdetect` fails to detect a language, tesseract still does
 | 
			
		||||
    a very good job at ocr\'ing a document with the default
 | 
			
		||||
    a very good job at ocr'ing a document with the default
 | 
			
		||||
    language. Certain language specifics such as umlauts may not get
 | 
			
		||||
    picked up properly.
 | 
			
		||||
  - `PAPERLESS_DEBUG` defaults to `false`.
 | 
			
		||||
@ -1798,34 +1823,34 @@ primarily.
 | 
			
		||||
  [\#442](https://github.com/the-paperless-project/paperless/pull/442).
 | 
			
		||||
- Added a `.editorconfig` file to better specify coding style.
 | 
			
		||||
- [Joshua Taillon](https://github.com/jat255) also added some logic to
 | 
			
		||||
  tie Paperless\' date guessing logic into how it parses file names on
 | 
			
		||||
  tie Paperless' date guessing logic into how it parses file names on
 | 
			
		||||
  import.
 | 
			
		||||
  [\#440](https://github.com/the-paperless-project/paperless/pull/440)
 | 
			
		||||
 | 
			
		||||
### 2.5.0
 | 
			
		||||
 | 
			
		||||
- **New dependency**: Paperless now optimises thumbnail generation
 | 
			
		||||
  with [optipng](http://optipng.sourceforge.net/), so you\'ll need to
 | 
			
		||||
  with [optipng](http://optipng.sourceforge.net/), so you'll need to
 | 
			
		||||
  install that somewhere in your PATH or declare its location in
 | 
			
		||||
  `PAPERLESS_OPTIPNG_BINARY`. The Docker image has already been
 | 
			
		||||
  updated on the Docker Hub, so you just need to pull the latest one
 | 
			
		||||
  from there if you\'re a Docker user.
 | 
			
		||||
- \"Login free\" instances of Paperless were breaking whenever you
 | 
			
		||||
  from there if you're a Docker user.
 | 
			
		||||
- "Login free" instances of Paperless were breaking whenever you
 | 
			
		||||
  tried to edit objects in the admin: adding/deleting tags or
 | 
			
		||||
  correspondents, or even fixing spelling. This was due to the \"user
 | 
			
		||||
  hack\" we were applying to sessions that weren\'t using a login, as
 | 
			
		||||
  that hack user didn\'t have a valid id. The fix was to attribute the
 | 
			
		||||
  correspondents, or even fixing spelling. This was due to the "user
 | 
			
		||||
  hack" we were applying to sessions that weren't using a login, as
 | 
			
		||||
  that hack user didn't have a valid id. The fix was to attribute the
 | 
			
		||||
  first user id in the system to this hack user.
 | 
			
		||||
  [\#394](https://github.com/the-paperless-project/paperless/issues/394)
 | 
			
		||||
- A problem in how we handle slug values on Tags and Correspondents
 | 
			
		||||
  required a few changes to how we handle this field
 | 
			
		||||
  [\#393](https://github.com/the-paperless-project/paperless/issues/393):
 | 
			
		||||
  1.  Slugs are no longer editable. They\'re derived from the name of
 | 
			
		||||
  1.  Slugs are no longer editable. They're derived from the name of
 | 
			
		||||
      the tag or correspondent at save time, so if you wanna change
 | 
			
		||||
      the slug, you have to change the name, and even then you\'re
 | 
			
		||||
      the slug, you have to change the name, and even then you're
 | 
			
		||||
      restricted to the rules of the `slugify()` function. The slug
 | 
			
		||||
      value is still visible in the admin though.
 | 
			
		||||
  2.  I\'ve added a migration to go over all existing tags &
 | 
			
		||||
  2.  I've added a migration to go over all existing tags &
 | 
			
		||||
      correspondents and rewrite the `.slug` values to ones conforming
 | 
			
		||||
      to the `slugify()` rules.
 | 
			
		||||
  3.  The consumption process now uses the same rules as `.save()` in
 | 
			
		||||
@ -1836,7 +1861,7 @@ primarily.
 | 
			
		||||
  Thanks to [Andrew Peng](https://github.com/pengc99) for reporting
 | 
			
		||||
  this.
 | 
			
		||||
  [\#414](https://github.com/the-paperless-project/paperless/issues/414).
 | 
			
		||||
- A bug in the Dockerfile meant that Tesseract language files weren\'t
 | 
			
		||||
- A bug in the Dockerfile meant that Tesseract language files weren't
 | 
			
		||||
  being installed correctly. [euri10](https://github.com/euri10) was
 | 
			
		||||
  quick to provide a fix:
 | 
			
		||||
  [\#406](https://github.com/the-paperless-project/paperless/issues/406),
 | 
			
		||||
@ -1851,13 +1876,13 @@ primarily.
 | 
			
		||||
### 2.4.0
 | 
			
		||||
 | 
			
		||||
- A new set of actions are now available thanks to
 | 
			
		||||
  [jonaswinkler](https://github.com/jonaswinkler)\'s very first pull
 | 
			
		||||
  [jonaswinkler](https://github.com/jonaswinkler)'s very first pull
 | 
			
		||||
  request! You can now do nifty things like tag documents in bulk, or
 | 
			
		||||
  set correspondents in bulk.
 | 
			
		||||
  [\#405](https://github.com/the-paperless-project/paperless/pull/405)
 | 
			
		||||
- The import/export system is now a little smarter. By default,
 | 
			
		||||
  documents are tagged as `unencrypted`, since exports are by their
 | 
			
		||||
  nature unencrypted. It\'s now in the import step that we decide the
 | 
			
		||||
  nature unencrypted. It's now in the import step that we decide the
 | 
			
		||||
  storage type. This allows you to export from an encrypted system and
 | 
			
		||||
  import into an unencrypted one, or vice-versa.
 | 
			
		||||
- The migration history has been slightly modified to accommodate
 | 
			
		||||
@ -1875,7 +1900,7 @@ primarily.
 | 
			
		||||
 | 
			
		||||
- Support for consuming plain text & markdown documents was added by
 | 
			
		||||
  [Joshua Taillon](https://github.com/jat255)! This was a
 | 
			
		||||
  long-requested feature, and it\'s addition is likely to be greatly
 | 
			
		||||
  long-requested feature, and it's addition is likely to be greatly
 | 
			
		||||
  appreciated by the community:
 | 
			
		||||
  [\#395](https://github.com/the-paperless-project/paperless/pull/395)
 | 
			
		||||
  Thanks also to [David Martin](https://github.com/ddddavidmartin) for
 | 
			
		||||
@ -1916,7 +1941,7 @@ primarily.
 | 
			
		||||
  lots of different tags:
 | 
			
		||||
  [\#391](https://github.com/the-paperless-project/paperless/pull/391).
 | 
			
		||||
- [Kilian Koeltzsch](https://github.com/kiliankoe) noticed a bug in
 | 
			
		||||
  how we capture & automatically create tags, so that\'s fixed now
 | 
			
		||||
  how we capture & automatically create tags, so that's fixed now
 | 
			
		||||
  too:
 | 
			
		||||
  [\#384](https://github.com/the-paperless-project/paperless/issues/384).
 | 
			
		||||
- [erikarvstedt](https://github.com/erikarvstedt) tweaked the
 | 
			
		||||
@ -1932,7 +1957,7 @@ primarily.
 | 
			
		||||
- [Enno Lohmeier](https://github.com/elohmeier) added three simple
 | 
			
		||||
  features that make Paperless a lot more user (and developer)
 | 
			
		||||
  friendly:
 | 
			
		||||
  1.  There\'s a new search box on the front page:
 | 
			
		||||
  1.  There's a new search box on the front page:
 | 
			
		||||
      [\#374](https://github.com/the-paperless-project/paperless/pull/374).
 | 
			
		||||
  2.  The correspondents & tags pages now have a column showing the
 | 
			
		||||
      number of relevant documents:
 | 
			
		||||
@ -1942,18 +1967,18 @@ primarily.
 | 
			
		||||
      environment:
 | 
			
		||||
      [\#376](https://github.com/the-paperless-project/paperless/pull/376).
 | 
			
		||||
- You now also have the ability to customise the interface to your
 | 
			
		||||
  heart\'s content by creating a file called `overrides.css` and/or
 | 
			
		||||
  heart's content by creating a file called `overrides.css` and/or
 | 
			
		||||
  `overrides.js` in the root of your media directory. Thanks to [Mark
 | 
			
		||||
  McFate](https://github.com/SummittDweller) for this idea:
 | 
			
		||||
  [\#371](https://github.com/the-paperless-project/paperless/issues/371)
 | 
			
		||||
 | 
			
		||||
### 2.0.0
 | 
			
		||||
 | 
			
		||||
This is a big release as we\'ve changed a core-functionality of
 | 
			
		||||
This is a big release as we've changed a core-functionality of
 | 
			
		||||
Paperless: we no longer encrypt files with GPG by default.
 | 
			
		||||
 | 
			
		||||
The reasons for this are many, but it boils down to that the encryption
 | 
			
		||||
wasn\'t really all that useful, as files on-disk were still accessible
 | 
			
		||||
wasn't really all that useful, as files on-disk were still accessible
 | 
			
		||||
so long as you had the key, and the key was most typically stored in the
 | 
			
		||||
config file. In other words, your files are only as safe as the
 | 
			
		||||
`paperless` user is. In addition to that, _the contents of the documents
 | 
			
		||||
@ -1965,7 +1990,7 @@ explicitly set a passphrase in your config file.
 | 
			
		||||
 | 
			
		||||
### Migrating from 1.x
 | 
			
		||||
 | 
			
		||||
Encryption isn\'t gone, it\'s just off for new users. So long as you
 | 
			
		||||
Encryption isn't gone, it's just off for new users. So long as you
 | 
			
		||||
have `PAPERLESS_PASSPHRASE` set in your config or your environment,
 | 
			
		||||
Paperless should continue to operate as it always has. If however, you
 | 
			
		||||
want to drop encryption too, you only need to do two things:
 | 
			
		||||
@ -1995,7 +2020,7 @@ this big change.
 | 
			
		||||
  for more information.
 | 
			
		||||
- Refactor the use of travis/tox/pytest/coverage into two files:
 | 
			
		||||
  `.travis.yml` and `setup.cfg`.
 | 
			
		||||
- Start generating requirements.txt from a Pipfile. I\'ll probably
 | 
			
		||||
- Start generating requirements.txt from a Pipfile. I'll probably
 | 
			
		||||
  switch over to just using pipenv in the future.
 | 
			
		||||
- All for a alternative FreeBSD-friendly location for
 | 
			
		||||
  `paperless.conf`. Thanks to [Martin
 | 
			
		||||
@ -2015,7 +2040,7 @@ this big change.
 | 
			
		||||
  [\#253](https://github.com/the-paperless-project/paperless/issues/253)
 | 
			
		||||
  and
 | 
			
		||||
  [\#323](https://github.com/the-paperless-project/paperless/issues/323),
 | 
			
		||||
  we\'ve removed a few of the hardcoded URL values to make it easier
 | 
			
		||||
  we've removed a few of the hardcoded URL values to make it easier
 | 
			
		||||
  for people to host Paperless on a subdirectory. Thanks to [Quentin
 | 
			
		||||
  Dawans](https://github.com/ovv) and [Kyle
 | 
			
		||||
  Lucy](https://github.com/kmlucy) for helping to work this out.
 | 
			
		||||
@ -2028,7 +2053,7 @@ this big change.
 | 
			
		||||
  very creating Bash skills:
 | 
			
		||||
  [\#352](https://github.com/the-paperless-project/paperless/pull/352).
 | 
			
		||||
- You can now use the search field to find documents by tag thanks to
 | 
			
		||||
  [thinkjk](https://github.com/thinkjk)\'s _first ever issue_:
 | 
			
		||||
  [thinkjk](https://github.com/thinkjk)'s _first ever issue_:
 | 
			
		||||
  [\#354](https://github.com/the-paperless-project/paperless/issues/354).
 | 
			
		||||
- Inotify is now being used to detect additions to the consume
 | 
			
		||||
  directory thanks to some excellent work from
 | 
			
		||||
@ -2037,7 +2062,7 @@ this big change.
 | 
			
		||||
 | 
			
		||||
### 1.3.0
 | 
			
		||||
 | 
			
		||||
- You can now run Paperless without a login, though you\'ll still have
 | 
			
		||||
- You can now run Paperless without a login, though you'll still have
 | 
			
		||||
  to create at least one user. This is thanks to a pull-request from
 | 
			
		||||
  [matthewmoto](https://github.com/matthewmoto):
 | 
			
		||||
  [\#295](https://github.com/the-paperless-project/paperless/pull/295).
 | 
			
		||||
@ -2068,7 +2093,7 @@ this big change.
 | 
			
		||||
  [\#312](https://github.com/the-paperless-project/paperless/pull/312)
 | 
			
		||||
  to fix
 | 
			
		||||
  [\#306](https://github.com/the-paperless-project/paperless/issues/306).
 | 
			
		||||
- Patch the historical migrations to support MySQL\'s um,
 | 
			
		||||
- Patch the historical migrations to support MySQL's um,
 | 
			
		||||
  _interesting_ way of handing indexes
 | 
			
		||||
  ([\#308](https://github.com/the-paperless-project/paperless/issues/308)).
 | 
			
		||||
  Thanks to [Simon Taddiken](https://github.com/skuzzle) for reporting
 | 
			
		||||
@ -2090,7 +2115,7 @@ this big change.
 | 
			
		||||
  already contains text. This can be overridden by setting
 | 
			
		||||
  `PAPERLESS_OCR_ALWAYS=YES` either in your `paperless.conf` or in the
 | 
			
		||||
  environment. Note that this also means that Paperless now requires
 | 
			
		||||
  `libpoppler-cpp-dev` to be installed. **Important**: You\'ll need to
 | 
			
		||||
  `libpoppler-cpp-dev` to be installed. **Important**: You'll need to
 | 
			
		||||
  run `pip install -r requirements.txt` after the usual `git pull` to
 | 
			
		||||
  properly update.
 | 
			
		||||
- [BastianPoe](https://github.com/BastianPoe) has also contributed a
 | 
			
		||||
@ -2117,7 +2142,7 @@ this big change.
 | 
			
		||||
 | 
			
		||||
### 1.0.0
 | 
			
		||||
 | 
			
		||||
- Upgrade to Django 1.11. **You\'ll need to run \`\`pip install -r
 | 
			
		||||
- Upgrade to Django 1.11. **You'll need to run \`\`pip install -r
 | 
			
		||||
  requirements.txt\`\` after the usual \`\`git pull\`\` to properly
 | 
			
		||||
  update**.
 | 
			
		||||
- Replace the templatetag-based hack we had for document listing in
 | 
			
		||||
@ -2138,14 +2163,14 @@ this big change.
 | 
			
		||||
  [Pit](https://github.com/pitkley) on
 | 
			
		||||
  [\#268](https://github.com/the-paperless-project/paperless/pull/268).
 | 
			
		||||
- Date fields in the admin are now expressed as HTML5 date fields
 | 
			
		||||
  thanks to [Lukas Winkler](https://github.com/Findus23)\'s issue
 | 
			
		||||
  thanks to [Lukas Winkler](https://github.com/Findus23)'s issue
 | 
			
		||||
  [\#278](https://github.com/the-paperless-project/paperless/issues/248)
 | 
			
		||||
 | 
			
		||||
### 0.8.0
 | 
			
		||||
 | 
			
		||||
- Paperless can now run in a subdirectory on a host (`/paperless`),
 | 
			
		||||
  rather than always running in the root (`/`) thanks to
 | 
			
		||||
  [maphy-psd](https://github.com/maphy-psd)\'s work on
 | 
			
		||||
  [maphy-psd](https://github.com/maphy-psd)'s work on
 | 
			
		||||
  [\#255](https://github.com/the-paperless-project/paperless/pull/255).
 | 
			
		||||
 | 
			
		||||
### 0.7.0
 | 
			
		||||
@ -2154,14 +2179,14 @@ this big change.
 | 
			
		||||
  [\#235](https://github.com/the-paperless-project/paperless/issues/235),
 | 
			
		||||
  Paperless will no longer automatically delete documents attached to
 | 
			
		||||
  correspondents when those correspondents are themselves deleted.
 | 
			
		||||
  This was Django\'s default behaviour, but didn\'t make much sense in
 | 
			
		||||
  Paperless\' case. Thanks to [Thomas
 | 
			
		||||
  This was Django's default behaviour, but didn't make much sense in
 | 
			
		||||
  Paperless' case. Thanks to [Thomas
 | 
			
		||||
  Brueggemann](https://github.com/thomasbrueggemann) and [David
 | 
			
		||||
  Martin](https://github.com/ddddavidmartin) for their input on this
 | 
			
		||||
  one.
 | 
			
		||||
- Fix for
 | 
			
		||||
  [\#232](https://github.com/the-paperless-project/paperless/issues/232)
 | 
			
		||||
  wherein Paperless wasn\'t recognising `.tif` files properly. Thanks
 | 
			
		||||
  wherein Paperless wasn't recognising `.tif` files properly. Thanks
 | 
			
		||||
  to [ayounggun](https://github.com/ayounggun) for reporting this one
 | 
			
		||||
  and to [Kusti Skytén](https://github.com/kskyten) for posting the
 | 
			
		||||
  correct solution in the Github issue.
 | 
			
		||||
@ -2172,12 +2197,12 @@ this big change.
 | 
			
		||||
  favour of BasicAuth or Django session.
 | 
			
		||||
- Fix the POST API so it actually works.
 | 
			
		||||
  [\#236](https://github.com/the-paperless-project/paperless/issues/236)
 | 
			
		||||
- **Breaking change**: We\'ve dropped the use of
 | 
			
		||||
- **Breaking change**: We've dropped the use of
 | 
			
		||||
  `PAPERLESS_SHARED_SECRET` as it was being used both for the API (now
 | 
			
		||||
  replaced with a normal auth) and form email polling. Now that we\'re
 | 
			
		||||
  replaced with a normal auth) and form email polling. Now that we're
 | 
			
		||||
  only using it for email, this variable has been renamed to
 | 
			
		||||
  `PAPERLESS_EMAIL_SECRET`. The old value will still work for a while,
 | 
			
		||||
  but you should change your config if you\'ve been using the email
 | 
			
		||||
  but you should change your config if you've been using the email
 | 
			
		||||
  polling feature. Thanks to [Joshua
 | 
			
		||||
  Gilman](https://github.com/jmgilman) for all the help with this
 | 
			
		||||
  feature.
 | 
			
		||||
@ -2185,7 +2210,7 @@ this big change.
 | 
			
		||||
### 0.5.0
 | 
			
		||||
 | 
			
		||||
- Support for fuzzy matching in the auto-tagger & auto-correspondent
 | 
			
		||||
  systems thanks to [Jake Gysland](https://github.com/jgysland)\'s
 | 
			
		||||
  systems thanks to [Jake Gysland](https://github.com/jgysland)'s
 | 
			
		||||
  patch
 | 
			
		||||
  [\#220](https://github.com/the-paperless-project/paperless/pull/220).
 | 
			
		||||
- Modified the Dockerfile to prepare an export directory
 | 
			
		||||
@ -2214,7 +2239,7 @@ this big change.
 | 
			
		||||
 | 
			
		||||
- Fix for
 | 
			
		||||
  [\#206](https://github.com/the-paperless-project/paperless/issues/206)
 | 
			
		||||
  wherein the pluggable parser didn\'t recognise files with all-caps
 | 
			
		||||
  wherein the pluggable parser didn't recognise files with all-caps
 | 
			
		||||
  suffixes like `.PDF`
 | 
			
		||||
 | 
			
		||||
### 0.4.0
 | 
			
		||||
@ -2224,7 +2249,7 @@ this big change.
 | 
			
		||||
  for more information, but the short explanation is that you can now
 | 
			
		||||
  attach simple notes & times to documents which are made available
 | 
			
		||||
  via the API. Currently, the default API (basically just the Django
 | 
			
		||||
  admin) doesn\'t really make use of this, but [Thomas
 | 
			
		||||
  admin) doesn't really make use of this, but [Thomas
 | 
			
		||||
  Brueggemann](https://github.com/thomasbrueggemann) over at
 | 
			
		||||
  [Paperless
 | 
			
		||||
  Desktop](https://github.com/thomasbrueggemann/paperless-desktop) has
 | 
			
		||||
@ -2234,16 +2259,16 @@ this big change.
 | 
			
		||||
 | 
			
		||||
- Fix for
 | 
			
		||||
  [\#200](https://github.com/the-paperless-project/paperless/issues/200)
 | 
			
		||||
  (!!) where the API wasn\'t configured to allow updating the
 | 
			
		||||
  (!!) where the API wasn't configured to allow updating the
 | 
			
		||||
  correspondent or the tags for a document.
 | 
			
		||||
- The `content` field is now optional, to allow for the edge case of a
 | 
			
		||||
  purely graphical document.
 | 
			
		||||
- You can no longer add documents via the admin. This never worked in
 | 
			
		||||
  the first place, so all I\'ve done here is remove the link to the
 | 
			
		||||
  the first place, so all I've done here is remove the link to the
 | 
			
		||||
  broken form.
 | 
			
		||||
- The consumer code has been heavily refactored to support a pluggable
 | 
			
		||||
  interface. Install a paperless consumer via pip and tell paperless
 | 
			
		||||
  about it with an environment variable, and you\'re good to go.
 | 
			
		||||
  about it with an environment variable, and you're good to go.
 | 
			
		||||
  Proper documentation is on its way.
 | 
			
		||||
 | 
			
		||||
### 0.3.5
 | 
			
		||||
@ -2264,10 +2289,10 @@ this big change.
 | 
			
		||||
- Removal of django-suit due to a licensing conflict I bumped into in
 | 
			
		||||
  0.3.3. Note that you _can_ use Django Suit with Paperless, but only
 | 
			
		||||
  in a non-profit situation as their free license prohibits for-profit
 | 
			
		||||
  use. As a result, I can\'t bundle Suit with Paperless without
 | 
			
		||||
  use. As a result, I can't bundle Suit with Paperless without
 | 
			
		||||
  conflicting with the GPL. Further development will be done against
 | 
			
		||||
  the stock Django admin.
 | 
			
		||||
- I shrunk the thumbnails a little \'cause they were too big for me,
 | 
			
		||||
- I shrunk the thumbnails a little 'cause they were too big for me,
 | 
			
		||||
  even on my high-DPI monitor.
 | 
			
		||||
- BasicAuth support for document and thumbnail downloads, as well as
 | 
			
		||||
  the Push API thanks to \@thomasbrueggemann. See
 | 
			
		||||
@ -2294,14 +2319,14 @@ this big change.
 | 
			
		||||
### 0.3.0
 | 
			
		||||
 | 
			
		||||
- Updated to using django-filter 1.x
 | 
			
		||||
- Added some system checks so new users aren\'t confused by
 | 
			
		||||
- Added some system checks so new users aren't confused by
 | 
			
		||||
  misconfigurations.
 | 
			
		||||
- Consumer loop time is now configurable for systems with slow writes.
 | 
			
		||||
  Just set `PAPERLESS_CONSUMER_LOOP_TIME` to a number of seconds. The
 | 
			
		||||
  default is 10.
 | 
			
		||||
- As per
 | 
			
		||||
  [\#44](https://github.com/the-paperless-project/paperless/issues/44),
 | 
			
		||||
  we\'ve removed support for `PAPERLESS_CONVERT`, `PAPERLESS_CONSUME`,
 | 
			
		||||
  we've removed support for `PAPERLESS_CONVERT`, `PAPERLESS_CONSUME`,
 | 
			
		||||
  and `PAPERLESS_SECRET`. Please use `PAPERLESS_CONVERT_BINARY`,
 | 
			
		||||
  `PAPERLESS_CONSUMPTION_DIR`, and `PAPERLESS_SHARED_SECRET`
 | 
			
		||||
  respectively instead.
 | 
			
		||||
@ -2316,17 +2341,17 @@ this big change.
 | 
			
		||||
- [\#146](https://github.com/the-paperless-project/paperless/issues/146):
 | 
			
		||||
  Fixed a bug that allowed unauthorised access to the `/fetch` URL.
 | 
			
		||||
- [\#131](https://github.com/the-paperless-project/paperless/issues/131):
 | 
			
		||||
  Document files are now automatically removed from disk when they\'re
 | 
			
		||||
  Document files are now automatically removed from disk when they're
 | 
			
		||||
  deleted in Paperless.
 | 
			
		||||
- [\#121](https://github.com/the-paperless-project/paperless/issues/121):
 | 
			
		||||
  Fixed a bug where Paperless wasn\'t setting document creation time
 | 
			
		||||
  Fixed a bug where Paperless wasn't setting document creation time
 | 
			
		||||
  based on the file naming scheme.
 | 
			
		||||
- [\#81](https://github.com/the-paperless-project/paperless/issues/81):
 | 
			
		||||
  Added a hook to run an arbitrary script after every document is
 | 
			
		||||
  consumed.
 | 
			
		||||
- [\#98](https://github.com/the-paperless-project/paperless/issues/98):
 | 
			
		||||
  Added optional environment variables for ImageMagick so that it
 | 
			
		||||
  doesn\'t explode when handling Very Large Documents or when it\'s
 | 
			
		||||
  doesn't explode when handling Very Large Documents or when it's
 | 
			
		||||
  just running on a low-memory system. Thanks to [Florian
 | 
			
		||||
  Harr](https://github.com/evils) for his help on this one.
 | 
			
		||||
- [\#89](https://github.com/the-paperless-project/paperless/issues/89)
 | 
			
		||||
@ -2345,8 +2370,8 @@ this big change.
 | 
			
		||||
 | 
			
		||||
### 0.1.1
 | 
			
		||||
 | 
			
		||||
- Potentially **Breaking Change**: All references to \"sender\" in the
 | 
			
		||||
  code have been renamed to \"correspondent\" to better reflect the
 | 
			
		||||
- Potentially **Breaking Change**: All references to "sender" in the
 | 
			
		||||
  code have been renamed to "correspondent" to better reflect the
 | 
			
		||||
  nature of the property (one could quite reasonably scan a document
 | 
			
		||||
  before sending it to someone.)
 | 
			
		||||
- [\#67](https://github.com/the-paperless-project/paperless/issues/67):
 | 
			
		||||
@ -2360,7 +2385,7 @@ this big change.
 | 
			
		||||
  contributing conversation that lead to this change.
 | 
			
		||||
- [\#20](https://github.com/the-paperless-project/paperless/issues/20):
 | 
			
		||||
  Added _unpaper_ support to help in cleaning up the scanned image
 | 
			
		||||
  before it\'s OCR\'d. Thanks to [Pit](https://github.com/pitkley) for
 | 
			
		||||
  before it's OCR'd. Thanks to [Pit](https://github.com/pitkley) for
 | 
			
		||||
  this one.
 | 
			
		||||
- [\#71](https://github.com/the-paperless-project/paperless/issues/71)
 | 
			
		||||
  Added (encrypted) thumbnails in anticipation of a proper UI.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										337
									
								
								docs/conf.py
									
									
									
									
									
								
							
							
						
						@ -1,337 +0,0 @@
 | 
			
		||||
import sphinx_rtd_theme
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__version__ = None
 | 
			
		||||
__full_version_str__ = None
 | 
			
		||||
__major_minor_version_str__ = None
 | 
			
		||||
exec(open("../src/paperless/version.py").read())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
extensions = [
 | 
			
		||||
    "sphinx.ext.autodoc",
 | 
			
		||||
    "sphinx.ext.intersphinx",
 | 
			
		||||
    "sphinx.ext.todo",
 | 
			
		||||
    "sphinx.ext.imgmath",
 | 
			
		||||
    "sphinx.ext.viewcode",
 | 
			
		||||
    "sphinx_rtd_theme",
 | 
			
		||||
    "myst_parser",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain templates here, relative to this directory.
 | 
			
		||||
templates_path = ["_templates"]
 | 
			
		||||
 | 
			
		||||
# The suffix of source filenames.
 | 
			
		||||
source_suffix = {
 | 
			
		||||
    ".rst": "restructuredtext",
 | 
			
		||||
    ".md": "markdown",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# The encoding of source files.
 | 
			
		||||
# source_encoding = 'utf-8-sig'
 | 
			
		||||
 | 
			
		||||
# The master toctree document.
 | 
			
		||||
master_doc = "index"
 | 
			
		||||
 | 
			
		||||
# General information about the project.
 | 
			
		||||
project = "Paperless-ngx"
 | 
			
		||||
copyright = "2015-2022, Daniel Quinn, Jonas Winkler, and the paperless-ngx team"
 | 
			
		||||
 | 
			
		||||
# The version info for the project you're documenting, acts as replacement for
 | 
			
		||||
# |version| and |release|, also used in various other places throughout the
 | 
			
		||||
# built documents.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# If the build process ever explodes here, it's because you've set the version
 | 
			
		||||
# number in paperless.version to a tuple with 3 numbers in it.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# The short X.Y version.
 | 
			
		||||
version = __major_minor_version_str__
 | 
			
		||||
# The full version, including alpha/beta/rc tags.
 | 
			
		||||
release = __full_version_str__
 | 
			
		||||
 | 
			
		||||
# The language for content autogenerated by Sphinx. Refer to documentation
 | 
			
		||||
# for a list of supported languages.
 | 
			
		||||
# language = None
 | 
			
		||||
 | 
			
		||||
# There are two options for replacing |today|: either, you set today to some
 | 
			
		||||
# non-false value, then it is used:
 | 
			
		||||
# today = ''
 | 
			
		||||
# Else, today_fmt is used as the format for a strftime call.
 | 
			
		||||
# today_fmt = '%B %d, %Y'
 | 
			
		||||
 | 
			
		||||
# List of patterns, relative to source directory, that match files and
 | 
			
		||||
# directories to ignore when looking for source files.
 | 
			
		||||
exclude_patterns = ["_build"]
 | 
			
		||||
 | 
			
		||||
# The reST default role (used for this markup: `text`) to use for all
 | 
			
		||||
# documents.
 | 
			
		||||
# default_role = None
 | 
			
		||||
 | 
			
		||||
# If true, '()' will be appended to :func: etc. cross-reference text.
 | 
			
		||||
# add_function_parentheses = True
 | 
			
		||||
 | 
			
		||||
# If true, the current module name will be prepended to all description
 | 
			
		||||
# unit titles (such as .. function::).
 | 
			
		||||
# add_module_names = True
 | 
			
		||||
 | 
			
		||||
# If true, sectionauthor and moduleauthor directives will be shown in the
 | 
			
		||||
# output. They are ignored by default.
 | 
			
		||||
# show_authors = False
 | 
			
		||||
 | 
			
		||||
# The name of the Pygments (syntax highlighting) style to use.
 | 
			
		||||
pygments_style = "sphinx"
 | 
			
		||||
 | 
			
		||||
# A list of ignored prefixes for module index sorting.
 | 
			
		||||
# modindex_common_prefix = []
 | 
			
		||||
 | 
			
		||||
# If true, keep warnings as "system message" paragraphs in the built documents.
 | 
			
		||||
# keep_warnings = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for HTML output ----------------------------------------------
 | 
			
		||||
 | 
			
		||||
# The theme to use for HTML and HTML Help pages.  See the documentation for
 | 
			
		||||
# a list of builtin themes.
 | 
			
		||||
html_theme = "sphinx_rtd_theme"
 | 
			
		||||
 | 
			
		||||
# Theme options are theme-specific and customize the look and feel of a theme
 | 
			
		||||
# further.  For a list of options available for each theme, see the
 | 
			
		||||
# documentation.
 | 
			
		||||
# html_theme_options = {}
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain custom themes here, relative to this directory.
 | 
			
		||||
html_theme_path = []
 | 
			
		||||
 | 
			
		||||
# The name for this set of Sphinx documents.  If None, it defaults to
 | 
			
		||||
# "<project> v<release> documentation".
 | 
			
		||||
# html_title = None
 | 
			
		||||
 | 
			
		||||
# A shorter title for the navigation bar.  Default is the same as html_title.
 | 
			
		||||
# html_short_title = None
 | 
			
		||||
 | 
			
		||||
# The name of an image file (relative to this directory) to place at the top
 | 
			
		||||
# of the sidebar.
 | 
			
		||||
# html_logo = None
 | 
			
		||||
 | 
			
		||||
# The name of an image file (within the static path) to use as favicon of the
 | 
			
		||||
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 | 
			
		||||
# pixels large.
 | 
			
		||||
# html_favicon = None
 | 
			
		||||
 | 
			
		||||
# Add any paths that contain custom static files (such as style sheets) here,
 | 
			
		||||
# relative to this directory. They are copied after the builtin static files,
 | 
			
		||||
# so a file named "default.css" will overwrite the builtin "default.css".
 | 
			
		||||
html_static_path = ["_static"]
 | 
			
		||||
 | 
			
		||||
# These paths are either relative to html_static_path
 | 
			
		||||
# or fully qualified paths (eg. https://...)
 | 
			
		||||
html_css_files = [
 | 
			
		||||
    "css/custom.css",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
html_js_files = [
 | 
			
		||||
    "js/darkmode.js",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# Add any extra paths that contain custom files (such as robots.txt or
 | 
			
		||||
# .htaccess) here, relative to this directory. These files are copied
 | 
			
		||||
# directly to the root of the documentation.
 | 
			
		||||
# html_extra_path = []
 | 
			
		||||
 | 
			
		||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 | 
			
		||||
# using the given strftime format.
 | 
			
		||||
# html_last_updated_fmt = '%b %d, %Y'
 | 
			
		||||
 | 
			
		||||
# If true, SmartyPants will be used to convert quotes and dashes to
 | 
			
		||||
# typographically correct entities.
 | 
			
		||||
# html_use_smartypants = True
 | 
			
		||||
 | 
			
		||||
# Custom sidebar templates, maps document names to template names.
 | 
			
		||||
# html_sidebars = {}
 | 
			
		||||
 | 
			
		||||
# Additional templates that should be rendered to pages, maps page names to
 | 
			
		||||
# template names.
 | 
			
		||||
# html_additional_pages = {}
 | 
			
		||||
 | 
			
		||||
# If false, no module index is generated.
 | 
			
		||||
# html_domain_indices = True
 | 
			
		||||
 | 
			
		||||
# If false, no index is generated.
 | 
			
		||||
# html_use_index = True
 | 
			
		||||
 | 
			
		||||
# If true, the index is split into individual pages for each letter.
 | 
			
		||||
# html_split_index = False
 | 
			
		||||
 | 
			
		||||
# If true, links to the reST sources are added to the pages.
 | 
			
		||||
# html_show_sourcelink = True
 | 
			
		||||
 | 
			
		||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
 | 
			
		||||
# html_show_sphinx = True
 | 
			
		||||
 | 
			
		||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
 | 
			
		||||
# html_show_copyright = True
 | 
			
		||||
 | 
			
		||||
# If true, an OpenSearch description file will be output, and all pages will
 | 
			
		||||
# contain a <link> tag referring to it.  The value of this option must be the
 | 
			
		||||
# base URL from which the finished HTML is served.
 | 
			
		||||
# html_use_opensearch = ''
 | 
			
		||||
 | 
			
		||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
 | 
			
		||||
# html_file_suffix = None
 | 
			
		||||
 | 
			
		||||
# Output file base name for HTML help builder.
 | 
			
		||||
htmlhelp_basename = "paperless"
 | 
			
		||||
 | 
			
		||||
# -- Options for LaTeX output ---------------------------------------------
 | 
			
		||||
 | 
			
		||||
latex_elements = {
 | 
			
		||||
    # The paper size ('letterpaper' or 'a4paper').
 | 
			
		||||
    #'papersize': 'letterpaper',
 | 
			
		||||
    # The font size ('10pt', '11pt' or '12pt').
 | 
			
		||||
    #'pointsize': '10pt',
 | 
			
		||||
    # Additional stuff for the LaTeX preamble.
 | 
			
		||||
    #'preamble': '',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into LaTeX files. List of tuples
 | 
			
		||||
# (source start file, target name, title,
 | 
			
		||||
#  author, documentclass [howto, manual, or own class]).
 | 
			
		||||
latex_documents = [
 | 
			
		||||
    ("index", "paperless.tex", "Paperless Documentation", "Daniel Quinn", "manual"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# The name of an image file (relative to this directory) to place at the top of
 | 
			
		||||
# the title page.
 | 
			
		||||
# latex_logo = None
 | 
			
		||||
 | 
			
		||||
# For "manual" documents, if this is true, then toplevel headings are parts,
 | 
			
		||||
# not chapters.
 | 
			
		||||
# latex_use_parts = False
 | 
			
		||||
 | 
			
		||||
# If true, show page references after internal links.
 | 
			
		||||
# latex_show_pagerefs = False
 | 
			
		||||
 | 
			
		||||
# If true, show URL addresses after external links.
 | 
			
		||||
# latex_show_urls = False
 | 
			
		||||
 | 
			
		||||
# Documents to append as an appendix to all manuals.
 | 
			
		||||
# latex_appendices = []
 | 
			
		||||
 | 
			
		||||
# If false, no module index is generated.
 | 
			
		||||
# latex_domain_indices = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for manual page output ---------------------------------------
 | 
			
		||||
 | 
			
		||||
# One entry per manual page. List of tuples
 | 
			
		||||
# (source start file, name, description, authors, manual section).
 | 
			
		||||
man_pages = [("index", "paperless", "Paperless Documentation", ["Daniel Quinn"], 1)]
 | 
			
		||||
 | 
			
		||||
# If true, show URL addresses after external links.
 | 
			
		||||
# man_show_urls = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for Texinfo output -------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Grouping the document tree into Texinfo files. List of tuples
 | 
			
		||||
# (source start file, target name, title, author,
 | 
			
		||||
#  dir menu entry, description, category)
 | 
			
		||||
texinfo_documents = [
 | 
			
		||||
    (
 | 
			
		||||
        "index",
 | 
			
		||||
        "Paperless",
 | 
			
		||||
        "Paperless Documentation",
 | 
			
		||||
        "Daniel Quinn",
 | 
			
		||||
        "paperless",
 | 
			
		||||
        "Scan, index, and archive all of your paper documents.",
 | 
			
		||||
        "Miscellaneous",
 | 
			
		||||
    ),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# Documents to append as an appendix to all manuals.
 | 
			
		||||
# texinfo_appendices = []
 | 
			
		||||
 | 
			
		||||
# If false, no module index is generated.
 | 
			
		||||
# texinfo_domain_indices = True
 | 
			
		||||
 | 
			
		||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
 | 
			
		||||
# texinfo_show_urls = 'footnote'
 | 
			
		||||
 | 
			
		||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
 | 
			
		||||
# texinfo_no_detailmenu = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# -- Options for Epub output ----------------------------------------------
 | 
			
		||||
 | 
			
		||||
# Bibliographic Dublin Core info.
 | 
			
		||||
epub_title = "Paperless"
 | 
			
		||||
epub_author = "Daniel Quinn"
 | 
			
		||||
epub_publisher = "Daniel Quinn"
 | 
			
		||||
epub_copyright = "2015, Daniel Quinn"
 | 
			
		||||
 | 
			
		||||
# The basename for the epub file. It defaults to the project name.
 | 
			
		||||
# epub_basename = u'Paperless'
 | 
			
		||||
 | 
			
		||||
# The HTML theme for the epub output. Since the default themes are not optimized
 | 
			
		||||
# for small screen space, using the same theme for HTML and epub output is
 | 
			
		||||
# usually not wise. This defaults to 'epub', a theme designed to save visual
 | 
			
		||||
# space.
 | 
			
		||||
# epub_theme = 'epub'
 | 
			
		||||
 | 
			
		||||
# The language of the text. It defaults to the language option
 | 
			
		||||
# or en if the language is not set.
 | 
			
		||||
# epub_language = ''
 | 
			
		||||
 | 
			
		||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
 | 
			
		||||
# epub_scheme = ''
 | 
			
		||||
 | 
			
		||||
# The unique identifier of the text. This can be a ISBN number
 | 
			
		||||
# or the project homepage.
 | 
			
		||||
# epub_identifier = ''
 | 
			
		||||
 | 
			
		||||
# A unique identification for the text.
 | 
			
		||||
# epub_uid = ''
 | 
			
		||||
 | 
			
		||||
# A tuple containing the cover image and cover page html template filenames.
 | 
			
		||||
# epub_cover = ()
 | 
			
		||||
 | 
			
		||||
# A sequence of (type, uri, title) tuples for the guide element of content.opf.
 | 
			
		||||
# epub_guide = ()
 | 
			
		||||
 | 
			
		||||
# HTML files that should be inserted before the pages created by sphinx.
 | 
			
		||||
# The format is a list of tuples containing the path and title.
 | 
			
		||||
# epub_pre_files = []
 | 
			
		||||
 | 
			
		||||
# HTML files shat should be inserted after the pages created by sphinx.
 | 
			
		||||
# The format is a list of tuples containing the path and title.
 | 
			
		||||
# epub_post_files = []
 | 
			
		||||
 | 
			
		||||
# A list of files that should not be packed into the epub file.
 | 
			
		||||
epub_exclude_files = ["search.html"]
 | 
			
		||||
 | 
			
		||||
# The depth of the table of contents in toc.ncx.
 | 
			
		||||
# epub_tocdepth = 3
 | 
			
		||||
 | 
			
		||||
# Allow duplicate toc entries.
 | 
			
		||||
# epub_tocdup = True
 | 
			
		||||
 | 
			
		||||
# Choose between 'default' and 'includehidden'.
 | 
			
		||||
# epub_tocscope = 'default'
 | 
			
		||||
 | 
			
		||||
# Fix unsupported image types using the PIL.
 | 
			
		||||
# epub_fix_images = False
 | 
			
		||||
 | 
			
		||||
# Scale large images.
 | 
			
		||||
# epub_max_image_width = 0
 | 
			
		||||
 | 
			
		||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
 | 
			
		||||
# epub_show_urls = 'inline'
 | 
			
		||||
 | 
			
		||||
# If false, no index is generated.
 | 
			
		||||
# epub_use_index = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Example configuration for intersphinx: refer to the Python standard library.
 | 
			
		||||
intersphinx_mapping = {"http://docs.python.org/": None}
 | 
			
		||||
							
								
								
									
										1037
									
								
								docs/configuration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -1,931 +0,0 @@
 | 
			
		||||
.. _configuration:
 | 
			
		||||
 | 
			
		||||
*************
 | 
			
		||||
Configuration
 | 
			
		||||
*************
 | 
			
		||||
 | 
			
		||||
Paperless provides a wide range of customizations.
 | 
			
		||||
Depending on how you run paperless, these settings have to be defined in different
 | 
			
		||||
places.
 | 
			
		||||
 | 
			
		||||
*   If you run paperless on docker, ``paperless.conf`` is not used. Rather, configure
 | 
			
		||||
    paperless by copying necessary options to ``docker-compose.env``.
 | 
			
		||||
*   If you are running paperless on anything else, paperless will search for the
 | 
			
		||||
    configuration file in these locations and use the first one it finds:
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        /path/to/paperless/paperless.conf
 | 
			
		||||
        /etc/paperless.conf
 | 
			
		||||
        /usr/local/etc/paperless.conf
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Required services
 | 
			
		||||
#################
 | 
			
		||||
 | 
			
		||||
PAPERLESS_REDIS=<url>
 | 
			
		||||
    This is required for processing scheduled tasks such as email fetching, index
 | 
			
		||||
    optimization and for training the automatic document matcher.
 | 
			
		||||
 | 
			
		||||
    * If your Redis server needs login credentials PAPERLESS_REDIS = ``redis://<username>:<password>@<host>:<port>``
 | 
			
		||||
 | 
			
		||||
    * With the requirepass option PAPERLESS_REDIS = ``redis://:<password>@<host>:<port>``
 | 
			
		||||
 | 
			
		||||
    `More information on securing your Redis Instance <https://redis.io/docs/getting-started/#securing-redis>`_.
 | 
			
		||||
 | 
			
		||||
    Defaults to redis://localhost:6379.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DBENGINE=<engine_name>
 | 
			
		||||
    Optional, gives the ability to choose Postgres or MariaDB for database engine.
 | 
			
		||||
    Available options are `postgresql` and `mariadb`.
 | 
			
		||||
 | 
			
		||||
    Default is `postgresql`.
 | 
			
		||||
 | 
			
		||||
    .. warning::
 | 
			
		||||
 | 
			
		||||
      Using MariaDB comes with some caveats.  See :ref:`advanced-mysql-caveats` for details.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DBHOST=<hostname>
 | 
			
		||||
    By default, sqlite is used as the database backend. This can be changed here.
 | 
			
		||||
 | 
			
		||||
    Set PAPERLESS_DBHOST and another database will be used instead of sqlite.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DBPORT=<port>
 | 
			
		||||
    Adjust port if necessary.
 | 
			
		||||
 | 
			
		||||
    Default is 5432.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DBNAME=<name>
 | 
			
		||||
    Database name in PostgreSQL or MariaDB.
 | 
			
		||||
 | 
			
		||||
    Defaults to "paperless".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DBUSER=<name>
 | 
			
		||||
    Database user in PostgreSQL or MariaDB.
 | 
			
		||||
 | 
			
		||||
    Defaults to "paperless".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DBPASS=<password>
 | 
			
		||||
    Database password for PostgreSQL or MariaDB.
 | 
			
		||||
 | 
			
		||||
    Defaults to "paperless".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DBSSLMODE=<mode>
 | 
			
		||||
    SSL mode to use when connecting to PostgreSQL.
 | 
			
		||||
 | 
			
		||||
    See `the official documentation about sslmode <https://www.postgresql.org/docs/current/libpq-ssl.html>`_.
 | 
			
		||||
 | 
			
		||||
    Default is ``prefer``.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DB_TIMEOUT=<float>
 | 
			
		||||
    Amount of time for a database connection to wait for the database to unlock.
 | 
			
		||||
    Mostly applicable for an sqlite based installation, consider changing to postgresql
 | 
			
		||||
    if you need to increase this.
 | 
			
		||||
 | 
			
		||||
    Defaults to unset, keeping the Django defaults.
 | 
			
		||||
 | 
			
		||||
Paths and folders
 | 
			
		||||
#################
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMPTION_DIR=<path>
 | 
			
		||||
    This where your documents should go to be consumed.  Make sure that it exists
 | 
			
		||||
    and that the user running the paperless service can read/write its contents
 | 
			
		||||
    before you start Paperless.
 | 
			
		||||
 | 
			
		||||
    Don't change this when using docker, as it only changes the path within the
 | 
			
		||||
    container. Change the local consumption directory in the docker-compose.yml
 | 
			
		||||
    file instead.
 | 
			
		||||
 | 
			
		||||
    Defaults to "../consume/", relative to the "src" directory.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DATA_DIR=<path>
 | 
			
		||||
    This is where paperless stores all its data (search index, SQLite database,
 | 
			
		||||
    classification model, etc).
 | 
			
		||||
 | 
			
		||||
    Defaults to "../data/", relative to the "src" directory.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_TRASH_DIR=<path>
 | 
			
		||||
    Instead of removing deleted documents, they are moved to this directory.
 | 
			
		||||
 | 
			
		||||
    This must be writeable by the user running paperless. When running inside
 | 
			
		||||
    docker, ensure that this path is within a permanent volume (such as
 | 
			
		||||
    "../media/trash") so it won't get lost on upgrades.
 | 
			
		||||
 | 
			
		||||
    Defaults to empty (i.e. really delete documents).
 | 
			
		||||
 | 
			
		||||
PAPERLESS_MEDIA_ROOT=<path>
 | 
			
		||||
    This is where your documents and thumbnails are stored.
 | 
			
		||||
 | 
			
		||||
    You can set this and PAPERLESS_DATA_DIR to the same folder to have paperless
 | 
			
		||||
    store all its data within the same volume.
 | 
			
		||||
 | 
			
		||||
    Defaults to "../media/", relative to the "src" directory.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_STATICDIR=<path>
 | 
			
		||||
    Override the default STATIC_ROOT here.  This is where all static files
 | 
			
		||||
    created using "collectstatic" manager command are stored.
 | 
			
		||||
 | 
			
		||||
    Unless you're doing something fancy, there is no need to override this.
 | 
			
		||||
 | 
			
		||||
    Defaults to "../static/", relative to the "src" directory.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_FILENAME_FORMAT=<format>
 | 
			
		||||
    Changes the filenames paperless uses to store documents in the media directory.
 | 
			
		||||
    See :ref:`advanced-file_name_handling` for details.
 | 
			
		||||
 | 
			
		||||
    Default is none, which disables this feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_FILENAME_FORMAT_REMOVE_NONE=<bool>
 | 
			
		||||
    Tells paperless to replace placeholders in `PAPERLESS_FILENAME_FORMAT` that would resolve
 | 
			
		||||
    to 'none' to be omitted from the resulting filename. This also holds true for directory
 | 
			
		||||
    names.
 | 
			
		||||
    See :ref:`advanced-file_name_handling` for details.
 | 
			
		||||
 | 
			
		||||
    Defaults to `false` which disables this feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_LOGGING_DIR=<path>
 | 
			
		||||
    This is where paperless will store log files.
 | 
			
		||||
 | 
			
		||||
    Defaults to "``PAPERLESS_DATA_DIR``/log/".
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Logging
 | 
			
		||||
#######
 | 
			
		||||
 | 
			
		||||
PAPERLESS_LOGROTATE_MAX_SIZE=<num>
 | 
			
		||||
    Maximum file size for log files before they are rotated, in bytes.
 | 
			
		||||
 | 
			
		||||
    Defaults to 1 MiB.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_LOGROTATE_MAX_BACKUPS=<num>
 | 
			
		||||
    Number of rotated log files to keep.
 | 
			
		||||
 | 
			
		||||
    Defaults to 20.
 | 
			
		||||
 | 
			
		||||
.. _hosting-and-security:
 | 
			
		||||
 | 
			
		||||
Hosting & Security
 | 
			
		||||
##################
 | 
			
		||||
 | 
			
		||||
PAPERLESS_SECRET_KEY=<key>
 | 
			
		||||
    Paperless uses this to make session tokens. If you expose paperless on the
 | 
			
		||||
    internet, you need to change this, since the default secret is well known.
 | 
			
		||||
 | 
			
		||||
    Use any sequence of characters. The more, the better. You don't need to
 | 
			
		||||
    remember this. Just face-roll your keyboard.
 | 
			
		||||
 | 
			
		||||
    Default is listed in the file ``src/paperless/settings.py``.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_URL=<url>
 | 
			
		||||
    This setting can be used to set the three options below (ALLOWED_HOSTS,
 | 
			
		||||
    CORS_ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS). If the other options are
 | 
			
		||||
    set the values will be combined with this one. Do not include a trailing
 | 
			
		||||
    slash. E.g. https://paperless.domain.com
 | 
			
		||||
 | 
			
		||||
    Defaults to empty string, leaving the other settings unaffected.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CSRF_TRUSTED_ORIGINS=<comma-separated-list>
 | 
			
		||||
    A list of trusted origins for unsafe requests (e.g. POST). As of Django 4.0
 | 
			
		||||
    this is required to access the Django admin via the web.
 | 
			
		||||
    See https://docs.djangoproject.com/en/4.0/ref/settings/#csrf-trusted-origins
 | 
			
		||||
 | 
			
		||||
    Can also be set using PAPERLESS_URL (see above).
 | 
			
		||||
 | 
			
		||||
    Defaults to empty string, which does not add any origins to the trusted list.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ALLOWED_HOSTS=<comma-separated-list>
 | 
			
		||||
    If you're planning on putting Paperless on the open internet, then you
 | 
			
		||||
    really should set this value to the domain name you're using.  Failing to do
 | 
			
		||||
    so leaves you open to HTTP host header attacks:
 | 
			
		||||
    https://docs.djangoproject.com/en/3.1/topics/security/#host-header-validation
 | 
			
		||||
 | 
			
		||||
    Just remember that this is a comma-separated list, so "example.com" is fine,
 | 
			
		||||
    as is "example.com,www.example.com", but NOT " example.com" or "example.com,"
 | 
			
		||||
 | 
			
		||||
    Can also be set using PAPERLESS_URL (see above).
 | 
			
		||||
 | 
			
		||||
    If manually set, please remember to include "localhost". Otherwise docker
 | 
			
		||||
    healthcheck will fail.
 | 
			
		||||
 | 
			
		||||
    Defaults to "*", which is all hosts.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CORS_ALLOWED_HOSTS=<comma-separated-list>
 | 
			
		||||
    You need to add your servers to the list of allowed hosts that can do CORS
 | 
			
		||||
    calls. Set this to your public domain name.
 | 
			
		||||
 | 
			
		||||
    Can also be set using PAPERLESS_URL (see above).
 | 
			
		||||
 | 
			
		||||
    Defaults to "http://localhost:8000".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_FORCE_SCRIPT_NAME=<path>
 | 
			
		||||
    To host paperless under a subpath url like example.com/paperless you set
 | 
			
		||||
    this value to /paperless. No trailing slash!
 | 
			
		||||
 | 
			
		||||
    Defaults to none, which hosts paperless at "/".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_STATIC_URL=<path>
 | 
			
		||||
    Override the STATIC_URL here.  Unless you're hosting Paperless off a
 | 
			
		||||
    subdomain like /paperless/, you probably don't need to change this.
 | 
			
		||||
    If you do change it, be sure to include the trailing slash.
 | 
			
		||||
 | 
			
		||||
    Defaults to "/static/".
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        When hosting paperless behind a reverse proxy like Traefik or Nginx at a subpath e.g.
 | 
			
		||||
        example.com/paperlessngx you will also need to set ``PAPERLESS_FORCE_SCRIPT_NAME``
 | 
			
		||||
        (see above).
 | 
			
		||||
 | 
			
		||||
PAPERLESS_AUTO_LOGIN_USERNAME=<username>
 | 
			
		||||
    Specify a username here so that paperless will automatically perform login
 | 
			
		||||
    with the selected user.
 | 
			
		||||
 | 
			
		||||
    .. danger::
 | 
			
		||||
 | 
			
		||||
        Do not use this when exposing paperless on the internet. There are no
 | 
			
		||||
        checks in place that would prevent you from doing this.
 | 
			
		||||
 | 
			
		||||
    Defaults to none, which disables this feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ADMIN_USER=<username>
 | 
			
		||||
    If this environment variable is specified, Paperless automatically creates
 | 
			
		||||
    a superuser with the provided username at start. This is useful in cases
 | 
			
		||||
    where you can not run the `createsuperuser` command separately, such as Kubernetes
 | 
			
		||||
    or AWS ECS.
 | 
			
		||||
 | 
			
		||||
    Requires `PAPERLESS_ADMIN_PASSWORD` to be set.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        This will not change an existing [super]user's password, nor will
 | 
			
		||||
        it recreate a user that already exists. You can leave this throughout
 | 
			
		||||
        the lifecycle of the containers.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ADMIN_MAIL=<email>
 | 
			
		||||
    (Optional) Specify superuser email address. Only used when
 | 
			
		||||
    `PAPERLESS_ADMIN_USER` is set.
 | 
			
		||||
 | 
			
		||||
    Defaults to ``root@localhost``.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ADMIN_PASSWORD=<password>
 | 
			
		||||
    Only used when `PAPERLESS_ADMIN_USER` is set.
 | 
			
		||||
    This will be the password of the automatically created superuser.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_COOKIE_PREFIX=<str>
 | 
			
		||||
    Specify a prefix that is added to the cookies used by paperless to identify
 | 
			
		||||
    the currently logged in user. This is useful for when you're running two
 | 
			
		||||
    instances of paperless on the same host.
 | 
			
		||||
 | 
			
		||||
    After changing this, you will have to login again.
 | 
			
		||||
 | 
			
		||||
    Defaults to ``""``, which does not alter the cookie names.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ENABLE_HTTP_REMOTE_USER=<bool>
 | 
			
		||||
    Allows authentication via HTTP_REMOTE_USER which is used by some SSO
 | 
			
		||||
    applications.
 | 
			
		||||
 | 
			
		||||
    .. warning::
 | 
			
		||||
 | 
			
		||||
        This will allow authentication by simply adding a ``Remote-User: <username>`` header
 | 
			
		||||
        to a request. Use with care! You especially *must* ensure that any such header is not
 | 
			
		||||
        passed from your proxy server to paperless.
 | 
			
		||||
 | 
			
		||||
        If you're exposing paperless to the internet directly, do not use this.
 | 
			
		||||
 | 
			
		||||
        Also see the warning `in the official documentation <https://docs.djangoproject.com/en/3.1/howto/auth-remote-user/#configuration>`.
 | 
			
		||||
 | 
			
		||||
    Defaults to `false` which disables this feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME=<str>
 | 
			
		||||
    If `PAPERLESS_ENABLE_HTTP_REMOTE_USER` is enabled, this property allows to
 | 
			
		||||
    customize the name of the HTTP header from which the authenticated username
 | 
			
		||||
    is extracted. Values are in terms of
 | 
			
		||||
    [HttpRequest.META](https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpRequest.META).
 | 
			
		||||
    Thus, the configured value must start with `HTTP_` followed by the
 | 
			
		||||
    normalized actual header name.
 | 
			
		||||
 | 
			
		||||
    Defaults to `HTTP_REMOTE_USER`.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_LOGOUT_REDIRECT_URL=<str>
 | 
			
		||||
    URL to redirect the user to after a logout. This can be used together with
 | 
			
		||||
    `PAPERLESS_ENABLE_HTTP_REMOTE_USER` to redirect the user back to the SSO
 | 
			
		||||
    application's logout page.
 | 
			
		||||
 | 
			
		||||
    Defaults to None, which disables this feature.
 | 
			
		||||
 | 
			
		||||
.. _configuration-ocr:
 | 
			
		||||
 | 
			
		||||
OCR settings
 | 
			
		||||
############
 | 
			
		||||
 | 
			
		||||
Paperless uses `OCRmyPDF <https://ocrmypdf.readthedocs.io/en/latest/>`_ for
 | 
			
		||||
performing OCR on documents and images. Paperless uses sensible defaults for
 | 
			
		||||
most settings, but all of them can be configured to your needs.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_LANGUAGE=<lang>
 | 
			
		||||
    Customize the language that paperless will attempt to use when
 | 
			
		||||
    parsing documents.
 | 
			
		||||
 | 
			
		||||
    It should be a 3-letter language code consistent with ISO
 | 
			
		||||
    639: https://www.loc.gov/standards/iso639-2/php/code_list.php
 | 
			
		||||
 | 
			
		||||
    Set this to the language most of your documents are written in.
 | 
			
		||||
 | 
			
		||||
    This can be a combination of multiple languages such as ``deu+eng``,
 | 
			
		||||
    in which case tesseract will use whatever language matches best.
 | 
			
		||||
    Keep in mind that tesseract uses much more cpu time with multiple
 | 
			
		||||
    languages enabled.
 | 
			
		||||
 | 
			
		||||
    Defaults to "eng".
 | 
			
		||||
 | 
			
		||||
		Note: If your language contains a '-' such as chi-sim, you must use chi_sim
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_MODE=<mode>
 | 
			
		||||
    Tell paperless when and how to perform ocr on your documents. Four modes
 | 
			
		||||
    are available:
 | 
			
		||||
 | 
			
		||||
    *   ``skip``: Paperless skips all pages and will perform ocr only on pages
 | 
			
		||||
        where no text is present. This is the safest option.
 | 
			
		||||
    *   ``skip_noarchive``: In addition to skip, paperless won't create an
 | 
			
		||||
        archived version of your documents when it finds any text in them.
 | 
			
		||||
        This is useful if you don't want to have two almost-identical versions
 | 
			
		||||
        of your digital documents in the media folder. This is the fastest option.
 | 
			
		||||
    *   ``redo``: Paperless will OCR all pages of your documents and attempt to
 | 
			
		||||
        replace any existing text layers with new text. This will be useful for
 | 
			
		||||
        documents from scanners that already performed OCR with insufficient
 | 
			
		||||
        results. It will also perform OCR on purely digital documents.
 | 
			
		||||
 | 
			
		||||
        This option may fail on some documents that have features that cannot
 | 
			
		||||
        be removed, such as forms. In this case, the text from the document is
 | 
			
		||||
        used instead.
 | 
			
		||||
    *   ``force``: Paperless rasterizes your documents, converting any text
 | 
			
		||||
        into images and puts the OCRed text on top. This works for all documents,
 | 
			
		||||
        however, the resulting document may be significantly larger and text
 | 
			
		||||
        won't appear as sharp when zoomed in.
 | 
			
		||||
 | 
			
		||||
    The default is ``skip``, which only performs OCR when necessary and always
 | 
			
		||||
    creates archived documents.
 | 
			
		||||
 | 
			
		||||
    Read more about this in the `OCRmyPDF documentation <https://ocrmypdf.readthedocs.io/en/latest/advanced.html#when-ocr-is-skipped>`_.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_CLEAN=<mode>
 | 
			
		||||
    Tells paperless to use ``unpaper`` to clean any input document before
 | 
			
		||||
    sending it to tesseract. This uses more resources, but generally results
 | 
			
		||||
    in better OCR results. The following modes are available:
 | 
			
		||||
 | 
			
		||||
    *   ``clean``: Apply unpaper.
 | 
			
		||||
    *   ``clean-final``: Apply unpaper, and use the cleaned images to build the
 | 
			
		||||
        output file instead of the original images.
 | 
			
		||||
    *   ``none``: Do not apply unpaper.
 | 
			
		||||
 | 
			
		||||
    Defaults to ``clean``.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        ``clean-final`` is incompatible with ocr mode ``redo``. When both
 | 
			
		||||
        ``clean-final`` and the ocr mode ``redo`` is configured, ``clean``
 | 
			
		||||
        is used instead.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_DESKEW=<bool>
 | 
			
		||||
    Tells paperless to correct skewing (slight rotation of input images mainly
 | 
			
		||||
    due to improper scanning)
 | 
			
		||||
 | 
			
		||||
    Defaults to ``true``, which enables this feature.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        Deskewing is incompatible with ocr mode ``redo``. Deskewing will get
 | 
			
		||||
        disabled automatically if ``redo`` is used as the ocr mode.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_ROTATE_PAGES=<bool>
 | 
			
		||||
    Tells paperless to correct page rotation (90°, 180° and 270° rotation).
 | 
			
		||||
 | 
			
		||||
    If you notice that paperless is not rotating incorrectly rotated
 | 
			
		||||
    pages (or vice versa), try adjusting the threshold up or down (see below).
 | 
			
		||||
 | 
			
		||||
    Defaults to ``true``, which enables this feature.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=<num>
 | 
			
		||||
    Adjust the threshold for automatic page rotation by ``PAPERLESS_OCR_ROTATE_PAGES``.
 | 
			
		||||
    This is an arbitrary value reported by tesseract. "15" is a very conservative value,
 | 
			
		||||
    whereas "2" is a very aggressive option and will often result in correctly rotated pages
 | 
			
		||||
    being rotated as well.
 | 
			
		||||
 | 
			
		||||
    Defaults to "12".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_OUTPUT_TYPE=<type>
 | 
			
		||||
    Specify the the type of PDF documents that paperless should produce.
 | 
			
		||||
 | 
			
		||||
    *   ``pdf``: Modify the PDF document as little as possible.
 | 
			
		||||
    *   ``pdfa``: Convert PDF documents into PDF/A-2b documents, which is a
 | 
			
		||||
        subset of the entire PDF specification and meant for storing
 | 
			
		||||
        documents long term.
 | 
			
		||||
    *   ``pdfa-1``, ``pdfa-2``, ``pdfa-3`` to specify the exact version of
 | 
			
		||||
        PDF/A you wish to use.
 | 
			
		||||
 | 
			
		||||
    If not specified, ``pdfa`` is used. Remember that paperless also keeps
 | 
			
		||||
    the original input file as well as the archived version.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_PAGES=<num>
 | 
			
		||||
    Tells paperless to use only the specified amount of pages for OCR. Documents
 | 
			
		||||
    with less than the specified amount of pages get OCR'ed completely.
 | 
			
		||||
 | 
			
		||||
    Specifying 1 here will only use the first page.
 | 
			
		||||
 | 
			
		||||
    When combined with ``PAPERLESS_OCR_MODE=redo`` or ``PAPERLESS_OCR_MODE=force``,
 | 
			
		||||
    paperless will not modify any text it finds on excluded pages and copy it
 | 
			
		||||
    verbatim.
 | 
			
		||||
 | 
			
		||||
    Defaults to 0, which disables this feature and always uses all pages.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_IMAGE_DPI=<num>
 | 
			
		||||
    Paperless will OCR any images you put into the system and convert them
 | 
			
		||||
    into PDF documents. This is useful if your scanner produces images.
 | 
			
		||||
    In order to do so, paperless needs to know the DPI of the image.
 | 
			
		||||
    Most images from scanners will have this information embedded and
 | 
			
		||||
    paperless will detect and use that information. In case this fails, it
 | 
			
		||||
    uses this value as a fallback.
 | 
			
		||||
 | 
			
		||||
    Set this to the DPI your scanner produces images at.
 | 
			
		||||
 | 
			
		||||
    Default is none, which will automatically calculate image DPI so that
 | 
			
		||||
    the produced PDF documents are A4 sized.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_MAX_IMAGE_PIXELS=<num>
 | 
			
		||||
    Paperless will raise a warning when OCRing images which are over this limit and
 | 
			
		||||
    will not OCR images which are more than twice this limit.  Note this does not
 | 
			
		||||
    prevent the document from being consumed, but could result in missing text content.
 | 
			
		||||
 | 
			
		||||
    If unset, will default to the value determined by
 | 
			
		||||
    `Pillow <https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.MAX_IMAGE_PIXELS>`_.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        Increasing this limit could cause Paperless to consume additional resources
 | 
			
		||||
        when consuming a file.  Be sure you have sufficient system resources.
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        The limit is intended to prevent malicious files from consuming system resources
 | 
			
		||||
        and causing crashes and other errors.  Only increase this value if you are certain
 | 
			
		||||
        your documents are not malicious and you need the text which was not OCRed
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_USER_ARGS=<json>
 | 
			
		||||
    OCRmyPDF offers many more options. Use this parameter to specify any
 | 
			
		||||
    additional arguments you wish to pass to OCRmyPDF. Since Paperless uses
 | 
			
		||||
    the API of OCRmyPDF, you have to specify these in a format that can be
 | 
			
		||||
    passed to the API. See `the API reference of OCRmyPDF <https://ocrmypdf.readthedocs.io/en/latest/api.html#reference>`_
 | 
			
		||||
    for valid parameters. All command line options are supported, but they
 | 
			
		||||
    use underscores instead of dashes.
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        Paperless has been tested to work with the OCR options provided
 | 
			
		||||
        above. There are many options that are incompatible with each other,
 | 
			
		||||
        so specifying invalid options may prevent paperless from consuming
 | 
			
		||||
        any documents.
 | 
			
		||||
 | 
			
		||||
    Specify arguments as a JSON dictionary. Keep note of lower case booleans
 | 
			
		||||
    and double quoted parameter names and strings. Examples:
 | 
			
		||||
 | 
			
		||||
    .. code:: json
 | 
			
		||||
 | 
			
		||||
        {"deskew": true, "optimize": 3, "unpaper_args": "--pre-rotate 90"}
 | 
			
		||||
 | 
			
		||||
.. _configuration-tika:
 | 
			
		||||
 | 
			
		||||
Tika settings
 | 
			
		||||
#############
 | 
			
		||||
 | 
			
		||||
Paperless can make use of `Tika <https://tika.apache.org/>`_ and
 | 
			
		||||
`Gotenberg <https://gotenberg.dev/>`_ for parsing and
 | 
			
		||||
converting "Office" documents (such as ".doc", ".xlsx" and ".odt"). If you
 | 
			
		||||
wish to use this, you must provide a Tika server and a Gotenberg server,
 | 
			
		||||
configure their endpoints, and enable the feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_TIKA_ENABLED=<bool>
 | 
			
		||||
    Enable (or disable) the Tika parser.
 | 
			
		||||
 | 
			
		||||
    Defaults to false.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_TIKA_ENDPOINT=<url>
 | 
			
		||||
    Set the endpoint URL were Paperless can reach your Tika server.
 | 
			
		||||
 | 
			
		||||
    Defaults to "http://localhost:9998".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT=<url>
 | 
			
		||||
    Set the endpoint URL were Paperless can reach your Gotenberg server.
 | 
			
		||||
 | 
			
		||||
    Defaults to "http://localhost:3000".
 | 
			
		||||
 | 
			
		||||
If you run paperless on docker, you can add those services to the docker-compose
 | 
			
		||||
file (see the provided ``docker-compose.sqlite-tika.yml`` file for reference). The changes
 | 
			
		||||
requires are as follows:
 | 
			
		||||
 | 
			
		||||
.. code:: yaml
 | 
			
		||||
 | 
			
		||||
    services:
 | 
			
		||||
        # ...
 | 
			
		||||
 | 
			
		||||
        webserver:
 | 
			
		||||
            # ...
 | 
			
		||||
 | 
			
		||||
            environment:
 | 
			
		||||
                # ...
 | 
			
		||||
 | 
			
		||||
                PAPERLESS_TIKA_ENABLED: 1
 | 
			
		||||
                PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
 | 
			
		||||
                PAPERLESS_TIKA_ENDPOINT: http://tika:9998
 | 
			
		||||
 | 
			
		||||
        # ...
 | 
			
		||||
 | 
			
		||||
        gotenberg:
 | 
			
		||||
            image: gotenberg/gotenberg:7.6
 | 
			
		||||
            restart: unless-stopped
 | 
			
		||||
            command:
 | 
			
		||||
                - "gotenberg"
 | 
			
		||||
                - "--chromium-disable-routes=true"
 | 
			
		||||
 | 
			
		||||
        tika:
 | 
			
		||||
            image: ghcr.io/paperless-ngx/tika:latest
 | 
			
		||||
            restart: unless-stopped
 | 
			
		||||
 | 
			
		||||
Add the configuration variables to the environment of the webserver (alternatively
 | 
			
		||||
put the configuration in the ``docker-compose.env`` file) and add the additional
 | 
			
		||||
services below the webserver service. Watch out for indentation.
 | 
			
		||||
 | 
			
		||||
Make sure to use the correct format `PAPERLESS_TIKA_ENABLED = 1` so python_dotenv can parse the statement correctly.
 | 
			
		||||
 | 
			
		||||
Software tweaks
 | 
			
		||||
###############
 | 
			
		||||
 | 
			
		||||
PAPERLESS_TASK_WORKERS=<num>
 | 
			
		||||
    Paperless does multiple things in the background: Maintain the search index,
 | 
			
		||||
    maintain the automatic matching algorithm, check emails, consume documents,
 | 
			
		||||
    etc. This variable specifies how many things it will do in parallel.
 | 
			
		||||
 | 
			
		||||
    Defaults to 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_THREADS_PER_WORKER=<num>
 | 
			
		||||
    Furthermore, paperless uses multiple threads when consuming documents to
 | 
			
		||||
    speed up OCR. This variable specifies how many pages paperless will process
 | 
			
		||||
    in parallel on a single document.
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        Ensure that the product
 | 
			
		||||
 | 
			
		||||
            PAPERLESS_TASK_WORKERS * PAPERLESS_THREADS_PER_WORKER
 | 
			
		||||
 | 
			
		||||
        does not exceed your CPU core count or else paperless will be extremely slow.
 | 
			
		||||
        If you want paperless to process many documents in parallel, choose a high
 | 
			
		||||
        worker count. If you want paperless to process very large documents faster,
 | 
			
		||||
        use a higher thread per worker count.
 | 
			
		||||
 | 
			
		||||
    The default is a balance between the two, according to your CPU core count,
 | 
			
		||||
    with a slight favor towards threads per worker:
 | 
			
		||||
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    | CPU core count | Workers | Threads |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    |              1 |       1 |       1 |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    |              2 |       2 |       1 |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    |              4 |       2 |       2 |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    |              6 |       2 |       3 |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    |              8 |       2 |       4 |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    |             12 |       3 |       4 |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
    |             16 |       4 |       4 |
 | 
			
		||||
    +----------------+---------+---------+
 | 
			
		||||
 | 
			
		||||
    If you only specify PAPERLESS_TASK_WORKERS, paperless will adjust
 | 
			
		||||
    PAPERLESS_THREADS_PER_WORKER automatically.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_WORKER_TIMEOUT=<num>
 | 
			
		||||
    Machines with few cores or weak ones might not be able to finish OCR on
 | 
			
		||||
    large documents within the default 1800 seconds. So extending this timeout
 | 
			
		||||
    may prove to be useful on weak hardware setups.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_WORKER_RETRY=<num>
 | 
			
		||||
    If PAPERLESS_WORKER_TIMEOUT has been configured, the retry time for a task can
 | 
			
		||||
    also be configured.  By default, this value will be set to 10s more than the
 | 
			
		||||
    worker timeout.  This value should never be set less than the worker timeout.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_TIME_ZONE=<timezone>
 | 
			
		||||
    Set the time zone here.
 | 
			
		||||
    See https://docs.djangoproject.com/en/3.1/ref/settings/#std:setting-TIME_ZONE
 | 
			
		||||
    for details on how to set it.
 | 
			
		||||
 | 
			
		||||
    Defaults to UTC.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _configuration-polling:
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_POLLING=<num>
 | 
			
		||||
    If paperless won't find documents added to your consume folder, it might
 | 
			
		||||
    not be able to automatically detect filesystem changes. In that case,
 | 
			
		||||
    specify a polling interval in seconds here, which will then cause paperless
 | 
			
		||||
    to periodically check your consumption directory for changes. This will also
 | 
			
		||||
    disable listening for file system changes with ``inotify``.
 | 
			
		||||
 | 
			
		||||
    Defaults to 0, which disables polling and uses filesystem notifications.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_POLLING_RETRY_COUNT=<num>
 | 
			
		||||
    If consumer polling is enabled, sets the number of times paperless will check for a
 | 
			
		||||
    file to remain unmodified.
 | 
			
		||||
 | 
			
		||||
    Defaults to 5.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_POLLING_DELAY=<num>
 | 
			
		||||
    If consumer polling is enabled, sets the delay in seconds between each check (above) paperless
 | 
			
		||||
    will do while waiting for a file to remain unmodified.
 | 
			
		||||
 | 
			
		||||
    Defaults to 5.
 | 
			
		||||
 | 
			
		||||
.. _configuration-inotify:
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_INOTIFY_DELAY=<num>
 | 
			
		||||
    Sets the time in seconds the consumer will wait for additional events
 | 
			
		||||
    from inotify before the consumer will consider a file ready and begin consumption.
 | 
			
		||||
    Certain scanners or network setups may generate multiple events for a single file,
 | 
			
		||||
    leading to multiple consumers working on the same file.  Configure this to
 | 
			
		||||
    prevent that.
 | 
			
		||||
 | 
			
		||||
    Defaults to 0.5 seconds.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>
 | 
			
		||||
    When the consumer detects a duplicate document, it will not touch the
 | 
			
		||||
    original document. This default behavior can be changed here.
 | 
			
		||||
 | 
			
		||||
    Defaults to false.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_RECURSIVE=<bool>
 | 
			
		||||
    Enable recursive watching of the consumption directory. Paperless will
 | 
			
		||||
    then pickup files from files in subdirectories within your consumption
 | 
			
		||||
    directory as well.
 | 
			
		||||
 | 
			
		||||
    Defaults to false.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=<bool>
 | 
			
		||||
    Set the names of subdirectories as tags for consumed files.
 | 
			
		||||
    E.g. <CONSUMPTION_DIR>/foo/bar/file.pdf will add the tags "foo" and "bar" to
 | 
			
		||||
    the consumed file. Paperless will create any tags that don't exist yet.
 | 
			
		||||
 | 
			
		||||
    This is useful for sorting documents with certain tags such as ``car`` or
 | 
			
		||||
    ``todo`` prior to consumption. These folders won't be deleted.
 | 
			
		||||
 | 
			
		||||
    PAPERLESS_CONSUMER_RECURSIVE must be enabled for this to work.
 | 
			
		||||
 | 
			
		||||
    Defaults to false.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_ENABLE_BARCODES=<bool>
 | 
			
		||||
    Enables the scanning and page separation based on detected barcodes.
 | 
			
		||||
    This allows for scanning and adding multiple documents per uploaded
 | 
			
		||||
    file, which are separated by one or multiple barcode pages.
 | 
			
		||||
 | 
			
		||||
    For ease of use, it is suggested to use a standardized separation page,
 | 
			
		||||
    e.g. `here <https://www.alliancegroup.co.uk/patch-codes.htm>`_.
 | 
			
		||||
 | 
			
		||||
    If no barcodes are detected in the uploaded file, no page separation
 | 
			
		||||
    will happen.
 | 
			
		||||
 | 
			
		||||
    The original document will be removed and the separated pages will be
 | 
			
		||||
    saved as pdf.
 | 
			
		||||
 | 
			
		||||
    Defaults to false.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_BARCODE_TIFF_SUPPORT=<bool>
 | 
			
		||||
    Whether TIFF image files should be scanned for barcodes.
 | 
			
		||||
    This will automatically convert any TIFF image(s) to pdfs for later
 | 
			
		||||
    processing.
 | 
			
		||||
    This only has an effect, if PAPERLESS_CONSUMER_ENABLE_BARCODES has been
 | 
			
		||||
    enabled.
 | 
			
		||||
 | 
			
		||||
    Defaults to false.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT
 | 
			
		||||
  Defines the string to be detected as a separator barcode.
 | 
			
		||||
  If paperless is used with the PATCH-T separator pages, users
 | 
			
		||||
  shouldn't change this.
 | 
			
		||||
 | 
			
		||||
  Defaults to "PATCHT"
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONVERT_MEMORY_LIMIT=<num>
 | 
			
		||||
    On smaller systems, or even in the case of Very Large Documents, the consumer
 | 
			
		||||
    may explode, complaining about how it's "unable to extend pixel cache".  In
 | 
			
		||||
    such cases, try setting this to a reasonably low value, like 32.  The
 | 
			
		||||
    default is to use whatever is necessary to do everything without writing to
 | 
			
		||||
    disk, and units are in megabytes.
 | 
			
		||||
 | 
			
		||||
    For more information on how to use this value, you should search
 | 
			
		||||
    the web for "MAGICK_MEMORY_LIMIT".
 | 
			
		||||
 | 
			
		||||
    Defaults to 0, which disables the limit.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONVERT_TMPDIR=<path>
 | 
			
		||||
    Similar to the memory limit, if you've got a small system and your OS mounts
 | 
			
		||||
    /tmp as tmpfs, you should set this to a path that's on a physical disk, like
 | 
			
		||||
    /home/your_user/tmp or something.  ImageMagick will use this as scratch space
 | 
			
		||||
    when crunching through very large documents.
 | 
			
		||||
 | 
			
		||||
    For more information on how to use this value, you should search
 | 
			
		||||
    the web for "MAGICK_TMPDIR".
 | 
			
		||||
 | 
			
		||||
    Default is none, which disables the temporary directory.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_POST_CONSUME_SCRIPT=<filename>
 | 
			
		||||
    After a document is consumed, Paperless can trigger an arbitrary script if
 | 
			
		||||
    you like.  This script will be passed a number of arguments for you to work
 | 
			
		||||
    with. For more information, take a look at :ref:`advanced-post_consume_script`.
 | 
			
		||||
 | 
			
		||||
    The default is blank, which means nothing will be executed.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_FILENAME_DATE_ORDER=<format>
 | 
			
		||||
    Paperless will check the document text for document date information.
 | 
			
		||||
    Use this setting to enable checking the document filename for date
 | 
			
		||||
    information. The date order can be set to any option as specified in
 | 
			
		||||
    https://dateparser.readthedocs.io/en/latest/settings.html#date-order.
 | 
			
		||||
    The filename will be checked first, and if nothing is found, the document
 | 
			
		||||
    text will be checked as normal.
 | 
			
		||||
 | 
			
		||||
    A date in a filename must have some separators (`.`, `-`, `/`, etc)
 | 
			
		||||
    for it to be parsed.
 | 
			
		||||
 | 
			
		||||
    Defaults to none, which disables this feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_NUMBER_OF_SUGGESTED_DATES=<num>
 | 
			
		||||
    Paperless searches an entire document for dates. The first date found will
 | 
			
		||||
    be used as the initial value for the created date. When this variable is
 | 
			
		||||
    greater than 0 (or left to it's default value), paperless will also suggest
 | 
			
		||||
    other dates found in the document, up to a maximum of this setting. Note that
 | 
			
		||||
    duplicates will be removed, which can result in fewer dates displayed in the
 | 
			
		||||
    frontend than this setting value.
 | 
			
		||||
 | 
			
		||||
    The task to find all dates can be time-consuming and increases with a higher
 | 
			
		||||
    (maximum) number of suggested dates and slower hardware.
 | 
			
		||||
 | 
			
		||||
    Defaults to 3. Set to 0 to disable this feature.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_THUMBNAIL_FONT_NAME=<filename>
 | 
			
		||||
    Paperless creates thumbnails for plain text files by rendering the content
 | 
			
		||||
    of the file on an image and uses a predefined font for that. This
 | 
			
		||||
    font can be changed here.
 | 
			
		||||
 | 
			
		||||
    Note that this won't have any effect on already generated thumbnails.
 | 
			
		||||
 | 
			
		||||
    Defaults to ``/usr/share/fonts/liberation/LiberationSerif-Regular.ttf``.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_IGNORE_DATES=<string>
 | 
			
		||||
    Paperless parses a documents creation date from filename and file content.
 | 
			
		||||
    You may specify a comma separated list of dates that should be ignored during
 | 
			
		||||
    this process. This is useful for special dates (like date of birth) that appear
 | 
			
		||||
    in documents regularly but are very unlikely to be the documents creation date.
 | 
			
		||||
 | 
			
		||||
    The date is parsed using the order specified in PAPERLESS_DATE_ORDER
 | 
			
		||||
 | 
			
		||||
    Defaults to an empty string to not ignore any dates.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_DATE_ORDER=<format>
 | 
			
		||||
    Paperless will try to determine the document creation date from its contents.
 | 
			
		||||
    Specify the date format Paperless should expect to see within your documents.
 | 
			
		||||
 | 
			
		||||
    This option defaults to DMY which translates to day first, month second, and year
 | 
			
		||||
    last order. Characters D, M, or Y can be shuffled to meet the required order.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONSUMER_IGNORE_PATTERNS=<json>
 | 
			
		||||
    By default, paperless ignores certain files and folders in the consumption
 | 
			
		||||
    directory, such as system files created by the Mac OS.
 | 
			
		||||
 | 
			
		||||
    This can be adjusted by configuring a custom json array with patterns to exclude.
 | 
			
		||||
 | 
			
		||||
    Defaults to ``[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini"]``.
 | 
			
		||||
 | 
			
		||||
Binaries
 | 
			
		||||
########
 | 
			
		||||
 | 
			
		||||
There are a few external software packages that Paperless expects to find on
 | 
			
		||||
your system when it starts up.  Unless you've done something creative with
 | 
			
		||||
their installation, you probably won't need to edit any of these.  However,
 | 
			
		||||
if you've installed these programs somewhere where simply typing the name of
 | 
			
		||||
the program doesn't automatically execute it (ie. the program isn't in your
 | 
			
		||||
$PATH), then you'll need to specify the literal path for that program.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_CONVERT_BINARY=<path>
 | 
			
		||||
    Defaults to "convert".
 | 
			
		||||
 | 
			
		||||
PAPERLESS_GS_BINARY=<path>
 | 
			
		||||
    Defaults to "gs".
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _configuration-docker:
 | 
			
		||||
 | 
			
		||||
Docker-specific options
 | 
			
		||||
#######################
 | 
			
		||||
 | 
			
		||||
These options don't have any effect in ``paperless.conf``. These options adjust
 | 
			
		||||
the behavior of the docker container. Configure these in `docker-compose.env`.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_WEBSERVER_WORKERS=<num>
 | 
			
		||||
    The number of worker processes the webserver should spawn. More worker processes
 | 
			
		||||
    usually result in the front end to load data much quicker. However, each worker process
 | 
			
		||||
    also loads the entire application into memory separately, so increasing this value
 | 
			
		||||
    will increase RAM usage.
 | 
			
		||||
 | 
			
		||||
    Defaults to 1.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_BIND_ADDR=<ip address>
 | 
			
		||||
    The IP address the webserver will listen on inside the container. There are
 | 
			
		||||
    special setups where you may need to configure this value to restrict the
 | 
			
		||||
    Ip address or interface the webserver listens on.
 | 
			
		||||
 | 
			
		||||
    Defaults to [::], meaning all interfaces, including IPv6.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_PORT=<port>
 | 
			
		||||
    The port number the webserver will listen on inside the container. There are
 | 
			
		||||
    special setups where you may need this to avoid collisions with other
 | 
			
		||||
    services (like using podman with multiple containers in one pod).
 | 
			
		||||
 | 
			
		||||
    Don't change this when using Docker. To change the port the webserver is
 | 
			
		||||
    reachable outside of the container, instead refer to the "ports" key in
 | 
			
		||||
    ``docker-compose.yml``.
 | 
			
		||||
 | 
			
		||||
    Defaults to 8000.
 | 
			
		||||
 | 
			
		||||
USERMAP_UID=<uid>
 | 
			
		||||
    The ID of the paperless user in the container. Set this to your actual user ID on the
 | 
			
		||||
    host system, which you can get by executing
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ id -u
 | 
			
		||||
 | 
			
		||||
    Paperless will change ownership on its folders to this user, so you need to get this right
 | 
			
		||||
    in order to be able to write to the consumption directory.
 | 
			
		||||
 | 
			
		||||
    Defaults to 1000.
 | 
			
		||||
 | 
			
		||||
USERMAP_GID=<gid>
 | 
			
		||||
    The ID of the paperless Group in the container. Set this to your actual group ID on the
 | 
			
		||||
    host system, which you can get by executing
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ id -g
 | 
			
		||||
 | 
			
		||||
    Paperless will change ownership on its folders to this group, so you need to get this right
 | 
			
		||||
    in order to be able to write to the consumption directory.
 | 
			
		||||
 | 
			
		||||
    Defaults to 1000.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_OCR_LANGUAGES=<list>
 | 
			
		||||
    Additional OCR languages to install. By default, paperless comes with
 | 
			
		||||
    English, German, Italian, Spanish and French. If your language is not in this list, install
 | 
			
		||||
    additional languages with this configuration option:
 | 
			
		||||
 | 
			
		||||
    .. code:: bash
 | 
			
		||||
 | 
			
		||||
        PAPERLESS_OCR_LANGUAGES=tur ces
 | 
			
		||||
 | 
			
		||||
    To actually use these languages, also set the default OCR language of paperless:
 | 
			
		||||
 | 
			
		||||
    .. code:: bash
 | 
			
		||||
 | 
			
		||||
        PAPERLESS_OCR_LANGUAGE=tur
 | 
			
		||||
 | 
			
		||||
    Defaults to none, which does not install any additional languages.
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ENABLE_FLOWER=<defined>
 | 
			
		||||
    If this environment variable is defined, the Celery monitoring tool
 | 
			
		||||
    `Flower <https://flower.readthedocs.io/en/latest/index.html>`_ will
 | 
			
		||||
    be started by the container.
 | 
			
		||||
 | 
			
		||||
    You can read more about this in the :ref:`advanced setup <advanced-celery-monitoring>`
 | 
			
		||||
    documentation.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _configuration-update-checking:
 | 
			
		||||
 | 
			
		||||
Update Checking
 | 
			
		||||
###############
 | 
			
		||||
 | 
			
		||||
PAPERLESS_ENABLE_UPDATE_CHECK=<bool>
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
            This setting was deprecated in favor of a frontend setting after v1.9.2. A one-time
 | 
			
		||||
            migration is performed for users who have this setting set. This setting is always
 | 
			
		||||
            ignored if the corresponding frontend setting has been set.
 | 
			
		||||
							
								
								
									
										469
									
								
								docs/development.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,469 @@
 | 
			
		||||
# Development
 | 
			
		||||
 | 
			
		||||
This section describes the steps you need to take to start development
 | 
			
		||||
on paperless-ngx.
 | 
			
		||||
 | 
			
		||||
Check out the source from github. The repository is organized in the
 | 
			
		||||
following way:
 | 
			
		||||
 | 
			
		||||
- `main` always represents the latest release and will only see
 | 
			
		||||
  changes when a new release is made.
 | 
			
		||||
- `dev` contains the code that will be in the next release.
 | 
			
		||||
- `feature-X` contain bigger changes that will be in some release, but
 | 
			
		||||
  not necessarily the next one.
 | 
			
		||||
 | 
			
		||||
When making functional changes to paperless, _always_ make your changes
 | 
			
		||||
on the `dev` branch.
 | 
			
		||||
 | 
			
		||||
Apart from that, the folder structure is as follows:
 | 
			
		||||
 | 
			
		||||
- `docs/` - Documentation.
 | 
			
		||||
- `src-ui/` - Code of the front end.
 | 
			
		||||
- `src/` - Code of the back end.
 | 
			
		||||
- `scripts/` - Various scripts that help with different parts of
 | 
			
		||||
  development.
 | 
			
		||||
- `docker/` - Files required to build the docker image.
 | 
			
		||||
 | 
			
		||||
## Contributing to Paperless
 | 
			
		||||
 | 
			
		||||
Maybe you've been using Paperless for a while and want to add a feature
 | 
			
		||||
or two, or maybe you've come across a bug that you have some ideas how
 | 
			
		||||
to solve. The beauty of open source software is that you can see what's
 | 
			
		||||
wrong and help to get it fixed for everyone!
 | 
			
		||||
 | 
			
		||||
Before contributing please review our [code of
 | 
			
		||||
conduct](https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md)
 | 
			
		||||
and other important information in the [contributing
 | 
			
		||||
guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md).
 | 
			
		||||
 | 
			
		||||
## Code formatting with pre-commit Hooks
 | 
			
		||||
 | 
			
		||||
To ensure a consistent style and formatting across the project source,
 | 
			
		||||
the project utilizes a Git [pre-commit]{.title-ref} hook to perform some
 | 
			
		||||
formatting and linting before a commit is allowed. That way, everyone
 | 
			
		||||
uses the same style and some common issues can be caught early on. See
 | 
			
		||||
below for installation instructions.
 | 
			
		||||
 | 
			
		||||
Once installed, hooks will run when you commit. If the formatting isn't
 | 
			
		||||
quite right or a linter catches something, the commit will be rejected.
 | 
			
		||||
You'll need to look at the output and fix the issue. Some hooks, such
 | 
			
		||||
as the Python formatting tool [black]{.title-ref}, will format failing
 | 
			
		||||
files, so all you need to do is [git add]{.title-ref} those files again
 | 
			
		||||
and retry your commit.
 | 
			
		||||
 | 
			
		||||
## Initial setup and first start
 | 
			
		||||
 | 
			
		||||
After you forked and cloned the code from github you need to perform a
 | 
			
		||||
first-time setup. To do the setup you need to perform the steps from the
 | 
			
		||||
following chapters in a certain order:
 | 
			
		||||
 | 
			
		||||
1.  Install prerequisites + pipenv as mentioned in
 | 
			
		||||
    `[Bare metal route](/setup#bare_metal)
 | 
			
		||||
 | 
			
		||||
2.  Copy `paperless.conf.example` to `paperless.conf` and enable debug
 | 
			
		||||
    mode.
 | 
			
		||||
 | 
			
		||||
3.  Install the Angular CLI interface:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ npm install -g @angular/cli
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
4.  Install pre-commit
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    pre-commit install
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
5.  Create `consume` and `media` folders in the cloned root folder.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    mkdir -p consume media
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
6.  You can now either \...
 | 
			
		||||
 | 
			
		||||
    - install redis or
 | 
			
		||||
 | 
			
		||||
    - use the included scripts/start-services.sh to use docker to fire
 | 
			
		||||
      up a redis instance (and some other services such as tika,
 | 
			
		||||
      gotenberg and a database server) or
 | 
			
		||||
 | 
			
		||||
    - spin up a bare redis container
 | 
			
		||||
 | 
			
		||||
      > ```shell-session
 | 
			
		||||
      > docker run -d -p 6379:6379 --restart unless-stopped redis:latest
 | 
			
		||||
      > ```
 | 
			
		||||
 | 
			
		||||
7.  Install the python dependencies by performing in the src/ directory.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    pipenv install --dev
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
> - Make sure you're using python 3.9.x or lower. Otherwise you might
 | 
			
		||||
>   get issues with building dependencies. You can use
 | 
			
		||||
>   [pyenv](https://github.com/pyenv/pyenv) to install a specific
 | 
			
		||||
>   python version.
 | 
			
		||||
 | 
			
		||||
8.  Generate the static UI so you can perform a login to get session
 | 
			
		||||
    that is required for frontend development (this needs to be done one
 | 
			
		||||
    time only). From src-ui directory:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    npm install .
 | 
			
		||||
    ./node_modules/.bin/ng build --configuration production
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
9.  Apply migrations and create a superuser for your dev instance:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    python3 manage.py migrate
 | 
			
		||||
    python3 manage.py createsuperuser
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
10. Now spin up the dev backend. Depending on which part of paperless
 | 
			
		||||
    you're developing for, you need to have some or all of them
 | 
			
		||||
    running.
 | 
			
		||||
 | 
			
		||||
> ```shell-session
 | 
			
		||||
> python3 manage.py runserver & python3 manage.py document_consumer & celery --app paperless worker
 | 
			
		||||
> ```
 | 
			
		||||
 | 
			
		||||
11. Login with the superuser credentials provided in step 8 at
 | 
			
		||||
    `http://localhost:8000` to create a session that enables you to use
 | 
			
		||||
    the backend.
 | 
			
		||||
 | 
			
		||||
Backend development environment is now ready, to start Frontend
 | 
			
		||||
development go to `/src-ui` and run `ng serve`. From there you can use
 | 
			
		||||
`http://localhost:4200` for a preview.
 | 
			
		||||
 | 
			
		||||
## Back end development
 | 
			
		||||
 | 
			
		||||
The backend is a django application. PyCharm works well for development,
 | 
			
		||||
but you can use whatever you want.
 | 
			
		||||
 | 
			
		||||
Configure the IDE to use the src/ folder as the base source folder.
 | 
			
		||||
Configure the following launch configurations in your IDE:
 | 
			
		||||
 | 
			
		||||
- python3 manage.py runserver
 | 
			
		||||
- celery \--app paperless worker
 | 
			
		||||
- python3 manage.py document_consumer
 | 
			
		||||
 | 
			
		||||
To start them all:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
python3 manage.py runserver & python3 manage.py document_consumer & celery --app paperless worker
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Testing and code style:
 | 
			
		||||
 | 
			
		||||
- Run `pytest` in the src/ directory to execute all tests. This also
 | 
			
		||||
  generates a HTML coverage report. When runnings test, paperless.conf
 | 
			
		||||
  is loaded as well. However: the tests rely on the default
 | 
			
		||||
  configuration. This is not ideal. But for now, make sure no settings
 | 
			
		||||
  except for DEBUG are overridden when testing.
 | 
			
		||||
 | 
			
		||||
- Coding style is enforced by the Git pre-commit hooks. These will
 | 
			
		||||
  ensure your code is formatted and do some linting when you do a [git
 | 
			
		||||
  commit]{.title-ref}.
 | 
			
		||||
 | 
			
		||||
- You can also run `black` manually to format your code
 | 
			
		||||
 | 
			
		||||
  !!! note
 | 
			
		||||
 | 
			
		||||
      The line length rule E501 is generally useful for getting multiple
 | 
			
		||||
      source files next to each other on the screen. However, in some
 | 
			
		||||
      cases, its just not possible to make some lines fit, especially
 | 
			
		||||
      complicated IF cases. Append `# NOQA: E501` to disable this check
 | 
			
		||||
      for certain lines.
 | 
			
		||||
 | 
			
		||||
## Front end development
 | 
			
		||||
 | 
			
		||||
The front end is built using Angular. In order to get started, you need
 | 
			
		||||
`npm`. Install the Angular CLI interface with
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ npm install -g @angular/cli
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and make sure that it's on your path. Next, in the src-ui/ directory,
 | 
			
		||||
install the required dependencies of the project.
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ npm install
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You can launch a development server by running
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ ng serve
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This will automatically update whenever you save. However, in-place
 | 
			
		||||
compilation might fail on syntax errors, in which case you need to
 | 
			
		||||
restart it.
 | 
			
		||||
 | 
			
		||||
By default, the development server is available on
 | 
			
		||||
`http://localhost:4200/` and is configured to access the API at
 | 
			
		||||
`http://localhost:8000/api/`, which is the default of the backend. If
 | 
			
		||||
you enabled DEBUG on the back end, several security overrides for
 | 
			
		||||
allowed hosts, CORS and X-Frame-Options are in place so that the front
 | 
			
		||||
end behaves exactly as in production. This also relies on you being
 | 
			
		||||
logged into the back end. Without a valid session, The front end will
 | 
			
		||||
simply not work.
 | 
			
		||||
 | 
			
		||||
Testing and code style:
 | 
			
		||||
 | 
			
		||||
- The frontend code (.ts, .html, .scss) use `prettier` for code
 | 
			
		||||
  formatting via the Git `pre-commit` hooks which run automatically on
 | 
			
		||||
  commit. See
 | 
			
		||||
  [above](#code-formatting-with-pre-commit-hooks) for installation. You can also run this via cli with a
 | 
			
		||||
  command such as
 | 
			
		||||
 | 
			
		||||
  ```shell-session
 | 
			
		||||
  $ git ls-files -- '*.ts' | xargs pre-commit run prettier --files
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
- Frontend testing uses jest and cypress. There is currently a need
 | 
			
		||||
  for significantly more frontend tests. Unit tests and e2e tests,
 | 
			
		||||
  respectively, can be run non-interactively with:
 | 
			
		||||
 | 
			
		||||
  ```shell-session
 | 
			
		||||
  $ ng test
 | 
			
		||||
  $ npm run e2e:ci
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
  Cypress also includes a UI which can be run from within the `src-ui`
 | 
			
		||||
  directory with
 | 
			
		||||
 | 
			
		||||
  ```shell-session
 | 
			
		||||
  $ ./node_modules/.bin/cypress open
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
In order to build the front end and serve it as part of django, execute
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ ng build --prod
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This will build the front end and put it in a location from which the
 | 
			
		||||
Django server will serve it as static content. This way, you can verify
 | 
			
		||||
that authentication is working.
 | 
			
		||||
 | 
			
		||||
## Localization
 | 
			
		||||
 | 
			
		||||
Paperless is available in many different languages. Since paperless
 | 
			
		||||
consists both of a django application and an Angular front end, both
 | 
			
		||||
these parts have to be translated separately.
 | 
			
		||||
 | 
			
		||||
### Front end localization
 | 
			
		||||
 | 
			
		||||
- The Angular front end does localization according to the [Angular
 | 
			
		||||
  documentation](https://angular.io/guide/i18n).
 | 
			
		||||
- The source language of the project is "en_US".
 | 
			
		||||
- The source strings end up in the file "src-ui/messages.xlf".
 | 
			
		||||
- The translated strings need to be placed in the
 | 
			
		||||
  "src-ui/src/locale/" folder.
 | 
			
		||||
- In order to extract added or changed strings from the source files,
 | 
			
		||||
  call `ng xi18n --ivy`.
 | 
			
		||||
 | 
			
		||||
Adding new languages requires adding the translated files in the
 | 
			
		||||
"src-ui/src/locale/" folder and adjusting a couple files.
 | 
			
		||||
 | 
			
		||||
1.  Adjust "src-ui/angular.json":
 | 
			
		||||
 | 
			
		||||
    ```json
 | 
			
		||||
    "i18n": {
 | 
			
		||||
        "sourceLocale": "en-US",
 | 
			
		||||
        "locales": {
 | 
			
		||||
            "de": "src/locale/messages.de.xlf",
 | 
			
		||||
            "nl-NL": "src/locale/messages.nl_NL.xlf",
 | 
			
		||||
            "fr": "src/locale/messages.fr.xlf",
 | 
			
		||||
            "en-GB": "src/locale/messages.en_GB.xlf",
 | 
			
		||||
            "pt-BR": "src/locale/messages.pt_BR.xlf",
 | 
			
		||||
            "language-code": "language-file"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
2.  Add the language to the available options in
 | 
			
		||||
    "src-ui/src/app/services/settings.service.ts":
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    getLanguageOptions(): LanguageOption[] {
 | 
			
		||||
        return [
 | 
			
		||||
            {code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
 | 
			
		||||
            {code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
 | 
			
		||||
            {code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
 | 
			
		||||
            {code: "nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
 | 
			
		||||
            {code: "fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
 | 
			
		||||
            {code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"}
 | 
			
		||||
            // Add your new language here
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    `dateInputFormat` is a special string that defines the behavior of
 | 
			
		||||
    the date input fields and absolutely needs to contain "dd", "mm"
 | 
			
		||||
    and "yyyy".
 | 
			
		||||
 | 
			
		||||
3.  Import and register the Angular data for this locale in
 | 
			
		||||
    "src-ui/src/app/app.module.ts":
 | 
			
		||||
 | 
			
		||||
    ```typescript
 | 
			
		||||
    import localeDe from '@angular/common/locales/de'
 | 
			
		||||
    registerLocaleData(localeDe)
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
### Back end localization
 | 
			
		||||
 | 
			
		||||
A majority of the strings that appear in the back end appear only when
 | 
			
		||||
the admin is used. However, some of these are still shown on the front
 | 
			
		||||
end (such as error messages).
 | 
			
		||||
 | 
			
		||||
- The django application does localization according to the [django
 | 
			
		||||
  documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/).
 | 
			
		||||
- The source language of the project is "en_US".
 | 
			
		||||
- Localization files end up in the folder "src/locale/".
 | 
			
		||||
- In order to extract strings from the application, call
 | 
			
		||||
  `python3 manage.py makemessages -l en_US`. This is important after
 | 
			
		||||
  making changes to translatable strings.
 | 
			
		||||
- The message files need to be compiled for them to show up in the
 | 
			
		||||
  application. Call `python3 manage.py compilemessages` to do this.
 | 
			
		||||
  The generated files don't get committed into git, since these are
 | 
			
		||||
  derived artifacts. The build pipeline takes care of executing this
 | 
			
		||||
  command.
 | 
			
		||||
 | 
			
		||||
Adding new languages requires adding the translated files in the
 | 
			
		||||
"src/locale/" folder and adjusting the file
 | 
			
		||||
"src/paperless/settings.py" to include the new language:
 | 
			
		||||
 | 
			
		||||
```python
 | 
			
		||||
LANGUAGES = [
 | 
			
		||||
    ("en-us", _("English (US)")),
 | 
			
		||||
    ("en-gb", _("English (GB)")),
 | 
			
		||||
    ("de", _("German")),
 | 
			
		||||
    ("nl-nl", _("Dutch")),
 | 
			
		||||
    ("fr", _("French")),
 | 
			
		||||
    ("pt-br", _("Portuguese (Brazil)")),
 | 
			
		||||
    # Add language here.
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Building the documentation
 | 
			
		||||
 | 
			
		||||
The documentation is built using material-mkdocs, see their [documentation](https://squidfunk.github.io/mkdocs-material/reference/). If you want to build the documentation locally, this is how you do it:
 | 
			
		||||
 | 
			
		||||
1.  Install python dependencies.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ cd /path/to/paperless
 | 
			
		||||
    $ pipenv install --dev
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
2.  Build the documentation
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ cd /path/to/paperless
 | 
			
		||||
    $ pipenv mkdocs build
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
## Building the Docker image
 | 
			
		||||
 | 
			
		||||
The docker image is primarily built by the GitHub actions workflow, but
 | 
			
		||||
it can be faster when developing to build and tag an image locally.
 | 
			
		||||
 | 
			
		||||
To provide the build arguments automatically, build the image using the
 | 
			
		||||
helper script `build-docker-image.sh`.
 | 
			
		||||
 | 
			
		||||
Building the docker image from source:
 | 
			
		||||
 | 
			
		||||
> ```shell-session
 | 
			
		||||
> ./build-docker-image.sh Dockerfile -t <your-tag>
 | 
			
		||||
> ```
 | 
			
		||||
 | 
			
		||||
## Extending Paperless
 | 
			
		||||
 | 
			
		||||
Paperless does not have any fancy plugin systems and will probably never
 | 
			
		||||
have. However, some parts of the application have been designed to allow
 | 
			
		||||
easy integration of additional features without any modification to the
 | 
			
		||||
base code.
 | 
			
		||||
 | 
			
		||||
### Making custom parsers
 | 
			
		||||
 | 
			
		||||
Paperless uses parsers to add documents to paperless. A parser is
 | 
			
		||||
responsible for:
 | 
			
		||||
 | 
			
		||||
- Retrieve the content from the original
 | 
			
		||||
- Create a thumbnail
 | 
			
		||||
- Optional: Retrieve a created date from the original
 | 
			
		||||
- Optional: Create an archived document from the original
 | 
			
		||||
 | 
			
		||||
Custom parsers can be added to paperless to support more file types. In
 | 
			
		||||
order to do that, you need to write the parser itself and announce its
 | 
			
		||||
existence to paperless.
 | 
			
		||||
 | 
			
		||||
The parser itself must extend `documents.parsers.DocumentParser` and
 | 
			
		||||
must implement the methods `parse` and `get_thumbnail`. You can provide
 | 
			
		||||
your own implementation to `get_date` if you don't want to rely on
 | 
			
		||||
paperless' default date guessing mechanisms.
 | 
			
		||||
 | 
			
		||||
```python
 | 
			
		||||
class MyCustomParser(DocumentParser):
 | 
			
		||||
 | 
			
		||||
    def parse(self, document_path, mime_type):
 | 
			
		||||
        # This method does not return anything. Rather, you should assign
 | 
			
		||||
        # whatever you got from the document to the following fields:
 | 
			
		||||
 | 
			
		||||
        # The content of the document.
 | 
			
		||||
        self.text = "content"
 | 
			
		||||
 | 
			
		||||
        # Optional: path to a PDF document that you created from the original.
 | 
			
		||||
        self.archive_path = os.path.join(self.tempdir, "archived.pdf")
 | 
			
		||||
 | 
			
		||||
        # Optional: "created" date of the document.
 | 
			
		||||
        self.date = get_created_from_metadata(document_path)
 | 
			
		||||
 | 
			
		||||
    def get_thumbnail(self, document_path, mime_type):
 | 
			
		||||
        # This should return the path to a thumbnail you created for this
 | 
			
		||||
        # document.
 | 
			
		||||
        return os.path.join(self.tempdir, "thumb.png")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If you encounter any issues during parsing, raise a
 | 
			
		||||
`documents.parsers.ParseError`.
 | 
			
		||||
 | 
			
		||||
The `self.tempdir` directory is a temporary directory that is guaranteed
 | 
			
		||||
to be empty and removed after consumption finished. You can use that
 | 
			
		||||
directory to store any intermediate files and also use it to store the
 | 
			
		||||
thumbnail / archived document.
 | 
			
		||||
 | 
			
		||||
After that, you need to announce your parser to paperless. You need to
 | 
			
		||||
connect a handler to the `document_consumer_declaration` signal. Have a
 | 
			
		||||
look in the file `src/paperless_tesseract/apps.py` on how that's done.
 | 
			
		||||
The handler is a method that returns information about your parser:
 | 
			
		||||
 | 
			
		||||
```python
 | 
			
		||||
def myparser_consumer_declaration(sender, **kwargs):
 | 
			
		||||
    return {
 | 
			
		||||
        "parser": MyCustomParser,
 | 
			
		||||
        "weight": 0,
 | 
			
		||||
        "mime_types": {
 | 
			
		||||
            "application/pdf": ".pdf",
 | 
			
		||||
            "image/jpeg": ".jpg",
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- `parser` is a reference to a class that extends `DocumentParser`.
 | 
			
		||||
- `weight` is used whenever two or more parsers are able to parse a
 | 
			
		||||
  file: The parser with the higher weight wins. This can be used to
 | 
			
		||||
  override the parsers provided by paperless.
 | 
			
		||||
- `mime_types` is a dictionary. The keys are the mime types your
 | 
			
		||||
  parser supports and the value is the default file extension that
 | 
			
		||||
  paperless should use when storing files and serving them for
 | 
			
		||||
  download. We could guess that from the file extensions, but some
 | 
			
		||||
  mime types have many extensions associated with them and the python
 | 
			
		||||
  methods responsible for guessing the extension do not always return
 | 
			
		||||
  the same value.
 | 
			
		||||
@ -1,431 +0,0 @@
 | 
			
		||||
.. _extending:
 | 
			
		||||
 | 
			
		||||
Paperless-ngx Development
 | 
			
		||||
#########################
 | 
			
		||||
 | 
			
		||||
This section describes the steps you need to take to start development on paperless-ngx.
 | 
			
		||||
 | 
			
		||||
Check out the source from github. The repository is organized in the following way:
 | 
			
		||||
 | 
			
		||||
*   ``main`` always represents the latest release and will only see changes
 | 
			
		||||
    when a new release is made.
 | 
			
		||||
*   ``dev`` contains the code that will be in the next release.
 | 
			
		||||
*   ``feature-X`` contain bigger changes that will be in some release, but not
 | 
			
		||||
    necessarily the next one.
 | 
			
		||||
 | 
			
		||||
When making functional changes to paperless, *always* make your changes on the ``dev`` branch.
 | 
			
		||||
 | 
			
		||||
Apart from that, the folder structure is as follows:
 | 
			
		||||
 | 
			
		||||
*   ``docs/`` - Documentation.
 | 
			
		||||
*   ``src-ui/`` - Code of the front end.
 | 
			
		||||
*   ``src/`` - Code of the back end.
 | 
			
		||||
*   ``scripts/`` - Various scripts that help with different parts of development.
 | 
			
		||||
*   ``docker/`` - Files required to build the docker image.
 | 
			
		||||
 | 
			
		||||
Contributing to Paperless
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
Maybe you've been using Paperless for a while and want to add a feature or two,
 | 
			
		||||
or maybe you've come across a bug that you have some ideas how to solve.  The
 | 
			
		||||
beauty of open source software is that you can see what's wrong and help to get
 | 
			
		||||
it fixed for everyone!
 | 
			
		||||
 | 
			
		||||
Before contributing please review our `code of conduct`_ and other important
 | 
			
		||||
information in the `contributing guidelines`_.
 | 
			
		||||
 | 
			
		||||
.. _code-formatting-with-pre-commit-hooks:
 | 
			
		||||
 | 
			
		||||
Code formatting with pre-commit Hooks
 | 
			
		||||
=====================================
 | 
			
		||||
 | 
			
		||||
To ensure a consistent style and formatting across the project source, the project
 | 
			
		||||
utilizes a Git `pre-commit` hook to perform some formatting and linting before a
 | 
			
		||||
commit is allowed. That way, everyone uses the same style and some common issues
 | 
			
		||||
can be caught early on. See below for installation instructions.
 | 
			
		||||
 | 
			
		||||
Once installed, hooks will run when you commit. If the formatting isn't quite right
 | 
			
		||||
or a linter catches something, the commit will be rejected. You'll need to look at the
 | 
			
		||||
output and fix the issue. Some hooks, such as the Python formatting tool `black`,
 | 
			
		||||
will format failing files, so all you need to do is `git add` those files again and
 | 
			
		||||
retry your commit.
 | 
			
		||||
 | 
			
		||||
Initial setup and first start
 | 
			
		||||
=============================
 | 
			
		||||
 | 
			
		||||
After you forked and cloned the code from github you need to perform a first-time setup.
 | 
			
		||||
To do the setup you need to perform the steps from the following chapters in a certain order:
 | 
			
		||||
 | 
			
		||||
1.  Install prerequisites + pipenv as mentioned in :ref:`Bare metal route <setup-bare_metal>`
 | 
			
		||||
2.  Copy ``paperless.conf.example`` to ``paperless.conf`` and enable debug mode.
 | 
			
		||||
3.  Install the Angular CLI interface:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ npm install -g @angular/cli
 | 
			
		||||
 | 
			
		||||
4.  Install pre-commit
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        pre-commit install
 | 
			
		||||
 | 
			
		||||
5.  Create ``consume`` and ``media`` folders in the cloned root folder.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        mkdir -p consume media
 | 
			
		||||
 | 
			
		||||
6.  You can now either ...
 | 
			
		||||
 | 
			
		||||
    *  install redis or
 | 
			
		||||
    *  use the included scripts/start-services.sh to use docker to fire up a redis instance (and some other services such as tika, gotenberg and a database server) or
 | 
			
		||||
    *  spin up a bare redis container
 | 
			
		||||
 | 
			
		||||
        .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
            docker run -d -p 6379:6379 --restart unless-stopped redis:latest
 | 
			
		||||
 | 
			
		||||
7.  Install the python dependencies by performing in the src/ directory.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        pipenv install --dev
 | 
			
		||||
 | 
			
		||||
  * Make sure you're using python 3.9.x or lower. Otherwise you might get issues with building dependencies. You can use `pyenv <https://github.com/pyenv/pyenv>`_ to install a specific python version.
 | 
			
		||||
 | 
			
		||||
8.  Generate the static UI so you can perform a login to get session that is required for frontend development (this needs to be done one time only). From src-ui directory:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        npm install .
 | 
			
		||||
        ./node_modules/.bin/ng build --configuration production
 | 
			
		||||
 | 
			
		||||
9.  Apply migrations and create a superuser for your dev instance:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        python3 manage.py migrate
 | 
			
		||||
        python3 manage.py createsuperuser
 | 
			
		||||
 | 
			
		||||
10.  Now spin up the dev backend. Depending on which part of paperless you're developing for, you need to have some or all of them running.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        python3 manage.py runserver & python3 manage.py document_consumer & celery --app paperless worker
 | 
			
		||||
 | 
			
		||||
11. Login with the superuser credentials provided in step 8 at ``http://localhost:8000`` to create a session that enables you to use the backend.
 | 
			
		||||
 | 
			
		||||
Backend development environment is now ready, to start Frontend development go to ``/src-ui`` and run ``ng serve``. From there you can use ``http://localhost:4200`` for a preview.
 | 
			
		||||
 | 
			
		||||
Back end development
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
The backend is a django application. PyCharm works well for development, but you can use whatever
 | 
			
		||||
you want.
 | 
			
		||||
 | 
			
		||||
Configure the IDE to use the src/ folder as the base source folder. Configure the following
 | 
			
		||||
launch configurations in your IDE:
 | 
			
		||||
 | 
			
		||||
*   python3 manage.py runserver
 | 
			
		||||
*   celery --app paperless worker
 | 
			
		||||
*   python3 manage.py document_consumer
 | 
			
		||||
 | 
			
		||||
To start them all:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    python3 manage.py runserver & python3 manage.py document_consumer & celery --app paperless worker
 | 
			
		||||
 | 
			
		||||
Testing and code style:
 | 
			
		||||
 | 
			
		||||
*   Run ``pytest`` in the src/ directory to execute all tests. This also generates a HTML coverage
 | 
			
		||||
    report. When runnings test, paperless.conf is loaded as well. However: the tests rely on the default
 | 
			
		||||
    configuration. This is not ideal. But for now, make sure no settings except for DEBUG are overridden when testing.
 | 
			
		||||
*   Coding style is enforced by the Git pre-commit hooks.  These will ensure your code is formatted and do some
 | 
			
		||||
    linting when you do a `git commit`.
 | 
			
		||||
*   You can also run ``black`` manually to format your code
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        The line length rule E501 is generally useful for getting multiple source files
 | 
			
		||||
        next to each other on the screen. However, in some cases, its just not possible
 | 
			
		||||
        to make some lines fit, especially complicated IF cases. Append ``# NOQA: E501``
 | 
			
		||||
        to disable this check for certain lines.
 | 
			
		||||
 | 
			
		||||
Front end development
 | 
			
		||||
=====================
 | 
			
		||||
 | 
			
		||||
The front end is built using Angular. In order to get started, you need ``npm``.
 | 
			
		||||
Install the Angular CLI interface with
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ npm install -g @angular/cli
 | 
			
		||||
 | 
			
		||||
and make sure that it's on your path. Next, in the src-ui/ directory, install the
 | 
			
		||||
required dependencies of the project.
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ npm install
 | 
			
		||||
 | 
			
		||||
You can launch a development server by running
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ ng serve
 | 
			
		||||
 | 
			
		||||
This will automatically update whenever you save. However, in-place compilation might fail
 | 
			
		||||
on syntax errors, in which case you need to restart it.
 | 
			
		||||
 | 
			
		||||
By default, the development server is available on ``http://localhost:4200/`` and is configured
 | 
			
		||||
to access the API at ``http://localhost:8000/api/``, which is the default of the backend.
 | 
			
		||||
If you enabled DEBUG on the back end, several security overrides for allowed hosts, CORS and
 | 
			
		||||
X-Frame-Options are in place so that the front end behaves exactly as in production. This also
 | 
			
		||||
relies on you being logged into the back end. Without a valid session, The front end will simply
 | 
			
		||||
not work.
 | 
			
		||||
 | 
			
		||||
Testing and code style:
 | 
			
		||||
 | 
			
		||||
*   The frontend code (.ts, .html, .scss) use ``prettier`` for code formatting via the Git
 | 
			
		||||
    ``pre-commit`` hooks which run automatically on commit. See
 | 
			
		||||
    :ref:`above <code-formatting-with-pre-commit-hooks>` for installation. You can also run this
 | 
			
		||||
    via cli with a command such as
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ git ls-files -- '*.ts' | xargs pre-commit run prettier --files
 | 
			
		||||
 | 
			
		||||
*   Frontend testing uses jest and cypress. There is currently a need for significantly more
 | 
			
		||||
    frontend tests. Unit tests and e2e tests, respectively, can be run non-interactively with:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ ng test
 | 
			
		||||
        $ npm run e2e:ci
 | 
			
		||||
 | 
			
		||||
    Cypress also includes a UI which can be run from within the ``src-ui`` directory with
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ ./node_modules/.bin/cypress open
 | 
			
		||||
 | 
			
		||||
In order to build the front end and serve it as part of django, execute
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ ng build --prod
 | 
			
		||||
 | 
			
		||||
This will build the front end and put it in a location from which the Django server will serve
 | 
			
		||||
it as static content. This way, you can verify that authentication is working.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Localization
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
Paperless is available in many different languages. Since paperless consists both of a django
 | 
			
		||||
application and an Angular front end, both these parts have to be translated separately.
 | 
			
		||||
 | 
			
		||||
Front end localization
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
*   The Angular front end does localization according to the `Angular documentation <https://angular.io/guide/i18n>`_.
 | 
			
		||||
*   The source language of the project is "en_US".
 | 
			
		||||
*   The source strings end up in the file "src-ui/messages.xlf".
 | 
			
		||||
*   The translated strings need to be placed in the "src-ui/src/locale/" folder.
 | 
			
		||||
*   In order to extract added or changed strings from the source files, call ``ng xi18n --ivy``.
 | 
			
		||||
 | 
			
		||||
Adding new languages requires adding the translated files in the "src-ui/src/locale/" folder and adjusting a couple files.
 | 
			
		||||
 | 
			
		||||
1.  Adjust "src-ui/angular.json":
 | 
			
		||||
 | 
			
		||||
    .. code:: json
 | 
			
		||||
 | 
			
		||||
        "i18n": {
 | 
			
		||||
            "sourceLocale": "en-US",
 | 
			
		||||
            "locales": {
 | 
			
		||||
                "de": "src/locale/messages.de.xlf",
 | 
			
		||||
                "nl-NL": "src/locale/messages.nl_NL.xlf",
 | 
			
		||||
                "fr": "src/locale/messages.fr.xlf",
 | 
			
		||||
                "en-GB": "src/locale/messages.en_GB.xlf",
 | 
			
		||||
                "pt-BR": "src/locale/messages.pt_BR.xlf",
 | 
			
		||||
                "language-code": "language-file"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
2.  Add the language to the available options in "src-ui/src/app/services/settings.service.ts":
 | 
			
		||||
 | 
			
		||||
    .. code:: typescript
 | 
			
		||||
 | 
			
		||||
        getLanguageOptions(): LanguageOption[] {
 | 
			
		||||
            return [
 | 
			
		||||
                {code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
 | 
			
		||||
                {code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
 | 
			
		||||
                {code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
 | 
			
		||||
                {code: "nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
 | 
			
		||||
                {code: "fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
 | 
			
		||||
                {code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"}
 | 
			
		||||
                // Add your new language here
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    ``dateInputFormat`` is a special string that defines the behavior of the date input fields and absolutely needs to contain "dd", "mm" and "yyyy".
 | 
			
		||||
 | 
			
		||||
3.  Import and register the Angular data for this locale in "src-ui/src/app/app.module.ts":
 | 
			
		||||
 | 
			
		||||
    .. code:: typescript
 | 
			
		||||
 | 
			
		||||
        import localeDe from '@angular/common/locales/de';
 | 
			
		||||
        registerLocaleData(localeDe)
 | 
			
		||||
 | 
			
		||||
Back end localization
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
A majority of the strings that appear in the back end appear only when the admin is used. However,
 | 
			
		||||
some of these are still shown on the front end (such as error messages).
 | 
			
		||||
 | 
			
		||||
*   The django application does localization according to the `django documentation <https://docs.djangoproject.com/en/3.1/topics/i18n/translation/>`_.
 | 
			
		||||
*   The source language of the project is "en_US".
 | 
			
		||||
*   Localization files end up in the folder "src/locale/".
 | 
			
		||||
*   In order to extract strings from the application, call ``python3 manage.py makemessages -l en_US``. This is important after making changes to translatable strings.
 | 
			
		||||
*   The message files need to be compiled for them to show up in the application. Call ``python3 manage.py compilemessages`` to do this. The generated files don't get
 | 
			
		||||
    committed into git, since these are derived artifacts. The build pipeline takes care of executing this command.
 | 
			
		||||
 | 
			
		||||
Adding new languages requires adding the translated files in the "src/locale/" folder and adjusting the file "src/paperless/settings.py" to include the new language:
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    LANGUAGES = [
 | 
			
		||||
        ("en-us", _("English (US)")),
 | 
			
		||||
        ("en-gb", _("English (GB)")),
 | 
			
		||||
        ("de", _("German")),
 | 
			
		||||
        ("nl-nl", _("Dutch")),
 | 
			
		||||
        ("fr", _("French")),
 | 
			
		||||
        ("pt-br", _("Portuguese (Brazil)")),
 | 
			
		||||
        # Add language here.
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Building the documentation
 | 
			
		||||
==========================
 | 
			
		||||
 | 
			
		||||
The documentation is built using sphinx. I've configured ReadTheDocs to automatically build
 | 
			
		||||
the documentation when changes are pushed. If you want to build the documentation locally,
 | 
			
		||||
this is how you do it:
 | 
			
		||||
 | 
			
		||||
1.  Install python dependencies.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ cd /path/to/paperless
 | 
			
		||||
        $ pipenv install --dev
 | 
			
		||||
 | 
			
		||||
2.  Build the documentation
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ cd /path/to/paperless/docs
 | 
			
		||||
        $ pipenv run make clean html
 | 
			
		||||
 | 
			
		||||
This will build the HTML documentation, and put the resulting files in the ``_build/html``
 | 
			
		||||
directory.
 | 
			
		||||
 | 
			
		||||
Building the Docker image
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
The docker image is primarily built by the GitHub actions workflow, but it can be
 | 
			
		||||
faster when developing to build and tag an image locally.
 | 
			
		||||
 | 
			
		||||
To provide the build arguments automatically, build the image using the helper
 | 
			
		||||
script ``build-docker-image.sh``.
 | 
			
		||||
 | 
			
		||||
Building the docker image from source:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        ./build-docker-image.sh Dockerfile -t <your-tag>
 | 
			
		||||
 | 
			
		||||
Extending Paperless
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
Paperless does not have any fancy plugin systems and will probably never have. However,
 | 
			
		||||
some parts of the application have been designed to allow easy integration of additional
 | 
			
		||||
features without any modification to the base code.
 | 
			
		||||
 | 
			
		||||
Making custom parsers
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
Paperless uses parsers to add documents to paperless. A parser is responsible for:
 | 
			
		||||
 | 
			
		||||
*   Retrieve the content from the original
 | 
			
		||||
*   Create a thumbnail
 | 
			
		||||
*   Optional: Retrieve a created date from the original
 | 
			
		||||
*   Optional: Create an archived document from the original
 | 
			
		||||
 | 
			
		||||
Custom parsers can be added to paperless to support more file types. In order to do that,
 | 
			
		||||
you need to write the parser itself and announce its existence to paperless.
 | 
			
		||||
 | 
			
		||||
The parser itself must extend ``documents.parsers.DocumentParser`` and must implement the
 | 
			
		||||
methods ``parse`` and ``get_thumbnail``. You can provide your own implementation to
 | 
			
		||||
``get_date`` if you don't want to rely on paperless' default date guessing mechanisms.
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    class MyCustomParser(DocumentParser):
 | 
			
		||||
 | 
			
		||||
        def parse(self, document_path, mime_type):
 | 
			
		||||
            # This method does not return anything. Rather, you should assign
 | 
			
		||||
            # whatever you got from the document to the following fields:
 | 
			
		||||
 | 
			
		||||
            # The content of the document.
 | 
			
		||||
            self.text = "content"
 | 
			
		||||
 | 
			
		||||
            # Optional: path to a PDF document that you created from the original.
 | 
			
		||||
            self.archive_path = os.path.join(self.tempdir, "archived.pdf")
 | 
			
		||||
 | 
			
		||||
            # Optional: "created" date of the document.
 | 
			
		||||
            self.date = get_created_from_metadata(document_path)
 | 
			
		||||
 | 
			
		||||
        def get_thumbnail(self, document_path, mime_type):
 | 
			
		||||
            # This should return the path to a thumbnail you created for this
 | 
			
		||||
            # document.
 | 
			
		||||
            return os.path.join(self.tempdir, "thumb.png")
 | 
			
		||||
 | 
			
		||||
If you encounter any issues during parsing, raise a ``documents.parsers.ParseError``.
 | 
			
		||||
 | 
			
		||||
The ``self.tempdir`` directory is a temporary directory that is guaranteed to be empty
 | 
			
		||||
and removed after consumption finished. You can use that directory to store any
 | 
			
		||||
intermediate files and also use it to store the thumbnail / archived document.
 | 
			
		||||
 | 
			
		||||
After that, you need to announce your parser to paperless. You need to connect a
 | 
			
		||||
handler to the ``document_consumer_declaration`` signal. Have a look in the file
 | 
			
		||||
``src/paperless_tesseract/apps.py`` on how that's done. The handler is a method
 | 
			
		||||
that returns information about your parser:
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    def myparser_consumer_declaration(sender, **kwargs):
 | 
			
		||||
        return {
 | 
			
		||||
            "parser": MyCustomParser,
 | 
			
		||||
            "weight": 0,
 | 
			
		||||
            "mime_types": {
 | 
			
		||||
                "application/pdf": ".pdf",
 | 
			
		||||
                "image/jpeg": ".jpg",
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
*   ``parser`` is a reference to a class that extends ``DocumentParser``.
 | 
			
		||||
 | 
			
		||||
*   ``weight`` is used whenever two or more parsers are able to parse a file: The parser with
 | 
			
		||||
    the higher weight wins. This can be used to override the parsers provided by
 | 
			
		||||
    paperless.
 | 
			
		||||
 | 
			
		||||
*   ``mime_types`` is a dictionary. The keys are the mime types your parser supports and the value
 | 
			
		||||
    is the default file extension that paperless should use when storing files and serving them for
 | 
			
		||||
    download. We could guess that from the file extensions, but some mime types have many extensions
 | 
			
		||||
    associated with them and the python methods responsible for guessing the extension do not always
 | 
			
		||||
    return the same value.
 | 
			
		||||
 | 
			
		||||
.. _code of conduct: https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md
 | 
			
		||||
.. _contributing guidelines: https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md
 | 
			
		||||
							
								
								
									
										128
									
								
								docs/faq.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,128 @@
 | 
			
		||||
# Frequently Asked Questions
 | 
			
		||||
 | 
			
		||||
### _What's the general plan for Paperless-ngx?_
 | 
			
		||||
 | 
			
		||||
**A:** While Paperless-ngx is already considered largely
 | 
			
		||||
"feature-complete" it is a community-driven project and development
 | 
			
		||||
will be guided in this way. New features can be submitted via GitHub
 | 
			
		||||
discussions and "up-voted" by the community but this is not a
 | 
			
		||||
guarantee the feature will be implemented. This project will always be
 | 
			
		||||
open to collaboration in the form of PRs, ideas etc.
 | 
			
		||||
 | 
			
		||||
### _I'm using docker. Where are my documents?_
 | 
			
		||||
 | 
			
		||||
**A:** Your documents are stored inside the docker volume
 | 
			
		||||
`paperless_media`. Docker manages this volume automatically for you. It
 | 
			
		||||
is a persistent storage and will persist as long as you don't
 | 
			
		||||
explicitly delete it. The actual location depends on your host operating
 | 
			
		||||
system. On Linux, chances are high that this location is
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
/var/lib/docker/volumes/paperless_media/_data
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    Do not mess with this folder. Don't change permissions and don't move
 | 
			
		||||
    files around manually. This folder is meant to be entirely managed by
 | 
			
		||||
    docker and paperless.
 | 
			
		||||
 | 
			
		||||
### Let's say I want to switch tools in a year. Can I easily move
 | 
			
		||||
 | 
			
		||||
to other systems?\*
 | 
			
		||||
 | 
			
		||||
**A:** Your documents are stored as plain files inside the media folder.
 | 
			
		||||
You can always drag those files out of that folder to use them
 | 
			
		||||
elsewhere. Here are a couple notes about that.
 | 
			
		||||
 | 
			
		||||
- Paperless-ngx never modifies your original documents. It keeps
 | 
			
		||||
  checksums of all documents and uses a scheduled sanity checker to
 | 
			
		||||
  check that they remain the same.
 | 
			
		||||
- By default, paperless uses the internal ID of each document as its
 | 
			
		||||
  filename. This might not be very convenient for export. However, you
 | 
			
		||||
  can adjust the way files are stored in paperless by
 | 
			
		||||
  `configuring the filename format <advanced-file_name_handling>`{.interpreted-text
 | 
			
		||||
  role="ref"}.
 | 
			
		||||
- `The exporter <utilities-exporter>`{.interpreted-text role="ref"} is
 | 
			
		||||
  another easy way to get your files out of paperless with reasonable
 | 
			
		||||
  file names.
 | 
			
		||||
 | 
			
		||||
### _What file types does paperless-ngx support?_
 | 
			
		||||
 | 
			
		||||
**A:** Currently, the following files are supported:
 | 
			
		||||
 | 
			
		||||
- PDF documents, PNG images, JPEG images, TIFF images and GIF images
 | 
			
		||||
  are processed with OCR and converted into PDF documents.
 | 
			
		||||
- Plain text documents are supported as well and are added verbatim to
 | 
			
		||||
  paperless.
 | 
			
		||||
- With the optional Tika integration enabled (see
 | 
			
		||||
  `Configuration <configuration-tika>`{.interpreted-text role="ref"}),
 | 
			
		||||
  Paperless also supports various Office documents (.docx, .doc, odt,
 | 
			
		||||
  .ppt, .pptx, .odp, .xls, .xlsx, .ods).
 | 
			
		||||
 | 
			
		||||
Paperless-ngx determines the type of a file by inspecting its content.
 | 
			
		||||
The file extensions do not matter.
 | 
			
		||||
 | 
			
		||||
### _Will paperless-ngx run on Raspberry Pi?_
 | 
			
		||||
 | 
			
		||||
**A:** The short answer is yes. I've tested it on a Raspberry Pi 3 B.
 | 
			
		||||
The long answer is that certain parts of Paperless will run very slow,
 | 
			
		||||
such as the OCR. On Raspberry Pi, try to OCR documents before feeding
 | 
			
		||||
them into paperless so that paperless can reuse the text. The web
 | 
			
		||||
interface is a lot snappier, since it runs in your browser and paperless
 | 
			
		||||
has to do much less work to serve the data.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    You can adjust some of the settings so that paperless uses less
 | 
			
		||||
    processing power. See `setup-less_powerful_devices`{.interpreted-text
 | 
			
		||||
    role="ref"} for details.
 | 
			
		||||
 | 
			
		||||
### _How do I install paperless-ngx on Raspberry Pi?_
 | 
			
		||||
 | 
			
		||||
**A:** Docker images are available for arm and arm64 hardware, so just
 | 
			
		||||
follow the docker-compose instructions. Apart from more required disk
 | 
			
		||||
space compared to a bare metal installation, docker comes with close to
 | 
			
		||||
zero overhead, even on Raspberry Pi.
 | 
			
		||||
 | 
			
		||||
If you decide to got with the bare metal route, be aware that some of
 | 
			
		||||
the python requirements do not have precompiled packages for ARM /
 | 
			
		||||
ARM64. Installation of these will require additional development
 | 
			
		||||
libraries and compilation will take a long time.
 | 
			
		||||
 | 
			
		||||
### _How do I run this on Unraid?_
 | 
			
		||||
 | 
			
		||||
**A:** Paperless-ngx is available as [community
 | 
			
		||||
app](https://unraid.net/community/apps?q=paperless-ngx) in Unraid. [Uli
 | 
			
		||||
Fahrer](https://github.com/Tooa) created a container template for that.
 | 
			
		||||
 | 
			
		||||
### _How do I run this on my toaster?_
 | 
			
		||||
 | 
			
		||||
**A:** I honestly don't know! As for all other devices that might be
 | 
			
		||||
able to run paperless, you're a bit on your own. If you can't run the
 | 
			
		||||
docker image, the documentation has instructions for bare metal
 | 
			
		||||
installs. I'm running paperless on an i3 processor from 2015 or so.
 | 
			
		||||
This is also what I use to test new releases with. Apart from that, I
 | 
			
		||||
also have a Raspberry Pi, which I occasionally build the image on and
 | 
			
		||||
see if it works.
 | 
			
		||||
 | 
			
		||||
### _How do I proxy this with NGINX?_
 | 
			
		||||
 | 
			
		||||
**A:** See `here <setup-nginx>`{.interpreted-text role="ref"}.
 | 
			
		||||
 | 
			
		||||
### _How do I get WebSocket support with Apache mod_wsgi_?
 | 
			
		||||
 | 
			
		||||
**A:** `mod_wsgi` by itself does not support ASGI. Paperless will
 | 
			
		||||
continue to work with WSGI, but certain features such as status
 | 
			
		||||
notifications about document consumption won't be available.
 | 
			
		||||
 | 
			
		||||
If you want to continue using `mod_wsgi`, you will have to run an
 | 
			
		||||
ASGI-enabled web server as well that processes WebSocket connections,
 | 
			
		||||
and configure Apache to redirect WebSocket connections to this server.
 | 
			
		||||
Multiple options for ASGI servers exist:
 | 
			
		||||
 | 
			
		||||
- `gunicorn` with `uvicorn` as the worker implementation (the default
 | 
			
		||||
  of paperless)
 | 
			
		||||
- `daphne` as a standalone server, which is the reference
 | 
			
		||||
  implementation for ASGI.
 | 
			
		||||
- `uvicorn` as a standalone server
 | 
			
		||||
							
								
								
									
										117
									
								
								docs/faq.rst
									
									
									
									
									
								
							
							
						
						@ -1,117 +0,0 @@
 | 
			
		||||
 | 
			
		||||
**************************
 | 
			
		||||
Frequently asked questions
 | 
			
		||||
**************************
 | 
			
		||||
 | 
			
		||||
**Q:** *What's the general plan for Paperless-ngx?*
 | 
			
		||||
 | 
			
		||||
**A:** While Paperless-ngx is already considered largely "feature-complete" it is a community-driven
 | 
			
		||||
project and development will be guided in this way. New features can be submitted via
 | 
			
		||||
GitHub discussions and "up-voted" by the community but this is not a guarantee the feature
 | 
			
		||||
will be implemented. This project will always be open to collaboration in the form of PRs,
 | 
			
		||||
ideas etc.
 | 
			
		||||
 | 
			
		||||
**Q:** *I'm using docker. Where are my documents?*
 | 
			
		||||
 | 
			
		||||
**A:** Your documents are stored inside the docker volume ``paperless_media``.
 | 
			
		||||
Docker manages this volume automatically for you. It is a persistent storage
 | 
			
		||||
and will persist as long as you don't explicitly delete it. The actual location
 | 
			
		||||
depends on your host operating system. On Linux, chances are high that this location
 | 
			
		||||
is
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    /var/lib/docker/volumes/paperless_media/_data
 | 
			
		||||
 | 
			
		||||
.. caution::
 | 
			
		||||
 | 
			
		||||
    Do not mess with this folder. Don't change permissions and don't move
 | 
			
		||||
    files around manually. This folder is meant to be entirely managed by docker
 | 
			
		||||
    and paperless.
 | 
			
		||||
 | 
			
		||||
**Q:** *Let's say I want to switch tools in a year. Can I easily move to other systems?*
 | 
			
		||||
 | 
			
		||||
**A:** Your documents are stored as plain files inside the media folder. You can always drag those files
 | 
			
		||||
out of that folder to use them elsewhere. Here are a couple notes about that.
 | 
			
		||||
 | 
			
		||||
*   Paperless-ngx never modifies your original documents. It keeps checksums of all documents and uses a
 | 
			
		||||
    scheduled sanity checker to check that they remain the same.
 | 
			
		||||
*   By default, paperless uses the internal ID of each document as its filename. This might not be very
 | 
			
		||||
    convenient for export. However, you can adjust the way files are stored in paperless by
 | 
			
		||||
    :ref:`configuring the filename format <advanced-file_name_handling>`.
 | 
			
		||||
*   :ref:`The exporter <utilities-exporter>` is another easy way to get your files out of paperless with reasonable file names.
 | 
			
		||||
 | 
			
		||||
**Q:** *What file types does paperless-ngx support?*
 | 
			
		||||
 | 
			
		||||
**A:** Currently, the following files are supported:
 | 
			
		||||
 | 
			
		||||
*   PDF documents, PNG images, JPEG images, TIFF images and GIF images are processed with OCR and converted into PDF documents.
 | 
			
		||||
*   Plain text documents are supported as well and are added verbatim
 | 
			
		||||
    to paperless.
 | 
			
		||||
*   With the optional Tika integration enabled (see :ref:`Configuration <configuration-tika>`), Paperless also supports various
 | 
			
		||||
    Office documents (.docx, .doc, odt, .ppt, .pptx, .odp, .xls, .xlsx, .ods).
 | 
			
		||||
 | 
			
		||||
Paperless-ngx determines the type of a file by inspecting its content. The
 | 
			
		||||
file extensions do not matter.
 | 
			
		||||
 | 
			
		||||
**Q:** *Will paperless-ngx run on Raspberry Pi?*
 | 
			
		||||
 | 
			
		||||
**A:** The short answer is yes. I've tested it on a Raspberry Pi 3 B.
 | 
			
		||||
The long answer is that certain parts of
 | 
			
		||||
Paperless will run very slow, such as the OCR. On Raspberry Pi,
 | 
			
		||||
try to OCR documents before feeding them into paperless so that paperless can
 | 
			
		||||
reuse the text. The web interface is a lot snappier, since it runs
 | 
			
		||||
in your browser and paperless has to do much less work to serve the data.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    You can adjust some of the settings so that paperless uses less processing
 | 
			
		||||
    power. See :ref:`setup-less_powerful_devices` for details.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
**Q:** *How do I install paperless-ngx on Raspberry Pi?*
 | 
			
		||||
 | 
			
		||||
**A:** Docker images are available for arm and arm64 hardware, so just follow
 | 
			
		||||
the docker-compose instructions. Apart from more required disk space compared to
 | 
			
		||||
a bare metal installation, docker comes with close to zero overhead, even on
 | 
			
		||||
Raspberry Pi.
 | 
			
		||||
 | 
			
		||||
If you decide to got with the bare metal route, be aware that some of the
 | 
			
		||||
python requirements do not have precompiled packages for ARM / ARM64. Installation
 | 
			
		||||
of these will require additional development libraries and compilation will take
 | 
			
		||||
a long time.
 | 
			
		||||
 | 
			
		||||
**Q:** *How do I run this on Unraid?*
 | 
			
		||||
 | 
			
		||||
**A:** Paperless-ngx is available as `community app <https://unraid.net/community/apps?q=paperless-ngx>`_
 | 
			
		||||
in Unraid. `Uli Fahrer <https://github.com/Tooa>`_ created a container template for that.
 | 
			
		||||
 | 
			
		||||
**Q:** *How do I run this on my toaster?*
 | 
			
		||||
 | 
			
		||||
**A:** I honestly don't know! As for all other devices that might be able
 | 
			
		||||
to run paperless, you're a bit on your own. If you can't run the docker image,
 | 
			
		||||
the documentation has instructions for bare metal installs. I'm running
 | 
			
		||||
paperless on an i3 processor from 2015 or so. This is also what I use to test
 | 
			
		||||
new releases with. Apart from that, I also have a Raspberry Pi, which I
 | 
			
		||||
occasionally build the image on and see if it works.
 | 
			
		||||
 | 
			
		||||
**Q:** *How do I proxy this with NGINX?*
 | 
			
		||||
 | 
			
		||||
**A:** See :ref:`here <setup-nginx>`.
 | 
			
		||||
 | 
			
		||||
.. _faq-mod_wsgi:
 | 
			
		||||
 | 
			
		||||
**Q:** *How do I get WebSocket support with Apache mod_wsgi*?
 | 
			
		||||
 | 
			
		||||
**A:** ``mod_wsgi`` by itself does not support ASGI. Paperless will continue
 | 
			
		||||
to work with WSGI, but certain features such as status notifications about
 | 
			
		||||
document consumption won't be available.
 | 
			
		||||
 | 
			
		||||
If you want to continue using ``mod_wsgi``, you will have to run an ASGI-enabled
 | 
			
		||||
web server as well that processes WebSocket connections, and configure Apache to
 | 
			
		||||
redirect WebSocket connections to this server. Multiple options for ASGI servers
 | 
			
		||||
exist:
 | 
			
		||||
 | 
			
		||||
* ``gunicorn`` with ``uvicorn`` as the worker implementation (the default of paperless)
 | 
			
		||||
* ``daphne`` as a standalone server, which is the reference implementation for ASGI.
 | 
			
		||||
* ``uvicorn`` as a standalone server
 | 
			
		||||
							
								
								
									
										138
									
								
								docs/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,138 @@
 | 
			
		||||
<div class="grid-left" markdown>
 | 
			
		||||
{.index-logo}
 | 
			
		||||
{.index-logo}
 | 
			
		||||
 | 
			
		||||
**Paperless-ngx** is a _community-supported_ open-source document management system that transforms your
 | 
			
		||||
physical documents into a searchable online archive so you can keep, well, _less paper_.
 | 
			
		||||
 | 
			
		||||
[Get started](/setup/){ .md-button .md-button--primary .index-callout }
 | 
			
		||||
[Demo](https://demo.paperless-ngx.com){ .md-button .md-button--secondary }
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
<div class="grid-right" markdown>
 | 
			
		||||
{.index-screenshot}
 | 
			
		||||
{.index-screenshot}
 | 
			
		||||
</div>
 | 
			
		||||
<div class="clear"></div>
 | 
			
		||||
 | 
			
		||||
## Why This Exists
 | 
			
		||||
 | 
			
		||||
Paper is a nightmare. Environmental issues aside, there's no excuse for
 | 
			
		||||
it in the 21st century. It takes up space, collects dust, doesn't
 | 
			
		||||
support any form of a search feature, indexing is tedious, it's heavy
 | 
			
		||||
and prone to damage & loss.
 | 
			
		||||
 | 
			
		||||
This software is designed to make "going paperless" easier. No more worrying
 | 
			
		||||
about finding stuff again, feed documents right from the post box into
 | 
			
		||||
the scanner and then shred them. Perhaps you might find it useful too.
 | 
			
		||||
 | 
			
		||||
## Paperless, a history
 | 
			
		||||
 | 
			
		||||
Paperless is a simple Django application running in two parts: a
 | 
			
		||||
_Consumer_ (the thing that does the indexing) and the _Web server_ (the
 | 
			
		||||
part that lets you search & download already-indexed documents). If you
 | 
			
		||||
want to learn more about its functions keep on reading after the
 | 
			
		||||
installation section.
 | 
			
		||||
 | 
			
		||||
Paperless-ngx is a document management system that transforms your
 | 
			
		||||
physical documents into a searchable online archive so you can keep,
 | 
			
		||||
well, _less paper_.
 | 
			
		||||
 | 
			
		||||
Paperless-ngx forked from paperless-ng to continue the great work and
 | 
			
		||||
distribute responsibility of supporting and advancing the project among
 | 
			
		||||
a team of people.
 | 
			
		||||
 | 
			
		||||
NG stands for both Angular (the framework used for the Frontend) and
 | 
			
		||||
next-gen. Publishing this project under a different name also avoids
 | 
			
		||||
confusion between paperless and paperless-ngx.
 | 
			
		||||
 | 
			
		||||
If you want to learn about what's different in paperless-ngx from
 | 
			
		||||
Paperless, check out these resources in the documentation:
 | 
			
		||||
 | 
			
		||||
- [Some screenshots](#screenshots) of the new UI are available.
 | 
			
		||||
- Read [this section](/advanced_usage/#advanced-automatic_matching) if you want to learn about how paperless automates all
 | 
			
		||||
  tagging using machine learning.
 | 
			
		||||
- Paperless now comes with a `[proper email consumer](/usage/#usage-email) that's fully tested and production ready.
 | 
			
		||||
- Paperless creates searchable PDF/A documents from whatever you put into the consumption directory. This means
 | 
			
		||||
  that you can select text in image-only documents coming from your scanner.
 | 
			
		||||
- See [this note](/administration/#utilities-encyption) about GnuPG encryption in paperless-ngx.
 | 
			
		||||
- Paperless is now integrated with a
 | 
			
		||||
  [task processing queue](/setup#task_processor) that tells you at a glance when and why something is not working.
 | 
			
		||||
- The [changelog](/changelog) contains a detailed list of all changes in paperless-ngx.
 | 
			
		||||
 | 
			
		||||
## Screenshots
 | 
			
		||||
 | 
			
		||||
This is what Paperless-ngx looks like.
 | 
			
		||||
 | 
			
		||||
The dashboard shows customizable views on your document and allows
 | 
			
		||||
document uploads:
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/dashboard.png)
 | 
			
		||||
 | 
			
		||||
The document list provides three different styles to scroll through your
 | 
			
		||||
documents:
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/documents-table.png)
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/documents-smallcards.png)
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/documents-largecards.png)
 | 
			
		||||
 | 
			
		||||
Paperless-ngx also supports dark mode:
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/documents-smallcards-dark.png)
 | 
			
		||||
 | 
			
		||||
Extensive filtering mechanisms:
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/documents-filter.png)
 | 
			
		||||
 | 
			
		||||
Bulk editing of document tags, correspondents, etc.:
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/bulk-edit.png)
 | 
			
		||||
 | 
			
		||||
Side-by-side editing of documents:
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/editing.png)
 | 
			
		||||
 | 
			
		||||
Tag editing. This looks about the same for correspondents and document
 | 
			
		||||
types.
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/new-tag.png)
 | 
			
		||||
 | 
			
		||||
Searching provides auto complete and highlights the results.
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/search-preview.png)
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/search-results.png)
 | 
			
		||||
 | 
			
		||||
Fancy mail filters!
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/mail-rules-edited.png)
 | 
			
		||||
 | 
			
		||||
Mobile devices are supported.
 | 
			
		||||
 | 
			
		||||
[](assets/screenshots/mobile.png)
 | 
			
		||||
 | 
			
		||||
## Support
 | 
			
		||||
 | 
			
		||||
Community support is available via [GitHub Discussions](https://github.com/paperless-ngx/paperless-ngx/discussions/) and [the Matrix chat room](https://matrix.to/#/#paperless:matrix.org).
 | 
			
		||||
 | 
			
		||||
### Feature Requests
 | 
			
		||||
 | 
			
		||||
Feature requests can be submitted via [GitHub Discussions](https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests) where you can search for existing ideas, add your own and vote for the ones you care about.
 | 
			
		||||
 | 
			
		||||
### Bugs
 | 
			
		||||
 | 
			
		||||
For bugs please [open an issue](https://github.com/paperless-ngx/paperless-ngx/issues) or [start a discussion](https://github.com/paperless-ngx/paperless-ngx/discussions/categories/support) if you have questions.
 | 
			
		||||
 | 
			
		||||
## Contributing
 | 
			
		||||
 | 
			
		||||
People interested in continuing the work on paperless-ngx are encouraged to reach out on [GitHub](https://github.com/paperless-ngx/paperless-ngx) or [the Matrix chat room](https://matrix.to/#/#paperless:matrix.org). If you would like to contribute to the project on an ongoing basis there are multiple teams (frontend, ci/cd, etc) that could use your help so please reach out!
 | 
			
		||||
 | 
			
		||||
### Translation
 | 
			
		||||
 | 
			
		||||
Paperless-ngx is available in many languages that are coordinated on [Crowdin](https://crwd.in/paperless-ngx). If you want to help out by translating paperless-ngx into your language, please head over to https://crwd.in/paperless-ngx, and thank you!
 | 
			
		||||
 | 
			
		||||
## Scanners & Software
 | 
			
		||||
 | 
			
		||||
Paperless-ngx is compatible with many different scanners and scanning tools. A user-maintained list of scanners and other software is available on [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Scanner-&-Software-Recommendations).
 | 
			
		||||
@ -1,75 +0,0 @@
 | 
			
		||||
*********
 | 
			
		||||
Paperless
 | 
			
		||||
*********
 | 
			
		||||
 | 
			
		||||
Paperless is a simple Django application running in two parts:
 | 
			
		||||
a *Consumer* (the thing that does the indexing) and
 | 
			
		||||
the *Web server* (the part that lets you search &
 | 
			
		||||
download already-indexed documents). If you want to learn more about its
 | 
			
		||||
functions keep on reading after the installation section.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Why This Exists
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
Paper is a nightmare.  Environmental issues aside, there's no excuse for it in
 | 
			
		||||
the 21st century.  It takes up space, collects dust, doesn't support any form
 | 
			
		||||
of a search feature, indexing is tedious, it's heavy and prone to damage &
 | 
			
		||||
loss.
 | 
			
		||||
 | 
			
		||||
I wrote this to make "going paperless" easier.  I do not have to worry about
 | 
			
		||||
finding stuff again. I feed documents right from the post box into the scanner
 | 
			
		||||
and then shred them.  Perhaps you might find it useful too.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Paperless-ngx
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
Paperless-ngx is a document management system that transforms your physical
 | 
			
		||||
documents into a searchable online archive so you can keep, well, *less paper*.
 | 
			
		||||
 | 
			
		||||
Paperless-ngx forked from paperless-ng to continue the great work and
 | 
			
		||||
distribute responsibility of supporting and advancing the project among a team
 | 
			
		||||
of people.
 | 
			
		||||
 | 
			
		||||
NG stands for both Angular (the framework used for the
 | 
			
		||||
Frontend) and next-gen. Publishing this project under a different name also
 | 
			
		||||
avoids confusion between paperless and paperless-ngx.
 | 
			
		||||
 | 
			
		||||
If you want to learn about what's different in paperless-ngx from Paperless, check out these
 | 
			
		||||
resources in the documentation:
 | 
			
		||||
 | 
			
		||||
*   :ref:`Some screenshots <screenshots>` of the new UI are available.
 | 
			
		||||
*   Read :ref:`this section <advanced-automatic_matching>` if you want to
 | 
			
		||||
    learn about how paperless automates all tagging using machine learning.
 | 
			
		||||
*   Paperless now comes with a :ref:`proper email consumer <usage-email>`
 | 
			
		||||
    that's fully tested and production ready.
 | 
			
		||||
*   Paperless creates searchable PDF/A documents from whatever you put into
 | 
			
		||||
    the consumption directory. This means that you can select text in
 | 
			
		||||
    image-only documents coming from your scanner.
 | 
			
		||||
*   See :ref:`this note <utilities-encyption>` about GnuPG encryption in
 | 
			
		||||
    paperless-ngx.
 | 
			
		||||
*   Paperless is now integrated with a
 | 
			
		||||
    :ref:`task processing queue <setup-task_processor>` that tells you
 | 
			
		||||
    at a glance when and why something is not working.
 | 
			
		||||
*   The :doc:`changelog </changelog>` contains a detailed list of all changes
 | 
			
		||||
    in paperless-ngx.
 | 
			
		||||
 | 
			
		||||
Contents
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 1
 | 
			
		||||
 | 
			
		||||
   setup
 | 
			
		||||
   usage_overview
 | 
			
		||||
   advanced_usage
 | 
			
		||||
   administration
 | 
			
		||||
   configuration
 | 
			
		||||
   api
 | 
			
		||||
   faq
 | 
			
		||||
   troubleshooting
 | 
			
		||||
   extending
 | 
			
		||||
   scanners
 | 
			
		||||
   screenshots
 | 
			
		||||
   changelog
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
myst-parser==0.18.1
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
 | 
			
		||||
.. _scanners:
 | 
			
		||||
 | 
			
		||||
*******************
 | 
			
		||||
Scanners & Software
 | 
			
		||||
*******************
 | 
			
		||||
 | 
			
		||||
Paperless-ngx is compatible with many different scanners and scanning tools. A user-maintained list of scanners and other software is available on `the wiki <https://github.com/paperless-ngx/paperless-ngx/wiki/Scanner-&-Software-Recommendations>`_.
 | 
			
		||||
@ -1,63 +0,0 @@
 | 
			
		||||
.. _screenshots:
 | 
			
		||||
 | 
			
		||||
***********
 | 
			
		||||
Screenshots
 | 
			
		||||
***********
 | 
			
		||||
 | 
			
		||||
This is what Paperless-ngx looks like.
 | 
			
		||||
 | 
			
		||||
The dashboard shows customizable views on your document and allows document uploads:
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/dashboard.png
 | 
			
		||||
    :target: _static/screenshots/dashboard.png
 | 
			
		||||
 | 
			
		||||
The document list provides three different styles to scroll through your documents:
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/documents-table.png
 | 
			
		||||
    :target: _static/screenshots/documents-table.png
 | 
			
		||||
.. image:: _static/screenshots/documents-smallcards.png
 | 
			
		||||
    :target: _static/screenshots/documents-smallcards.png
 | 
			
		||||
.. image:: _static/screenshots/documents-largecards.png
 | 
			
		||||
    :target: _static/screenshots/documents-largecards.png
 | 
			
		||||
 | 
			
		||||
Paperless-ngx also supports "dark mode":
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/documents-smallcards-dark.png
 | 
			
		||||
    :target: _static/screenshots/documents-smallcards-dark.png
 | 
			
		||||
 | 
			
		||||
Extensive filtering mechanisms:
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/documents-filter.png
 | 
			
		||||
    :target: _static/screenshots/documents-filter.png
 | 
			
		||||
 | 
			
		||||
Bulk editing of document tags, correspondents, etc.:
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/bulk-edit.png
 | 
			
		||||
    :target: _static/screenshots/bulk-edit.png
 | 
			
		||||
 | 
			
		||||
Side-by-side editing of documents:
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/editing.png
 | 
			
		||||
    :target: _static/screenshots/editing.png
 | 
			
		||||
 | 
			
		||||
Tag editing. This looks about the same for correspondents and document types.
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/new-tag.png
 | 
			
		||||
    :target: _static/screenshots/new-tag.png
 | 
			
		||||
 | 
			
		||||
Searching provides auto complete and highlights the results.
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/search-preview.png
 | 
			
		||||
    :target: _static/screenshots/search-preview.png
 | 
			
		||||
.. image:: _static/screenshots/search-results.png
 | 
			
		||||
    :target: _static/screenshots/search-results.png
 | 
			
		||||
 | 
			
		||||
Fancy mail filters!
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/mail-rules-edited.png
 | 
			
		||||
    :target: _static/screenshots/mail-rules-edited.png
 | 
			
		||||
 | 
			
		||||
Mobile devices are supported.
 | 
			
		||||
 | 
			
		||||
.. image:: _static/screenshots/mobile.png
 | 
			
		||||
    :target: _static/screenshots/mobile.png
 | 
			
		||||
							
								
								
									
										852
									
								
								docs/setup.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,852 @@
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
You can go multiple routes to setup and run Paperless:
 | 
			
		||||
 | 
			
		||||
- [Use the easy install docker script](/setup#docker_script)
 | 
			
		||||
- [Pull the image from Docker Hub](/setup#docker_hub)
 | 
			
		||||
- [Build the Docker image yourself](/setup#docker_build)
 | 
			
		||||
- [Install Paperless directly on your system manually (bare metal)](/setup#bare_metal)
 | 
			
		||||
 | 
			
		||||
The Docker routes are quick & easy. These are the recommended routes.
 | 
			
		||||
This configures all the stuff from the above automatically so that it
 | 
			
		||||
just works and uses sensible defaults for all configuration options.
 | 
			
		||||
Here you find a cheat-sheet for docker beginners: [CLI
 | 
			
		||||
Basics](https://www.sehn.tech/refs/devops-with-docker/)
 | 
			
		||||
 | 
			
		||||
The bare metal route is complicated to setup but makes it easier should
 | 
			
		||||
you want to contribute some code back. You need to configure and run the
 | 
			
		||||
above mentioned components yourself.
 | 
			
		||||
 | 
			
		||||
### Docker using the Installation Script {#docker_script}
 | 
			
		||||
 | 
			
		||||
Paperless provides an interactive installation script. This script will
 | 
			
		||||
ask you for a couple configuration options, download and create the
 | 
			
		||||
necessary configuration files, pull the docker image, start paperless
 | 
			
		||||
and create your user account. This script essentially performs all the
 | 
			
		||||
steps described in `setup-docker_hub`{.interpreted-text role="ref"}
 | 
			
		||||
automatically.
 | 
			
		||||
 | 
			
		||||
1.  Make sure that docker and docker-compose are installed.
 | 
			
		||||
 | 
			
		||||
2.  Download and run the installation script:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
### From GHCR / Docker Hub {#docker_hub}
 | 
			
		||||
 | 
			
		||||
1.  Login with your user and create a folder in your home-directory
 | 
			
		||||
    [mkdir -v \~/paperless-ngx]{.title-ref} to have a place for your
 | 
			
		||||
    configuration files and consumption directory.
 | 
			
		||||
 | 
			
		||||
2.  Go to the [/docker/compose directory on the project
 | 
			
		||||
    page](https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose)
 | 
			
		||||
    and download one of the [docker-compose.\*.yml]{.title-ref} files,
 | 
			
		||||
    depending on which database backend you want to use. Rename this
 | 
			
		||||
    file to [docker-compose.yml]{.title-ref}. If you want to enable
 | 
			
		||||
    optional support for Office documents, download a file with
 | 
			
		||||
    [-tika]{.title-ref} in the file name. Download the
 | 
			
		||||
    `docker-compose.env` file and the `.env` file as well and store them
 | 
			
		||||
    in the same directory.
 | 
			
		||||
 | 
			
		||||
    !!! tip
 | 
			
		||||
 | 
			
		||||
        For new installations, it is recommended to use PostgreSQL as the
 | 
			
		||||
        database backend.
 | 
			
		||||
 | 
			
		||||
3.  Install [Docker](https://www.docker.com/) and
 | 
			
		||||
    [docker-compose](https://docs.docker.com/compose/install/).
 | 
			
		||||
 | 
			
		||||
    !!! warning
 | 
			
		||||
 | 
			
		||||
        If you want to use the included `docker-compose.*.yml` file, you
 | 
			
		||||
        need to have at least Docker version **17.09.0** and docker-compose
 | 
			
		||||
        version **1.17.0**. To check do: [docker-compose -v]{.title-ref} or
 | 
			
		||||
        [docker -v]{.title-ref}
 | 
			
		||||
 | 
			
		||||
        See the [Docker installation guide]() on how to install the current
 | 
			
		||||
        version of Docker for your operating system or Linux distribution of
 | 
			
		||||
        choice. To get the latest version of docker-compose, follow the
 | 
			
		||||
        [docker-compose installation guide]() if your package repository
 | 
			
		||||
        doesn't include it.
 | 
			
		||||
 | 
			
		||||
4.  Modify `docker-compose.yml` to your preferences. You may want to
 | 
			
		||||
    change the path to the consumption directory. Find the line that
 | 
			
		||||
    specifies where to mount the consumption directory:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    - ./consume:/usr/src/paperless/consume
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    Replace the part BEFORE the colon with a local directory of your
 | 
			
		||||
    choice:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    - /home/jonaswinkler/paperless-inbox:/usr/src/paperless/consume
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    Don't change the part after the colon or paperless wont find your
 | 
			
		||||
    documents.
 | 
			
		||||
 | 
			
		||||
    You may also need to change the default port that the webserver will
 | 
			
		||||
    use from the default (8000):
 | 
			
		||||
 | 
			
		||||
    > ```
 | 
			
		||||
    > ports:
 | 
			
		||||
    >   - 8000:8000
 | 
			
		||||
    > ```
 | 
			
		||||
 | 
			
		||||
    Replace the part BEFORE the colon with a port of your choice:
 | 
			
		||||
 | 
			
		||||
    > ```
 | 
			
		||||
    > ports:
 | 
			
		||||
    >   - 8010:8000
 | 
			
		||||
    > ```
 | 
			
		||||
 | 
			
		||||
    Don't change the part after the colon or edit other lines that
 | 
			
		||||
    refer to port 8000. Modifying the part before the colon will map
 | 
			
		||||
    requests on another port to the webserver running on the default
 | 
			
		||||
    port.
 | 
			
		||||
 | 
			
		||||
    **Rootless**
 | 
			
		||||
 | 
			
		||||
    If you want to run Paperless as a rootless container, you will need
 | 
			
		||||
    to do the following in your `docker-compose.yml`:
 | 
			
		||||
 | 
			
		||||
    - set the `user` running the container to map to the `paperless`
 | 
			
		||||
      user in the container. This value (`user_id` below), should be
 | 
			
		||||
      the same id that `USERMAP_UID` and `USERMAP_GID` are set to in
 | 
			
		||||
      the next step. See `USERMAP_UID` and `USERMAP_GID`
 | 
			
		||||
      [here](/configuration#docker).
 | 
			
		||||
 | 
			
		||||
    Your entry for Paperless should contain something like:
 | 
			
		||||
 | 
			
		||||
    > ```
 | 
			
		||||
    > webserver:
 | 
			
		||||
    >   image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
    >   user: <user_id>
 | 
			
		||||
    > ```
 | 
			
		||||
 | 
			
		||||
5.  Modify `docker-compose.env`, following the comments in the file. The
 | 
			
		||||
    most important change is to set `USERMAP_UID` and `USERMAP_GID` to
 | 
			
		||||
    the uid and gid of your user on the host system. Use `id -u` and
 | 
			
		||||
    `id -g` to get these.
 | 
			
		||||
 | 
			
		||||
    This ensures that both the docker container and you on the host
 | 
			
		||||
    machine have write access to the consumption directory. If your UID
 | 
			
		||||
    and GID on the host system is 1000 (the default for the first normal
 | 
			
		||||
    user on most systems), it will work out of the box without any
 | 
			
		||||
    modifications. [id "username"]{.title-ref} to check.
 | 
			
		||||
 | 
			
		||||
    !!! note
 | 
			
		||||
 | 
			
		||||
        You can copy any setting from the file `paperless.conf.example` and
 | 
			
		||||
        paste it here. Have a look at `configuration`{.interpreted-text
 | 
			
		||||
        role="ref"} to see what's available.
 | 
			
		||||
 | 
			
		||||
    !!! note
 | 
			
		||||
 | 
			
		||||
        You can utilize Docker secrets for some configuration settings by
 | 
			
		||||
        appending [\_FILE]{.title-ref} to some configuration values. This is
 | 
			
		||||
        supported currently only by:
 | 
			
		||||
 | 
			
		||||
        -   PAPERLESS_DBUSER
 | 
			
		||||
        -   PAPERLESS_DBPASS
 | 
			
		||||
        -   PAPERLESS_SECRET_KEY
 | 
			
		||||
        -   PAPERLESS_AUTO_LOGIN_USERNAME
 | 
			
		||||
        -   PAPERLESS_ADMIN_USER
 | 
			
		||||
        -   PAPERLESS_ADMIN_MAIL
 | 
			
		||||
        -   PAPERLESS_ADMIN_PASSWORD
 | 
			
		||||
 | 
			
		||||
    !!! warning
 | 
			
		||||
 | 
			
		||||
        Some file systems such as NFS network shares don't support file
 | 
			
		||||
        system notifications with `inotify`. When storing the consumption
 | 
			
		||||
        directory on such a file system, paperless will not pick up new
 | 
			
		||||
        files with the default configuration. You will need to use
 | 
			
		||||
        `PAPERLESS_CONSUMER_POLLING`, which will disable inotify. See
 | 
			
		||||
        [here](/configuration#polling).
 | 
			
		||||
 | 
			
		||||
6.  Run `docker-compose pull`, followed by `docker-compose up -d`. This
 | 
			
		||||
    will pull the image, create and start the necessary containers.
 | 
			
		||||
 | 
			
		||||
7.  To be able to login, you will need a super user. To create it,
 | 
			
		||||
    execute the following command:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ docker-compose run --rm webserver createsuperuser
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    This will prompt you to set a username, an optional e-mail address
 | 
			
		||||
    and finally a password (at least 8 characters).
 | 
			
		||||
 | 
			
		||||
8.  The default `docker-compose.yml` exports the webserver on your local
 | 
			
		||||
    port
 | 
			
		||||
 | 
			
		||||
    8000\. If you did not change this, you should now be able to visit
 | 
			
		||||
    your Paperless instance at `http://127.0.0.1:8000` or your servers
 | 
			
		||||
    IP-Address:8000. Use the login credentials you have created with the
 | 
			
		||||
    previous step.
 | 
			
		||||
 | 
			
		||||
### Build the Docker image yourself {#docker_build}
 | 
			
		||||
 | 
			
		||||
1.  Clone the entire repository of paperless:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    git clone https://github.com/paperless-ngx/paperless-ngx
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    The master branch always reflects the latest stable version.
 | 
			
		||||
 | 
			
		||||
2.  Copy one of the `docker/compose/docker-compose.*.yml` to
 | 
			
		||||
    `docker-compose.yml` in the root folder, depending on which database
 | 
			
		||||
    backend you want to use. Copy `docker-compose.env` into the project
 | 
			
		||||
    root as well.
 | 
			
		||||
 | 
			
		||||
3.  In the `docker-compose.yml` file, find the line that instructs
 | 
			
		||||
    docker-compose to pull the paperless image from Docker Hub:
 | 
			
		||||
 | 
			
		||||
    ```yaml
 | 
			
		||||
    webserver:
 | 
			
		||||
      image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    and replace it with a line that instructs docker-compose to build
 | 
			
		||||
    the image from the current working directory instead:
 | 
			
		||||
 | 
			
		||||
    ```yaml
 | 
			
		||||
    webserver:
 | 
			
		||||
      build:
 | 
			
		||||
        context: .
 | 
			
		||||
        args:
 | 
			
		||||
          QPDF_VERSION: x.y.x
 | 
			
		||||
          PIKEPDF_VERSION: x.y.z
 | 
			
		||||
          PSYCOPG2_VERSION: x.y.z
 | 
			
		||||
          JBIG2ENC_VERSION: 0.29
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    !!! note
 | 
			
		||||
 | 
			
		||||
        You should match the build argument versions to the version for the
 | 
			
		||||
        release you have checked out. These are pre-built images with
 | 
			
		||||
        certain, more updated software. If you want to build these images
 | 
			
		||||
        your self, that is possible, but beyond the scope of these steps.
 | 
			
		||||
 | 
			
		||||
4.  Follow steps 3 to 8 of `setup-docker_hub`{.interpreted-text
 | 
			
		||||
    role="ref"}. When asked to run `docker-compose pull` to pull the
 | 
			
		||||
    image, do
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ docker-compose build
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    instead to build the image.
 | 
			
		||||
 | 
			
		||||
### Bare Metal Route {#bare_metal}
 | 
			
		||||
 | 
			
		||||
Paperless runs on linux only. The following procedure has been tested on
 | 
			
		||||
a minimal installation of Debian/Buster, which is the current stable
 | 
			
		||||
release at the time of writing. Windows is not and will never be
 | 
			
		||||
supported.
 | 
			
		||||
 | 
			
		||||
1.  Install dependencies. Paperless requires the following packages.
 | 
			
		||||
 | 
			
		||||
    - `python3` 3.8, 3.9
 | 
			
		||||
    - `python3-pip`
 | 
			
		||||
    - `python3-dev`
 | 
			
		||||
    - `default-libmysqlclient-dev` for MariaDB
 | 
			
		||||
    - `fonts-liberation` for generating thumbnails for plain text
 | 
			
		||||
      files
 | 
			
		||||
    - `imagemagick` >= 6 for PDF conversion
 | 
			
		||||
    - `gnupg` for handling encrypted documents
 | 
			
		||||
    - `libpq-dev` for PostgreSQL
 | 
			
		||||
    - `libmagic-dev` for mime type detection
 | 
			
		||||
    - `mariadb-client` for MariaDB compile time
 | 
			
		||||
    - `mime-support` for mime type detection
 | 
			
		||||
    - `libzbar0` for barcode detection
 | 
			
		||||
    - `poppler-utils` for barcode detection
 | 
			
		||||
 | 
			
		||||
    Use this list for your preferred package management:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev libmagic-dev mime-support libzbar0 poppler-utils
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    These dependencies are required for OCRmyPDF, which is used for text
 | 
			
		||||
    recognition.
 | 
			
		||||
 | 
			
		||||
    - `unpaper`
 | 
			
		||||
    - `ghostscript`
 | 
			
		||||
    - `icc-profiles-free`
 | 
			
		||||
    - `qpdf`
 | 
			
		||||
    - `liblept5`
 | 
			
		||||
    - `libxml2`
 | 
			
		||||
    - `pngquant` (suggested for certain PDF image optimizations)
 | 
			
		||||
    - `zlib1g`
 | 
			
		||||
    - `tesseract-ocr` >= 4.0.0 for OCR
 | 
			
		||||
    - `tesseract-ocr` language packs (`tesseract-ocr-eng`,
 | 
			
		||||
      `tesseract-ocr-deu`, etc)
 | 
			
		||||
 | 
			
		||||
    Use this list for your preferred package management:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    unpaper ghostscript icc-profiles-free qpdf liblept5 libxml2 pngquant zlib1g tesseract-ocr
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    On Raspberry Pi, these libraries are required as well:
 | 
			
		||||
 | 
			
		||||
    - `libatlas-base-dev`
 | 
			
		||||
    - `libxslt1-dev`
 | 
			
		||||
 | 
			
		||||
    You will also need `build-essential`, `python3-setuptools` and
 | 
			
		||||
    `python3-wheel` for installing some of the python dependencies.
 | 
			
		||||
 | 
			
		||||
2.  Install `redis` >= 6.0 and configure it to start automatically.
 | 
			
		||||
 | 
			
		||||
3.  Optional. Install `postgresql` and configure a database, user and
 | 
			
		||||
    password for paperless. If you do not wish to use PostgreSQL,
 | 
			
		||||
    MariaDB and SQLite are available as well.
 | 
			
		||||
 | 
			
		||||
    !!! note
 | 
			
		||||
 | 
			
		||||
        On bare-metal installations using SQLite, ensure the [JSON1
 | 
			
		||||
        extension](https://code.djangoproject.com/wiki/JSON1Extension) is
 | 
			
		||||
        enabled. This is usually the case, but not always.
 | 
			
		||||
 | 
			
		||||
4.  Get the release archive from
 | 
			
		||||
    <https://github.com/paperless-ngx/paperless-ngx/releases>. If you
 | 
			
		||||
    clone the git repo as it is, you also have to compile the front end
 | 
			
		||||
    by yourself. Extract the archive to a place from where you wish to
 | 
			
		||||
    execute it, such as `/opt/paperless`.
 | 
			
		||||
 | 
			
		||||
5.  Configure paperless. See `configuration`{.interpreted-text
 | 
			
		||||
    role="ref"} for details. Edit the included `paperless.conf` and
 | 
			
		||||
    adjust the settings to your needs. Required settings for getting
 | 
			
		||||
    paperless running are:
 | 
			
		||||
 | 
			
		||||
    - `PAPERLESS_REDIS` should point to your redis server, such as
 | 
			
		||||
      <redis://localhost:6379>.
 | 
			
		||||
    - `PAPERLESS_DBENGINE` optional, and should be one of [postgres,
 | 
			
		||||
      mariadb, or sqlite]{.title-ref}
 | 
			
		||||
    - `PAPERLESS_DBHOST` should be the hostname on which your
 | 
			
		||||
      PostgreSQL server is running. Do not configure this to use
 | 
			
		||||
      SQLite instead. Also configure port, database name, user and
 | 
			
		||||
      password as necessary.
 | 
			
		||||
    - `PAPERLESS_CONSUMPTION_DIR` should point to a folder which
 | 
			
		||||
      paperless should watch for documents. You might want to have
 | 
			
		||||
      this somewhere else. Likewise, `PAPERLESS_DATA_DIR` and
 | 
			
		||||
      `PAPERLESS_MEDIA_ROOT` define where paperless stores its data.
 | 
			
		||||
      If you like, you can point both to the same directory.
 | 
			
		||||
    - `PAPERLESS_SECRET_KEY` should be a random sequence of
 | 
			
		||||
      characters. It's used for authentication. Failure to do so
 | 
			
		||||
      allows third parties to forge authentication credentials.
 | 
			
		||||
    - `PAPERLESS_URL` if you are behind a reverse proxy. This should
 | 
			
		||||
      point to your domain. Please see
 | 
			
		||||
      `configuration`{.interpreted-text role="ref"} for more
 | 
			
		||||
      information.
 | 
			
		||||
 | 
			
		||||
    Many more adjustments can be made to paperless, especially the OCR
 | 
			
		||||
    part. The following options are recommended for everyone:
 | 
			
		||||
 | 
			
		||||
    - Set `PAPERLESS_OCR_LANGUAGE` to the language most of your
 | 
			
		||||
      documents are written in.
 | 
			
		||||
    - Set `PAPERLESS_TIME_ZONE` to your local time zone.
 | 
			
		||||
 | 
			
		||||
6.  Create a system user under which you wish to run paperless.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    adduser paperless --system --home /opt/paperless --group
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
7.  Ensure that these directories exist and that the paperless user has
 | 
			
		||||
    write permissions to the following directories:
 | 
			
		||||
 | 
			
		||||
    - `/opt/paperless/media`
 | 
			
		||||
    - `/opt/paperless/data`
 | 
			
		||||
    - `/opt/paperless/consume`
 | 
			
		||||
 | 
			
		||||
    Adjust as necessary if you configured different folders.
 | 
			
		||||
 | 
			
		||||
8.  Install python requirements from the `requirements.txt` file. It is
 | 
			
		||||
    up to you if you wish to use a virtual environment or not. First you
 | 
			
		||||
    should update your pip, so it gets the actual packages.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    sudo -Hu paperless pip3 install --upgrade pip
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    sudo -Hu paperless pip3 install -r requirements.txt
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    This will install all python dependencies in the home directory of
 | 
			
		||||
    the new paperless user.
 | 
			
		||||
 | 
			
		||||
9.  Go to `/opt/paperless/src`, and execute the following commands:
 | 
			
		||||
 | 
			
		||||
    ```bash
 | 
			
		||||
    \# This creates the database schema.
 | 
			
		||||
    sudo -Hu paperless python3 manage.py migrate
 | 
			
		||||
 | 
			
		||||
    \# This creates your first paperless user
 | 
			
		||||
    sudo -Hu paperless python3 manage.py createsuperuser
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
10. Optional: Test that paperless is working by executing
 | 
			
		||||
 | 
			
		||||
    ```bash
 | 
			
		||||
    \# This collects static files from paperless and django.
 | 
			
		||||
    sudo -Hu paperless python3 manage.py runserver
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    and pointing your browser to <http://localhost:8000/>.
 | 
			
		||||
 | 
			
		||||
    !!! warning
 | 
			
		||||
 | 
			
		||||
        This is a development server which should not be used in production.
 | 
			
		||||
        It is not audited for security and performance is inferior to
 | 
			
		||||
        production ready web servers.
 | 
			
		||||
 | 
			
		||||
    !!! tip
 | 
			
		||||
 | 
			
		||||
        This will not start the consumer. Paperless does this in a separate
 | 
			
		||||
        process.
 | 
			
		||||
 | 
			
		||||
11. Setup systemd services to run paperless automatically. You may use
 | 
			
		||||
    the service definition files included in the `scripts` folder as a
 | 
			
		||||
    starting point.
 | 
			
		||||
 | 
			
		||||
    Paperless needs the `webserver` script to run the webserver, the
 | 
			
		||||
    `consumer` script to watch the input folder, `taskqueue` for the
 | 
			
		||||
    background workers used to handle things like document consumption
 | 
			
		||||
    and the `scheduler` script to run tasks such as email checking at
 | 
			
		||||
    certain times .
 | 
			
		||||
 | 
			
		||||
    !!! note
 | 
			
		||||
 | 
			
		||||
        The `socket` script enables `gunicorn` to run on port 80 without
 | 
			
		||||
        root privileges. For this you need to uncomment the
 | 
			
		||||
        `Require=paperless-webserver.socket` in the `webserver` script
 | 
			
		||||
        and configure `gunicorn` to listen on port 80 (see
 | 
			
		||||
        `paperless/gunicorn.conf.py`).
 | 
			
		||||
 | 
			
		||||
    You may need to adjust the path to the `gunicorn` executable. This
 | 
			
		||||
    will be installed as part of the python dependencies, and is either
 | 
			
		||||
    located in the `bin` folder of your virtual environment, or in
 | 
			
		||||
    `~/.local/bin/` if no virtual environment is used.
 | 
			
		||||
 | 
			
		||||
    These services rely on redis and optionally the database server, but
 | 
			
		||||
    don't need to be started in any particular order. The example files
 | 
			
		||||
    depend on redis being started. If you use a database server, you
 | 
			
		||||
    should add additional dependencies.
 | 
			
		||||
 | 
			
		||||
    !!! warning
 | 
			
		||||
 | 
			
		||||
        The included scripts run a `gunicorn` standalone server, which is
 | 
			
		||||
        fine for running paperless. It does support SSL, however, the
 | 
			
		||||
        documentation of GUnicorn states that you should use a proxy server
 | 
			
		||||
        in front of gunicorn instead.
 | 
			
		||||
 | 
			
		||||
        For instructions on how to use nginx for that,
 | 
			
		||||
        [see the instructions below](/setup#nginx).
 | 
			
		||||
 | 
			
		||||
12. Optional: Install a samba server and make the consumption folder
 | 
			
		||||
    available as a network share.
 | 
			
		||||
 | 
			
		||||
13. Configure ImageMagick to allow processing of PDF documents. Most
 | 
			
		||||
    distributions have this disabled by default, since PDF documents can
 | 
			
		||||
    contain malware. If you don't do this, paperless will fall back to
 | 
			
		||||
    ghostscript for certain steps such as thumbnail generation.
 | 
			
		||||
 | 
			
		||||
    Edit `/etc/ImageMagick-6/policy.xml` and adjust
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    <policy domain="coder" rights="none" pattern="PDF" />
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    to
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    <policy domain="coder" rights="read|write" pattern="PDF" />
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
14. Optional: Install the
 | 
			
		||||
    [jbig2enc](https://ocrmypdf.readthedocs.io/en/latest/jbig2.html)
 | 
			
		||||
    encoder. This will reduce the size of generated PDF documents.
 | 
			
		||||
    You'll most likely need to compile this by yourself, because this
 | 
			
		||||
    software has been patented until around 2017 and binary packages are
 | 
			
		||||
    not available for most distributions.
 | 
			
		||||
 | 
			
		||||
15. Optional: If using the NLTK machine learning processing (see
 | 
			
		||||
    `PAPERLESS_ENABLE_NLTK` in `configuration`{.interpreted-text
 | 
			
		||||
    role="ref"} for details), download the NLTK data for the Snowball
 | 
			
		||||
    Stemmer, Stopwords and Punkt tokenizer to your
 | 
			
		||||
    `PAPERLESS_DATA_DIR/nltk`. Refer to the [NLTK
 | 
			
		||||
    instructions](https://www.nltk.org/data.html) for details on how to
 | 
			
		||||
    download the data.
 | 
			
		||||
 | 
			
		||||
# Migrating to Paperless-ngx
 | 
			
		||||
 | 
			
		||||
Migration is possible both from Paperless-ng or directly from the
 | 
			
		||||
'original' Paperless.
 | 
			
		||||
 | 
			
		||||
## Migrating from Paperless-ng
 | 
			
		||||
 | 
			
		||||
Paperless-ngx is meant to be a drop-in replacement for Paperless-ng and
 | 
			
		||||
thus upgrading should be trivial for most users, especially when using
 | 
			
		||||
docker. However, as with any major change, it is recommended to take a
 | 
			
		||||
full backup first. Once you are ready, simply change the docker image to
 | 
			
		||||
point to the new source. E.g. if using Docker Compose, edit
 | 
			
		||||
`docker-compose.yml` and change:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
image: jonaswinkler/paperless-ng:latest
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
to
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and then run `docker-compose up -d` which will pull the new image
 | 
			
		||||
recreate the container. That's it!
 | 
			
		||||
 | 
			
		||||
Users who installed with the bare-metal route should also update their
 | 
			
		||||
Git clone to point to `https://github.com/paperless-ngx/paperless-ngx`,
 | 
			
		||||
e.g. using the command
 | 
			
		||||
`git remote set-url origin https://github.com/paperless-ngx/paperless-ngx`
 | 
			
		||||
and then pull the lastest version.
 | 
			
		||||
 | 
			
		||||
## Migrating from Paperless
 | 
			
		||||
 | 
			
		||||
At its core, paperless-ngx is still paperless and fully compatible.
 | 
			
		||||
However, some things have changed under the hood, so you need to adapt
 | 
			
		||||
your setup depending on how you installed paperless.
 | 
			
		||||
 | 
			
		||||
This setup describes how to update an existing paperless Docker
 | 
			
		||||
installation. The important things to keep in mind are as follows:
 | 
			
		||||
 | 
			
		||||
- Read the [changelog](/changelog) and
 | 
			
		||||
  take note of breaking changes.
 | 
			
		||||
- You should decide if you want to stick with SQLite or want to
 | 
			
		||||
  migrate your database to PostgreSQL. See
 | 
			
		||||
  `setup-sqlite_to_psql`{.interpreted-text role="ref"} for details on
 | 
			
		||||
  how to move your data from SQLite to PostgreSQL. Both work fine with
 | 
			
		||||
  paperless. However, if you already have a database server running
 | 
			
		||||
  for other services, you might as well use it for paperless as well.
 | 
			
		||||
- The task scheduler of paperless, which is used to execute periodic
 | 
			
		||||
  tasks such as email checking and maintenance, requires a
 | 
			
		||||
  [redis](https://redis.io/) message broker instance. The
 | 
			
		||||
  docker-compose route takes care of that.
 | 
			
		||||
- The layout of the folder structure for your documents and data
 | 
			
		||||
  remains the same, so you can just plug your old docker volumes into
 | 
			
		||||
  paperless-ngx and expect it to find everything where it should be.
 | 
			
		||||
 | 
			
		||||
Migration to paperless-ngx is then performed in a few simple steps:
 | 
			
		||||
 | 
			
		||||
1.  Stop paperless.
 | 
			
		||||
 | 
			
		||||
    ```bash
 | 
			
		||||
    $ cd /path/to/current/paperless
 | 
			
		||||
    $ docker-compose down
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
2.  Do a backup for two purposes: If something goes wrong, you still
 | 
			
		||||
    have your data. Second, if you don't like paperless-ngx, you can
 | 
			
		||||
    switch back to paperless.
 | 
			
		||||
 | 
			
		||||
3.  Download the latest release of paperless-ngx. You can either go with
 | 
			
		||||
    the docker-compose files from
 | 
			
		||||
    [here](https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose)
 | 
			
		||||
    or clone the repository to build the image yourself (see
 | 
			
		||||
    [above](/setup#docker_build)). You can
 | 
			
		||||
    either replace your current paperless folder or put paperless-ngx in
 | 
			
		||||
    a different location.
 | 
			
		||||
 | 
			
		||||
    !!! warning
 | 
			
		||||
 | 
			
		||||
        Paperless-ngx includes a `.env` file. This will set the project name
 | 
			
		||||
        for docker compose to `paperless`, which will also define the name
 | 
			
		||||
        of the volumes by paperless-ngx. However, if you experience that
 | 
			
		||||
        paperless-ngx is not using your old paperless volumes, verify the
 | 
			
		||||
        names of your volumes with
 | 
			
		||||
 | 
			
		||||
        ``` shell-session
 | 
			
		||||
        $ docker volume ls | grep _data
 | 
			
		||||
        ```
 | 
			
		||||
 | 
			
		||||
        and adjust the project name in the `.env` file so that it matches
 | 
			
		||||
        the name of the volumes before the `_data` part.
 | 
			
		||||
 | 
			
		||||
4.  Download the `docker-compose.sqlite.yml` file to
 | 
			
		||||
    `docker-compose.yml`. If you want to switch to PostgreSQL, do that
 | 
			
		||||
    after you migrated your existing SQLite database.
 | 
			
		||||
 | 
			
		||||
5.  Adjust `docker-compose.yml` and `docker-compose.env` to your needs.
 | 
			
		||||
    See `setup-docker_hub`{.interpreted-text role="ref"} for details on
 | 
			
		||||
    which edits are advised.
 | 
			
		||||
 | 
			
		||||
6.  [Update paperless.](/administration#updating)
 | 
			
		||||
 | 
			
		||||
7.  In order to find your existing documents with the new search
 | 
			
		||||
    feature, you need to invoke a one-time operation that will create
 | 
			
		||||
    the search index:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ docker-compose run --rm webserver document_index reindex
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    This will migrate your database and create the search index. After
 | 
			
		||||
    that, paperless will take care of maintaining the index by itself.
 | 
			
		||||
 | 
			
		||||
8.  Start paperless-ngx.
 | 
			
		||||
 | 
			
		||||
    ```bash
 | 
			
		||||
    $ docker-compose up -d
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    This will run paperless in the background and automatically start it
 | 
			
		||||
    on system boot.
 | 
			
		||||
 | 
			
		||||
9.  Paperless installed a permanent redirect to `admin/` in your
 | 
			
		||||
    browser. This redirect is still in place and prevents access to the
 | 
			
		||||
    new UI. Clear your browsing cache in order to fix this.
 | 
			
		||||
 | 
			
		||||
10. Optionally, follow the instructions below to migrate your existing
 | 
			
		||||
    data to PostgreSQL.
 | 
			
		||||
 | 
			
		||||
## Migrating from LinuxServer.io Docker Image
 | 
			
		||||
 | 
			
		||||
As with any upgrades and large changes, it is highly recommended to
 | 
			
		||||
create a backup before starting. This assumes the image was running
 | 
			
		||||
using Docker Compose, but the instructions are translatable to Docker
 | 
			
		||||
commands as well.
 | 
			
		||||
 | 
			
		||||
1.  Stop and remove the paperless container
 | 
			
		||||
2.  If using an external database, stop the container
 | 
			
		||||
3.  Update Redis configuration
 | 
			
		||||
    a) If `REDIS_URL` is already set, change it to `PAPERLESS_REDIS`
 | 
			
		||||
    and continue to step 4.
 | 
			
		||||
    b) Otherwise, in the `docker-compose.yml` add a new service for
 | 
			
		||||
    Redis, following [the example compose
 | 
			
		||||
    files](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
 | 
			
		||||
    c) Set the environment variable `PAPERLESS_REDIS` so it points to
 | 
			
		||||
    the new Redis container
 | 
			
		||||
4.  Update user mapping
 | 
			
		||||
    a) If set, change the environment variable `PUID` to `USERMAP_UID`
 | 
			
		||||
    b) If set, change the environment variable `PGID` to `USERMAP_GID`
 | 
			
		||||
5.  Update configuration paths
 | 
			
		||||
    a) Set the environment variable `PAPERLESS_DATA_DIR` to `/config`
 | 
			
		||||
6.  Update media paths
 | 
			
		||||
    a) Set the environment variable `PAPERLESS_MEDIA_ROOT` to
 | 
			
		||||
    `/data/media`
 | 
			
		||||
7.  Update timezone
 | 
			
		||||
    a) Set the environment variable `PAPERLESS_TIME_ZONE` to the same
 | 
			
		||||
    value as `TZ`
 | 
			
		||||
8.  Modify the `image:` to point to
 | 
			
		||||
    `ghcr.io/paperless-ngx/paperless-ngx:latest` or a specific version
 | 
			
		||||
    if preferred.
 | 
			
		||||
9.  Start the containers as before, using `docker-compose`.
 | 
			
		||||
 | 
			
		||||
## Moving data from SQLite to PostgreSQL or MySQL/MariaDB {#sqlite_to_psql}
 | 
			
		||||
 | 
			
		||||
Moving your data from SQLite to PostgreSQL or MySQL/MariaDB is done via
 | 
			
		||||
executing a series of django management commands as below. The commands
 | 
			
		||||
below use PostgreSQL, but are applicable to MySQL/MariaDB with the
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    Make sure that your SQLite database is migrated to the latest version.
 | 
			
		||||
    Starting paperless will make sure that this is the case. If your try to
 | 
			
		||||
    load data from an old database schema in SQLite into a newer database
 | 
			
		||||
    schema in PostgreSQL, you will run into trouble.
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    On some database fields, PostgreSQL enforces predefined limits on
 | 
			
		||||
    maximum length, whereas SQLite does not. The fields in question are the
 | 
			
		||||
    title of documents (128 characters), names of document types, tags and
 | 
			
		||||
    correspondents (128 characters), and filenames (1024 characters). If you
 | 
			
		||||
    have data in these fields that surpasses these limits, migration to
 | 
			
		||||
    PostgreSQL is not possible and will fail with an error.
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    MySQL is case insensitive by default, treating values like "Name" and
 | 
			
		||||
    "NAME" as identical. See `advanced-mysql-caveats`{.interpreted-text
 | 
			
		||||
    role="ref"} for details.
 | 
			
		||||
 | 
			
		||||
1.  Stop paperless, if it is running.
 | 
			
		||||
 | 
			
		||||
2.  Tell paperless to use PostgreSQL:
 | 
			
		||||
 | 
			
		||||
    a) With docker, copy the provided `docker-compose.postgres.yml`
 | 
			
		||||
    file to `docker-compose.yml`. Remember to adjust the consumption
 | 
			
		||||
    directory, if necessary.
 | 
			
		||||
    b) Without docker, configure the database in your `paperless.conf`
 | 
			
		||||
    file. See `configuration`{.interpreted-text role="ref"} for
 | 
			
		||||
    details.
 | 
			
		||||
 | 
			
		||||
3.  Open a shell and initialize the database:
 | 
			
		||||
 | 
			
		||||
    a) With docker, run the following command to open a shell within
 | 
			
		||||
    the paperless container:
 | 
			
		||||
 | 
			
		||||
        ``` shell-session
 | 
			
		||||
        $ cd /path/to/paperless
 | 
			
		||||
        $ docker-compose run --rm webserver /bin/bash
 | 
			
		||||
        ```
 | 
			
		||||
 | 
			
		||||
        This will launch the container and initialize the PostgreSQL
 | 
			
		||||
        database.
 | 
			
		||||
 | 
			
		||||
    b) Without docker, remember to activate any virtual environment,
 | 
			
		||||
    switch to the `src` directory and create the database schema:
 | 
			
		||||
 | 
			
		||||
        ``` shell-session
 | 
			
		||||
        $ cd /path/to/paperless/src
 | 
			
		||||
        $ python3 manage.py migrate
 | 
			
		||||
        ```
 | 
			
		||||
 | 
			
		||||
        This will not copy any data yet.
 | 
			
		||||
 | 
			
		||||
4.  Dump your data from SQLite:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ python3 manage.py dumpdata --database=sqlite --exclude=contenttypes --exclude=auth.Permission > data.json
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
5.  Load your data into PostgreSQL:
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ python3 manage.py loaddata data.json
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
6.  If operating inside Docker, you may exit the shell now.
 | 
			
		||||
 | 
			
		||||
    ```shell-session
 | 
			
		||||
    $ exit
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
7.  Start paperless.
 | 
			
		||||
 | 
			
		||||
## Moving back to Paperless
 | 
			
		||||
 | 
			
		||||
Lets say you migrated to Paperless-ngx and used it for a while, but
 | 
			
		||||
decided that you don't like it and want to move back (If you do, send
 | 
			
		||||
me a mail about what part you didn't like!), you can totally do that
 | 
			
		||||
with a few simple steps.
 | 
			
		||||
 | 
			
		||||
Paperless-ngx modified the database schema slightly, however, these
 | 
			
		||||
changes can be reverted while keeping your current data, so that your
 | 
			
		||||
current data will be compatible with original Paperless.
 | 
			
		||||
 | 
			
		||||
Execute this:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ cd /path/to/paperless
 | 
			
		||||
$ docker-compose run --rm webserver migrate documents 0023
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Or without docker:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
$ cd /path/to/paperless/src
 | 
			
		||||
$ python3 manage.py migrate documents 0023
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After that, you need to clear your cookies (Paperless-ngx comes with
 | 
			
		||||
updated dependencies that do cookie-processing differently) and probably
 | 
			
		||||
your cache as well.
 | 
			
		||||
 | 
			
		||||
# Considerations for less powerful devices {#less_powerful_devices}
 | 
			
		||||
 | 
			
		||||
Paperless runs on Raspberry Pi. However, some things are rather slow on
 | 
			
		||||
the Pi and configuring some options in paperless can help improve
 | 
			
		||||
performance immensely:
 | 
			
		||||
 | 
			
		||||
- Stick with SQLite to save some resources.
 | 
			
		||||
- Consider setting `PAPERLESS_OCR_PAGES` to 1, so that paperless will
 | 
			
		||||
  only OCR the first page of your documents. In most cases, this page
 | 
			
		||||
  contains enough information to be able to find it.
 | 
			
		||||
- `PAPERLESS_TASK_WORKERS` and `PAPERLESS_THREADS_PER_WORKER` are
 | 
			
		||||
  configured to use all cores. The Raspberry Pi models 3 and up have 4
 | 
			
		||||
  cores, meaning that paperless will use 2 workers and 2 threads per
 | 
			
		||||
  worker. This may result in sluggish response times during
 | 
			
		||||
  consumption, so you might want to lower these settings (example: 2
 | 
			
		||||
  workers and 1 thread to always have some computing power left for
 | 
			
		||||
  other tasks).
 | 
			
		||||
- Keep `PAPERLESS_OCR_MODE` at its default value `skip` and consider
 | 
			
		||||
  OCR'ing your documents before feeding them into paperless. Some
 | 
			
		||||
  scanners are able to do this! You might want to even specify
 | 
			
		||||
  `skip_noarchive` to skip archive file generation for already ocr'ed
 | 
			
		||||
  documents entirely.
 | 
			
		||||
- If you want to perform OCR on the device, consider using
 | 
			
		||||
  `PAPERLESS_OCR_CLEAN=none`. This will speed up OCR times and use
 | 
			
		||||
  less memory at the expense of slightly worse OCR results.
 | 
			
		||||
- If using docker, consider setting `PAPERLESS_WEBSERVER_WORKERS` to
 | 
			
		||||
  1.  This will save some memory.
 | 
			
		||||
- Consider setting `PAPERLESS_ENABLE_NLTK` to false, to disable the
 | 
			
		||||
  more advanced language processing, which can take more memory and
 | 
			
		||||
  processing time.
 | 
			
		||||
 | 
			
		||||
For details, refer to `configuration`{.interpreted-text role="ref"}.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    Updating the
 | 
			
		||||
    [automatic matching algorithm](/advanced_usage#automatic_matching) takes quite a bit of time. However, the update mechanism
 | 
			
		||||
    checks if your data has changed before doing the heavy lifting. If you
 | 
			
		||||
    experience the algorithm taking too much cpu time, consider changing the
 | 
			
		||||
    schedule in the admin interface to daily. You can also manually invoke
 | 
			
		||||
    the task by changing the date and time of the next run to today/now.
 | 
			
		||||
 | 
			
		||||
    The actual matching of the algorithm is fast and works on Raspberry Pi
 | 
			
		||||
    as well as on any other device.
 | 
			
		||||
 | 
			
		||||
# Using nginx as a reverse proxy {#nginx}
 | 
			
		||||
 | 
			
		||||
If you want to expose paperless to the internet, you should hide it
 | 
			
		||||
behind a reverse proxy with SSL enabled.
 | 
			
		||||
 | 
			
		||||
In addition to the usual configuration for SSL, the following
 | 
			
		||||
configuration is required for paperless to operate:
 | 
			
		||||
 | 
			
		||||
```nginx
 | 
			
		||||
http {
 | 
			
		||||
 | 
			
		||||
    # Adjust as required. This is the maximum size for file uploads.
 | 
			
		||||
    # The default value 1M might be a little too small.
 | 
			
		||||
    client_max_body_size 10M;
 | 
			
		||||
 | 
			
		||||
    server {
 | 
			
		||||
 | 
			
		||||
        location / {
 | 
			
		||||
 | 
			
		||||
            # Adjust host and port as required.
 | 
			
		||||
            proxy_pass http://localhost:8000/;
 | 
			
		||||
 | 
			
		||||
            # These configuration options are required for WebSockets to work.
 | 
			
		||||
            proxy_http_version 1.1;
 | 
			
		||||
            proxy_set_header Upgrade $http_upgrade;
 | 
			
		||||
            proxy_set_header Connection "upgrade";
 | 
			
		||||
 | 
			
		||||
            proxy_redirect off;
 | 
			
		||||
            proxy_set_header Host $host;
 | 
			
		||||
            proxy_set_header X-Real-IP $remote_addr;
 | 
			
		||||
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 | 
			
		||||
            proxy_set_header X-Forwarded-Host $server_name;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `PAPERLESS_URL` configuration variable is also required when using a
 | 
			
		||||
reverse proxy. Please refer to the
 | 
			
		||||
`hosting-and-security`{.interpreted-text role="ref"} docs.
 | 
			
		||||
 | 
			
		||||
Also read
 | 
			
		||||
[this](https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu),
 | 
			
		||||
towards the end of the section.
 | 
			
		||||
							
								
								
									
										894
									
								
								docs/setup.rst
									
									
									
									
									
								
							
							
						
						@ -1,894 +0,0 @@
 | 
			
		||||
 | 
			
		||||
*****
 | 
			
		||||
Setup
 | 
			
		||||
*****
 | 
			
		||||
 | 
			
		||||
Overview of Paperless-ngx
 | 
			
		||||
#########################
 | 
			
		||||
 | 
			
		||||
Compared to paperless, paperless-ngx works a little different under the hood and has
 | 
			
		||||
more moving parts that work together. While this increases the complexity of
 | 
			
		||||
the system, it also brings many benefits.
 | 
			
		||||
 | 
			
		||||
Paperless consists of the following components:
 | 
			
		||||
 | 
			
		||||
*   **The webserver:** This is pretty much the same as in paperless. It serves
 | 
			
		||||
    the administration pages, the API, and the new frontend. This is the main
 | 
			
		||||
    tool you'll be using to interact with paperless. You may start the webserver
 | 
			
		||||
    with
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ cd /path/to/paperless/src/
 | 
			
		||||
        $ gunicorn -c ../gunicorn.conf.py paperless.wsgi
 | 
			
		||||
 | 
			
		||||
    or by any other means such as Apache ``mod_wsgi``.
 | 
			
		||||
 | 
			
		||||
*   **The consumer:** This is what watches your consumption folder for documents.
 | 
			
		||||
    However, the consumer itself does not really consume your documents.
 | 
			
		||||
    Now it notifies a task processor that a new file is ready for consumption.
 | 
			
		||||
    I suppose it should be named differently.
 | 
			
		||||
    This was also used to check your emails, but that's now done elsewhere as well.
 | 
			
		||||
 | 
			
		||||
    Start the consumer with the management command ``document_consumer``:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ cd /path/to/paperless/src/
 | 
			
		||||
        $ python3 manage.py document_consumer
 | 
			
		||||
 | 
			
		||||
    .. _setup-task_processor:
 | 
			
		||||
 | 
			
		||||
*   **The task processor:** Paperless relies on `Celery - Distributed Task Queue <https://docs.celeryq.dev/en/stable/index.html>`_
 | 
			
		||||
    for doing most of the heavy lifting. This is a task queue that accepts tasks from
 | 
			
		||||
    multiple sources and processes these in parallel. It also comes with a scheduler that executes
 | 
			
		||||
    certain commands periodically.
 | 
			
		||||
 | 
			
		||||
    This task processor is responsible for:
 | 
			
		||||
 | 
			
		||||
    *   Consuming documents. When the consumer finds new documents, it notifies the task processor to
 | 
			
		||||
        start a consumption task.
 | 
			
		||||
    *   The task processor also performs the consumption of any documents you upload through
 | 
			
		||||
        the web interface.
 | 
			
		||||
    *   Consuming emails. It periodically checks your configured accounts for new emails and
 | 
			
		||||
        notifies the task processor to consume the attachment of an email.
 | 
			
		||||
    *   Maintaining the search index and the automatic matching algorithm. These are things that paperless
 | 
			
		||||
        needs to do from time to time in order to operate properly.
 | 
			
		||||
 | 
			
		||||
    This allows paperless to process multiple documents from your consumption folder in parallel! On
 | 
			
		||||
    a modern multi core system, this makes the consumption process with full OCR blazingly fast.
 | 
			
		||||
 | 
			
		||||
    The task processor comes with a built-in admin interface that you can use to check whenever any of the
 | 
			
		||||
    tasks fail and inspect the errors (i.e., wrong email credentials, errors during consuming a specific
 | 
			
		||||
    file, etc).
 | 
			
		||||
 | 
			
		||||
*   A `redis <https://redis.io/>`_ message broker: This is a really lightweight service that is responsible
 | 
			
		||||
    for getting the tasks from the webserver and the consumer to the task scheduler. These run in a different
 | 
			
		||||
    process (maybe even on different machines!), and therefore, this is necessary.
 | 
			
		||||
 | 
			
		||||
*   Optional: A database server. Paperless supports PostgreSQL, MariaDB and SQLite for storing its data.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
############
 | 
			
		||||
 | 
			
		||||
You can go multiple routes to setup and run Paperless:
 | 
			
		||||
 | 
			
		||||
* :ref:`Use the easy install docker script <setup-docker_script>`
 | 
			
		||||
* :ref:`Pull the image from Docker Hub <setup-docker_hub>`
 | 
			
		||||
* :ref:`Build the Docker image yourself <setup-docker_build>`
 | 
			
		||||
* :ref:`Install Paperless directly on your system manually (bare metal) <setup-bare_metal>`
 | 
			
		||||
 | 
			
		||||
The Docker routes are quick & easy. These are the recommended routes. This configures all the stuff
 | 
			
		||||
from the above automatically so that it just works and uses sensible defaults for all configuration options.
 | 
			
		||||
Here you find a cheat-sheet for docker beginners: `CLI Basics <https://www.sehn.tech/refs/devops-with-docker/>`_
 | 
			
		||||
 | 
			
		||||
The bare metal route is complicated to setup but makes it easier
 | 
			
		||||
should you want to contribute some code back. You need to configure and
 | 
			
		||||
run the above mentioned components yourself.
 | 
			
		||||
 | 
			
		||||
.. _CLI Basics: https://www.sehn.tech/refs/devops-with-docker/
 | 
			
		||||
 | 
			
		||||
.. _setup-docker_script:
 | 
			
		||||
 | 
			
		||||
Install Paperless from Docker Hub using the installation script
 | 
			
		||||
===============================================================
 | 
			
		||||
 | 
			
		||||
Paperless provides an interactive installation script. This script will ask you
 | 
			
		||||
for a couple configuration options, download and create the necessary configuration files, pull the docker image, start paperless and create your user account. This script essentially
 | 
			
		||||
performs all the steps described in :ref:`setup-docker_hub` automatically.
 | 
			
		||||
 | 
			
		||||
1.  Make sure that docker and docker-compose are installed.
 | 
			
		||||
2.  Download and run the installation script:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
 | 
			
		||||
 | 
			
		||||
.. _setup-docker_hub:
 | 
			
		||||
 | 
			
		||||
Install Paperless from Docker Hub
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
1.  Login with your user and create a folder in your home-directory `mkdir -v ~/paperless-ngx` to have a place for your configuration files and consumption directory.
 | 
			
		||||
 | 
			
		||||
2.  Go to the `/docker/compose directory on the project page <https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose>`_
 | 
			
		||||
    and download one of the `docker-compose.*.yml` files, depending on which database backend you
 | 
			
		||||
    want to use. Rename this file to `docker-compose.yml`.
 | 
			
		||||
    If you want to enable optional support for Office documents, download a file with `-tika` in the file name.
 | 
			
		||||
    Download the ``docker-compose.env`` file and the ``.env`` file as well and store them
 | 
			
		||||
    in the same directory.
 | 
			
		||||
 | 
			
		||||
    .. hint::
 | 
			
		||||
 | 
			
		||||
        For new installations, it is recommended to use PostgreSQL as the database
 | 
			
		||||
        backend.
 | 
			
		||||
 | 
			
		||||
3.  Install `Docker`_ and `docker-compose`_.
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        If you want to use the included ``docker-compose.*.yml`` file, you
 | 
			
		||||
        need to have at least Docker version **17.09.0** and docker-compose
 | 
			
		||||
        version **1.17.0**.
 | 
			
		||||
        To check do: `docker-compose -v` or `docker -v`
 | 
			
		||||
 | 
			
		||||
        See the `Docker installation guide`_ on how to install the current
 | 
			
		||||
        version of Docker for your operating system or Linux distribution of
 | 
			
		||||
        choice. To get the latest version of docker-compose, follow the
 | 
			
		||||
        `docker-compose installation guide`_ if your package repository doesn't
 | 
			
		||||
        include it.
 | 
			
		||||
 | 
			
		||||
        .. _Docker installation guide: https://docs.docker.com/engine/installation/
 | 
			
		||||
        .. _docker-compose installation guide: https://docs.docker.com/compose/install/
 | 
			
		||||
 | 
			
		||||
4.  Modify ``docker-compose.yml`` to your preferences. You may want to change the path
 | 
			
		||||
    to the consumption directory. Find the line that specifies where
 | 
			
		||||
    to mount the consumption directory:
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        - ./consume:/usr/src/paperless/consume
 | 
			
		||||
 | 
			
		||||
    Replace the part BEFORE the colon with a local directory of your choice:
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        - /home/jonaswinkler/paperless-inbox:/usr/src/paperless/consume
 | 
			
		||||
 | 
			
		||||
    Don't change the part after the colon or paperless wont find your documents.
 | 
			
		||||
 | 
			
		||||
    You may also need to change the default port that the webserver will use
 | 
			
		||||
    from the default (8000):
 | 
			
		||||
 | 
			
		||||
     .. code::
 | 
			
		||||
 | 
			
		||||
        ports:
 | 
			
		||||
          - 8000:8000
 | 
			
		||||
 | 
			
		||||
    Replace the part BEFORE the colon with a port of your choice:
 | 
			
		||||
 | 
			
		||||
     .. code::
 | 
			
		||||
 | 
			
		||||
        ports:
 | 
			
		||||
          - 8010:8000
 | 
			
		||||
 | 
			
		||||
    Don't change the part after the colon or edit other lines that refer to
 | 
			
		||||
    port 8000. Modifying the part before the colon will map requests on another
 | 
			
		||||
    port to the webserver running on the default port.
 | 
			
		||||
 | 
			
		||||
    **Rootless**
 | 
			
		||||
 | 
			
		||||
    If you want to run Paperless as a rootless container, you will need to do the
 | 
			
		||||
    following in your ``docker-compose.yml``:
 | 
			
		||||
 | 
			
		||||
    - set the ``user`` running the container to map to the ``paperless`` user in the
 | 
			
		||||
      container.
 | 
			
		||||
      This value (``user_id`` below), should be the same id that ``USERMAP_UID`` and
 | 
			
		||||
      ``USERMAP_GID`` are set to in the next step.
 | 
			
		||||
      See ``USERMAP_UID`` and ``USERMAP_GID`` :ref:`here <configuration-docker>`.
 | 
			
		||||
 | 
			
		||||
    Your entry for Paperless should contain something like:
 | 
			
		||||
 | 
			
		||||
     .. code::
 | 
			
		||||
 | 
			
		||||
        webserver:
 | 
			
		||||
          image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
          user: <user_id>
 | 
			
		||||
 | 
			
		||||
5.  Modify ``docker-compose.env``, following the comments in the file. The
 | 
			
		||||
    most important change is to set ``USERMAP_UID`` and ``USERMAP_GID``
 | 
			
		||||
    to the uid and gid of your user on the host system. Use ``id -u`` and
 | 
			
		||||
    ``id -g`` to get these.
 | 
			
		||||
 | 
			
		||||
    This ensures that
 | 
			
		||||
    both the docker container and you on the host machine have write access
 | 
			
		||||
    to the consumption directory. If your UID and GID on the host system is
 | 
			
		||||
    1000 (the default for the first normal user on most systems), it will
 | 
			
		||||
    work out of the box without any modifications. `id "username"` to check.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        You can copy any setting from the file ``paperless.conf.example`` and paste it here.
 | 
			
		||||
        Have a look at :ref:`configuration` to see what's available.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        You can utilize Docker secrets for some configuration settings by
 | 
			
		||||
        appending `_FILE` to some configuration values.  This is supported currently
 | 
			
		||||
        only by:
 | 
			
		||||
 | 
			
		||||
          * PAPERLESS_DBUSER
 | 
			
		||||
          * PAPERLESS_DBPASS
 | 
			
		||||
          * PAPERLESS_SECRET_KEY
 | 
			
		||||
          * PAPERLESS_AUTO_LOGIN_USERNAME
 | 
			
		||||
          * PAPERLESS_ADMIN_USER
 | 
			
		||||
          * PAPERLESS_ADMIN_MAIL
 | 
			
		||||
          * PAPERLESS_ADMIN_PASSWORD
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        Some file systems such as NFS network shares don't support file system
 | 
			
		||||
        notifications with ``inotify``. When storing the consumption directory
 | 
			
		||||
        on such a file system, paperless will not pick up new files
 | 
			
		||||
        with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``,
 | 
			
		||||
        which will disable inotify. See :ref:`here <configuration-polling>`.
 | 
			
		||||
 | 
			
		||||
6.  Run ``docker-compose pull``, followed by ``docker-compose up -d``.
 | 
			
		||||
    This will pull the image, create and start the necessary containers.
 | 
			
		||||
 | 
			
		||||
7.  To be able to login, you will need a super user. To create it, execute the
 | 
			
		||||
    following command:
 | 
			
		||||
 | 
			
		||||
    .. code-block:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ docker-compose run --rm webserver createsuperuser
 | 
			
		||||
 | 
			
		||||
    This will prompt you to set a username, an optional e-mail address and
 | 
			
		||||
    finally a password (at least 8 characters).
 | 
			
		||||
 | 
			
		||||
8.  The default ``docker-compose.yml`` exports the webserver on your local port
 | 
			
		||||
    8000. If you did not change this, you should now be able to visit your
 | 
			
		||||
    Paperless instance at ``http://127.0.0.1:8000`` or your servers IP-Address:8000.
 | 
			
		||||
    Use the login credentials you have created with the previous step.
 | 
			
		||||
 | 
			
		||||
.. _Docker: https://www.docker.com/
 | 
			
		||||
.. _docker-compose: https://docs.docker.com/compose/install/
 | 
			
		||||
 | 
			
		||||
.. _setup-docker_build:
 | 
			
		||||
 | 
			
		||||
Build the Docker image yourself
 | 
			
		||||
===============================
 | 
			
		||||
 | 
			
		||||
1.  Clone the entire repository of paperless:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        git clone https://github.com/paperless-ngx/paperless-ngx
 | 
			
		||||
 | 
			
		||||
    The master branch always reflects the latest stable version.
 | 
			
		||||
 | 
			
		||||
2.  Copy one of the ``docker/compose/docker-compose.*.yml`` to ``docker-compose.yml`` in the root folder,
 | 
			
		||||
    depending on which database backend you want to use. Copy
 | 
			
		||||
    ``docker-compose.env`` into the project root as well.
 | 
			
		||||
 | 
			
		||||
3.  In the ``docker-compose.yml`` file, find the line that instructs docker-compose to pull the paperless image from Docker Hub:
 | 
			
		||||
 | 
			
		||||
    .. code:: yaml
 | 
			
		||||
 | 
			
		||||
        webserver:
 | 
			
		||||
            image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
 | 
			
		||||
    and replace it with a line that instructs docker-compose to build the image from the current working directory instead:
 | 
			
		||||
 | 
			
		||||
    .. code:: yaml
 | 
			
		||||
 | 
			
		||||
        webserver:
 | 
			
		||||
            build:
 | 
			
		||||
              context: .
 | 
			
		||||
              args:
 | 
			
		||||
                QPDF_VERSION: x.y.x
 | 
			
		||||
                PIKEPDF_VERSION: x.y.z
 | 
			
		||||
                PSYCOPG2_VERSION: x.y.z
 | 
			
		||||
                JBIG2ENC_VERSION: 0.29
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        You should match the build argument versions to the version for the release you have
 | 
			
		||||
        checked out.  These are pre-built images with certain, more updated software.
 | 
			
		||||
        If you want to build these images your self, that is possible, but beyond
 | 
			
		||||
        the scope of these steps.
 | 
			
		||||
 | 
			
		||||
4.  Follow steps 3 to 8 of :ref:`setup-docker_hub`. When asked to run
 | 
			
		||||
    ``docker-compose pull`` to pull the image, do
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ docker-compose build
 | 
			
		||||
 | 
			
		||||
    instead to build the image.
 | 
			
		||||
 | 
			
		||||
.. _setup-bare_metal:
 | 
			
		||||
 | 
			
		||||
Bare Metal Route
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
Paperless runs on linux only. The following procedure has been tested on a minimal
 | 
			
		||||
installation of Debian/Buster, which is the current stable release at the time of
 | 
			
		||||
writing. Windows is not and will never be supported.
 | 
			
		||||
 | 
			
		||||
1.  Install dependencies. Paperless requires the following packages.
 | 
			
		||||
 | 
			
		||||
    *   ``python3`` 3.8, 3.9
 | 
			
		||||
    *   ``python3-pip``
 | 
			
		||||
    *   ``python3-dev``
 | 
			
		||||
 | 
			
		||||
    *   ``default-libmysqlclient-dev`` for MariaDB
 | 
			
		||||
    *   ``fonts-liberation`` for generating thumbnails for plain text files
 | 
			
		||||
    *   ``imagemagick`` >= 6 for PDF conversion
 | 
			
		||||
    *   ``gnupg`` for handling encrypted documents
 | 
			
		||||
    *   ``libpq-dev`` for PostgreSQL
 | 
			
		||||
    *   ``libmagic-dev`` for mime type detection
 | 
			
		||||
    *   ``mariadb-client`` for MariaDB compile time
 | 
			
		||||
    *   ``mime-support`` for mime type detection
 | 
			
		||||
    *   ``libzbar0`` for barcode detection
 | 
			
		||||
    *   ``poppler-utils`` for barcode detection
 | 
			
		||||
 | 
			
		||||
    Use this list for your preferred package management:
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev libmagic-dev mime-support libzbar0 poppler-utils
 | 
			
		||||
 | 
			
		||||
    These dependencies are required for OCRmyPDF, which is used for text recognition.
 | 
			
		||||
 | 
			
		||||
    *   ``unpaper``
 | 
			
		||||
    *   ``ghostscript``
 | 
			
		||||
    *   ``icc-profiles-free``
 | 
			
		||||
    *   ``qpdf``
 | 
			
		||||
    *   ``liblept5``
 | 
			
		||||
    *   ``libxml2``
 | 
			
		||||
    *   ``pngquant`` (suggested for certain PDF image optimizations)
 | 
			
		||||
    *   ``zlib1g``
 | 
			
		||||
    *   ``tesseract-ocr`` >= 4.0.0 for OCR
 | 
			
		||||
    *   ``tesseract-ocr`` language packs (``tesseract-ocr-eng``, ``tesseract-ocr-deu``, etc)
 | 
			
		||||
 | 
			
		||||
    Use this list for your preferred package management:
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        unpaper ghostscript icc-profiles-free qpdf liblept5 libxml2 pngquant zlib1g tesseract-ocr
 | 
			
		||||
 | 
			
		||||
    On Raspberry Pi, these libraries are required as well:
 | 
			
		||||
 | 
			
		||||
    *   ``libatlas-base-dev``
 | 
			
		||||
    *   ``libxslt1-dev``
 | 
			
		||||
 | 
			
		||||
    You will also need ``build-essential``, ``python3-setuptools`` and ``python3-wheel``
 | 
			
		||||
    for installing some of the python dependencies.
 | 
			
		||||
 | 
			
		||||
2.  Install ``redis`` >= 6.0 and configure it to start automatically.
 | 
			
		||||
 | 
			
		||||
3.  Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish
 | 
			
		||||
    to use PostgreSQL, MariaDB and SQLite are available as well.
 | 
			
		||||
 | 
			
		||||
    .. note::
 | 
			
		||||
 | 
			
		||||
        On bare-metal installations using SQLite, ensure the
 | 
			
		||||
        `JSON1 extension <https://code.djangoproject.com/wiki/JSON1Extension>`_ is enabled. This is
 | 
			
		||||
        usually the case, but not always.
 | 
			
		||||
 | 
			
		||||
4.  Get the release archive from `<https://github.com/paperless-ngx/paperless-ngx/releases>`_.
 | 
			
		||||
    If you clone the git repo as it is, you also have to compile the front end by yourself.
 | 
			
		||||
    Extract the archive to a place from where you wish to execute it, such as ``/opt/paperless``.
 | 
			
		||||
 | 
			
		||||
5.  Configure paperless. See :ref:`configuration` for details. Edit the included ``paperless.conf`` and adjust the
 | 
			
		||||
    settings to your needs. Required settings for getting paperless running are:
 | 
			
		||||
 | 
			
		||||
    *   ``PAPERLESS_REDIS`` should point to your redis server, such as redis://localhost:6379.
 | 
			
		||||
    *   ``PAPERLESS_DBENGINE`` optional, and should be one of `postgres, mariadb, or sqlite`
 | 
			
		||||
    *   ``PAPERLESS_DBHOST`` should be the hostname on which your PostgreSQL server is running. Do not configure this
 | 
			
		||||
        to use SQLite instead. Also configure port, database name, user and password as necessary.
 | 
			
		||||
    *   ``PAPERLESS_CONSUMPTION_DIR`` should point to a folder which paperless should watch for documents. You might
 | 
			
		||||
        want to have this somewhere else. Likewise, ``PAPERLESS_DATA_DIR`` and ``PAPERLESS_MEDIA_ROOT`` define where
 | 
			
		||||
        paperless stores its data. If you like, you can point both to the same directory.
 | 
			
		||||
    *   ``PAPERLESS_SECRET_KEY`` should be a random sequence of characters. It's used for authentication. Failure
 | 
			
		||||
        to do so allows third parties to forge authentication credentials.
 | 
			
		||||
    *   ``PAPERLESS_URL`` if you are behind a reverse proxy. This should point to your domain. Please see
 | 
			
		||||
        :ref:`configuration` for more information.
 | 
			
		||||
 | 
			
		||||
    Many more adjustments can be made to paperless, especially the OCR part. The following options are recommended
 | 
			
		||||
    for everyone:
 | 
			
		||||
 | 
			
		||||
    *   Set ``PAPERLESS_OCR_LANGUAGE`` to the language most of your documents are written in.
 | 
			
		||||
    *   Set ``PAPERLESS_TIME_ZONE`` to your local time zone.
 | 
			
		||||
 | 
			
		||||
6.  Create a system user under which you wish to run paperless.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        adduser paperless --system --home /opt/paperless --group
 | 
			
		||||
 | 
			
		||||
7.  Ensure that these directories exist
 | 
			
		||||
    and that the paperless user has write permissions to the following directories:
 | 
			
		||||
 | 
			
		||||
    *   ``/opt/paperless/media``
 | 
			
		||||
    *   ``/opt/paperless/data``
 | 
			
		||||
    *   ``/opt/paperless/consume``
 | 
			
		||||
 | 
			
		||||
    Adjust as necessary if you configured different folders.
 | 
			
		||||
 | 
			
		||||
8.  Install python requirements from the ``requirements.txt`` file.
 | 
			
		||||
    It is up to you if you wish to use a virtual environment or not. First you should update your pip, so it gets the actual packages.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        sudo -Hu paperless pip3 install --upgrade pip
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        sudo -Hu paperless pip3 install -r requirements.txt
 | 
			
		||||
 | 
			
		||||
    This will install all python dependencies in the home directory of
 | 
			
		||||
    the new paperless user.
 | 
			
		||||
 | 
			
		||||
9.  Go to ``/opt/paperless/src``, and execute the following commands:
 | 
			
		||||
 | 
			
		||||
    .. code:: bash
 | 
			
		||||
 | 
			
		||||
        # This creates the database schema.
 | 
			
		||||
        sudo -Hu paperless python3 manage.py migrate
 | 
			
		||||
 | 
			
		||||
        # This creates your first paperless user
 | 
			
		||||
        sudo -Hu paperless python3 manage.py createsuperuser
 | 
			
		||||
 | 
			
		||||
10. Optional: Test that paperless is working by executing
 | 
			
		||||
 | 
			
		||||
      .. code:: bash
 | 
			
		||||
 | 
			
		||||
        # This collects static files from paperless and django.
 | 
			
		||||
        sudo -Hu paperless python3 manage.py runserver
 | 
			
		||||
 | 
			
		||||
    and pointing your browser to http://localhost:8000/.
 | 
			
		||||
 | 
			
		||||
    .. warning::
 | 
			
		||||
 | 
			
		||||
        This is a development server which should not be used in
 | 
			
		||||
        production. It is not audited for security and performance
 | 
			
		||||
        is inferior to production ready web servers.
 | 
			
		||||
 | 
			
		||||
    .. hint::
 | 
			
		||||
 | 
			
		||||
        This will not start the consumer. Paperless does this in a
 | 
			
		||||
        separate process.
 | 
			
		||||
 | 
			
		||||
11. Setup systemd services to run paperless automatically. You may
 | 
			
		||||
    use the service definition files included in the ``scripts`` folder
 | 
			
		||||
    as a starting point.
 | 
			
		||||
 | 
			
		||||
    Paperless needs the ``webserver`` script to run the webserver, the
 | 
			
		||||
    ``consumer`` script to watch the input folder, ``taskqueue`` for the background workers
 | 
			
		||||
    used to handle things like document consumption and the ``scheduler`` script to run tasks such as
 | 
			
		||||
    email checking at certain times .
 | 
			
		||||
 | 
			
		||||
		The ``socket`` script enables ``gunicorn`` to run on port 80 without
 | 
			
		||||
		root privileges. For this you need to uncomment the ``Require=paperless-webserver.socket``
 | 
			
		||||
		in the ``webserver`` script and configure ``gunicorn`` to listen on port 80 (see ``paperless/gunicorn.conf.py``).
 | 
			
		||||
 | 
			
		||||
    You may need to adjust the path to the ``gunicorn`` executable. This
 | 
			
		||||
    will be installed as part of the python dependencies, and is either located
 | 
			
		||||
    in the ``bin`` folder of your virtual environment, or in ``~/.local/bin/`` if
 | 
			
		||||
    no virtual environment is used.
 | 
			
		||||
 | 
			
		||||
    These services rely on redis and optionally the database server, but
 | 
			
		||||
    don't need to be started in any particular order. The example files
 | 
			
		||||
    depend on redis being started. If you use a database server, you should
 | 
			
		||||
    add additional dependencies.
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        The included scripts run a ``gunicorn`` standalone server,
 | 
			
		||||
        which is fine for running paperless. It does support SSL,
 | 
			
		||||
        however, the documentation of GUnicorn states that you should
 | 
			
		||||
        use a proxy server in front of gunicorn instead.
 | 
			
		||||
 | 
			
		||||
        For instructions on how to use nginx for that,
 | 
			
		||||
        :ref:`see the instructions below <setup-nginx>`.
 | 
			
		||||
 | 
			
		||||
12. Optional: Install a samba server and make the consumption folder
 | 
			
		||||
    available as a network share.
 | 
			
		||||
 | 
			
		||||
13. Configure ImageMagick to allow processing of PDF documents. Most distributions have
 | 
			
		||||
    this disabled by default, since PDF documents can contain malware. If
 | 
			
		||||
    you don't do this, paperless will fall back to ghostscript for certain steps
 | 
			
		||||
    such as thumbnail generation.
 | 
			
		||||
 | 
			
		||||
    Edit ``/etc/ImageMagick-6/policy.xml`` and adjust
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        <policy domain="coder" rights="none" pattern="PDF" />
 | 
			
		||||
 | 
			
		||||
    to
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        <policy domain="coder" rights="read|write" pattern="PDF" />
 | 
			
		||||
 | 
			
		||||
14. Optional: Install the `jbig2enc <https://ocrmypdf.readthedocs.io/en/latest/jbig2.html>`_
 | 
			
		||||
    encoder. This will reduce the size of generated PDF documents. You'll most likely need
 | 
			
		||||
    to compile this by yourself, because this software has been patented until around 2017 and
 | 
			
		||||
    binary packages are not available for most distributions.
 | 
			
		||||
 | 
			
		||||
15. Optional: If using the NLTK machine learning processing (see ``PAPERLESS_ENABLE_NLTK`` in
 | 
			
		||||
    :ref:`configuration` for details), download the NLTK data for the Snowball Stemmer, Stopwords
 | 
			
		||||
    and Punkt tokenizer to your ``PAPERLESS_DATA_DIR/nltk``.  Refer to
 | 
			
		||||
    the `NLTK instructions <https://www.nltk.org/data.html>`_ for details on how to
 | 
			
		||||
    download the data.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Migrating to Paperless-ngx
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
Migration is possible both from Paperless-ng or directly from the 'original' Paperless.
 | 
			
		||||
 | 
			
		||||
Migrating from Paperless-ng
 | 
			
		||||
===========================
 | 
			
		||||
 | 
			
		||||
Paperless-ngx is meant to be a drop-in replacement for Paperless-ng and thus upgrading should be
 | 
			
		||||
trivial for most users, especially when using docker. However, as with any major change, it is
 | 
			
		||||
recommended to take a full backup first. Once you are ready, simply change the docker image to
 | 
			
		||||
point to the new source. E.g. if using Docker Compose, edit ``docker-compose.yml`` and change:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
  image: jonaswinkler/paperless-ng:latest
 | 
			
		||||
 | 
			
		||||
to
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
  image: ghcr.io/paperless-ngx/paperless-ngx:latest
 | 
			
		||||
 | 
			
		||||
and then run ``docker-compose up -d`` which will pull the new image recreate the container.
 | 
			
		||||
That's it!
 | 
			
		||||
 | 
			
		||||
Users who installed with the bare-metal route should also update their Git clone to point to
 | 
			
		||||
``https://github.com/paperless-ngx/paperless-ngx``, e.g. using the command
 | 
			
		||||
``git remote set-url origin https://github.com/paperless-ngx/paperless-ngx`` and then pull the
 | 
			
		||||
lastest version.
 | 
			
		||||
 | 
			
		||||
Migrating from Paperless
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
At its core, paperless-ngx is still paperless and fully compatible. However, some
 | 
			
		||||
things have changed under the hood, so you need to adapt your setup depending on
 | 
			
		||||
how you installed paperless.
 | 
			
		||||
 | 
			
		||||
This setup describes how to update an existing paperless Docker installation.
 | 
			
		||||
The important things to keep in mind are as follows:
 | 
			
		||||
 | 
			
		||||
* Read the :doc:`changelog </changelog>` and take note of breaking changes.
 | 
			
		||||
* You should decide if you want to stick with SQLite or want to migrate your database
 | 
			
		||||
  to PostgreSQL. See :ref:`setup-sqlite_to_psql` for details on how to move your data from
 | 
			
		||||
  SQLite to PostgreSQL. Both work fine with paperless. However, if you already have a
 | 
			
		||||
  database server running for other services, you might as well use it for paperless as well.
 | 
			
		||||
* The task scheduler of paperless, which is used to execute periodic tasks
 | 
			
		||||
  such as email checking and maintenance, requires a `redis`_ message broker
 | 
			
		||||
  instance. The docker-compose route takes care of that.
 | 
			
		||||
* The layout of the folder structure for your documents and data remains the
 | 
			
		||||
  same, so you can just plug your old docker volumes into paperless-ngx and
 | 
			
		||||
  expect it to find everything where it should be.
 | 
			
		||||
 | 
			
		||||
Migration to paperless-ngx is then performed in a few simple steps:
 | 
			
		||||
 | 
			
		||||
1.  Stop paperless.
 | 
			
		||||
 | 
			
		||||
    .. code:: bash
 | 
			
		||||
 | 
			
		||||
        $ cd /path/to/current/paperless
 | 
			
		||||
        $ docker-compose down
 | 
			
		||||
 | 
			
		||||
2.  Do a backup for two purposes: If something goes wrong, you still have your
 | 
			
		||||
    data. Second, if you don't like paperless-ngx, you can switch back to
 | 
			
		||||
    paperless.
 | 
			
		||||
 | 
			
		||||
3.  Download the latest release of paperless-ngx. You can either go with the
 | 
			
		||||
    docker-compose files from `here <https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose>`__
 | 
			
		||||
    or clone the repository to build the image yourself (see :ref:`above <setup-docker_build>`).
 | 
			
		||||
    You can either replace your current paperless folder or put paperless-ngx
 | 
			
		||||
    in a different location.
 | 
			
		||||
 | 
			
		||||
    .. caution::
 | 
			
		||||
 | 
			
		||||
        Paperless-ngx includes a ``.env`` file. This will set the
 | 
			
		||||
        project name for docker compose to ``paperless``, which will also define the name
 | 
			
		||||
        of the volumes by paperless-ngx. However, if you experience that paperless-ngx
 | 
			
		||||
        is not using your old paperless volumes, verify the names of your volumes with
 | 
			
		||||
 | 
			
		||||
        .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
            $ docker volume ls | grep _data
 | 
			
		||||
 | 
			
		||||
        and adjust the project name in the ``.env`` file so that it matches the name
 | 
			
		||||
        of the volumes before the ``_data`` part.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
4.  Download the ``docker-compose.sqlite.yml`` file to ``docker-compose.yml``.
 | 
			
		||||
    If you want to switch to PostgreSQL, do that after you migrated your existing
 | 
			
		||||
    SQLite database.
 | 
			
		||||
 | 
			
		||||
5.  Adjust ``docker-compose.yml`` and ``docker-compose.env`` to your needs.
 | 
			
		||||
    See :ref:`setup-docker_hub` for details on which edits are advised.
 | 
			
		||||
 | 
			
		||||
6.  :ref:`Update paperless. <administration-updating>`
 | 
			
		||||
 | 
			
		||||
7.  In order to find your existing documents with the new search feature, you need
 | 
			
		||||
    to invoke a one-time operation that will create the search index:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ docker-compose run --rm webserver document_index reindex
 | 
			
		||||
 | 
			
		||||
    This will migrate your database and create the search index. After that,
 | 
			
		||||
    paperless will take care of maintaining the index by itself.
 | 
			
		||||
 | 
			
		||||
8.  Start paperless-ngx.
 | 
			
		||||
 | 
			
		||||
    .. code:: bash
 | 
			
		||||
 | 
			
		||||
        $ docker-compose up -d
 | 
			
		||||
 | 
			
		||||
    This will run paperless in the background and automatically start it on system boot.
 | 
			
		||||
 | 
			
		||||
9.  Paperless installed a permanent redirect to ``admin/`` in your browser. This
 | 
			
		||||
    redirect is still in place and prevents access to the new UI. Clear your
 | 
			
		||||
    browsing cache in order to fix this.
 | 
			
		||||
 | 
			
		||||
10.  Optionally, follow the instructions below to migrate your existing data to PostgreSQL.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Migrating from LinuxServer.io Docker Image
 | 
			
		||||
==========================================
 | 
			
		||||
 | 
			
		||||
As with any upgrades and large changes, it is highly recommended to create a backup before
 | 
			
		||||
starting.  This assumes the image was running using Docker Compose, but the instructions
 | 
			
		||||
are translatable to Docker commands as well.
 | 
			
		||||
 | 
			
		||||
1.  Stop and remove the paperless container
 | 
			
		||||
2.  If using an external database, stop the container
 | 
			
		||||
3.  Update Redis configuration
 | 
			
		||||
 | 
			
		||||
    a)  If ``REDIS_URL`` is already set, change it to ``PAPERLESS_REDIS`` and continue
 | 
			
		||||
        to step 4.
 | 
			
		||||
    b)  Otherwise, in the ``docker-compose.yml`` add a new service for Redis,
 | 
			
		||||
        following `the example compose files <https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose>`_
 | 
			
		||||
    c)  Set the environment variable ``PAPERLESS_REDIS`` so it points to the new Redis container
 | 
			
		||||
 | 
			
		||||
4.  Update user mapping
 | 
			
		||||
 | 
			
		||||
    a)  If set, change the environment variable ``PUID`` to ``USERMAP_UID``
 | 
			
		||||
    b)  If set, change the environment variable ``PGID`` to ``USERMAP_GID``
 | 
			
		||||
 | 
			
		||||
5.  Update configuration paths
 | 
			
		||||
 | 
			
		||||
    a) Set the environment variable ``PAPERLESS_DATA_DIR``
 | 
			
		||||
       to ``/config``
 | 
			
		||||
 | 
			
		||||
6.  Update media paths
 | 
			
		||||
 | 
			
		||||
    a) Set the environment variable ``PAPERLESS_MEDIA_ROOT``
 | 
			
		||||
       to ``/data/media``
 | 
			
		||||
 | 
			
		||||
7.  Update timezone
 | 
			
		||||
 | 
			
		||||
    a) Set the environment variable ``PAPERLESS_TIME_ZONE``
 | 
			
		||||
       to the same value as ``TZ``
 | 
			
		||||
 | 
			
		||||
8.  Modify the ``image:`` to point to ``ghcr.io/paperless-ngx/paperless-ngx:latest`` or
 | 
			
		||||
    a specific version if preferred.
 | 
			
		||||
 | 
			
		||||
9.  Start the containers as before, using ``docker-compose``.
 | 
			
		||||
 | 
			
		||||
.. _setup-sqlite_to_psql:
 | 
			
		||||
 | 
			
		||||
Moving data from SQLite to PostgreSQL or MySQL/MariaDB
 | 
			
		||||
======================================================
 | 
			
		||||
 | 
			
		||||
Moving your data from SQLite to PostgreSQL or MySQL/MariaDB is done via executing a series of django
 | 
			
		||||
management commands as below.  The commands below use PostgreSQL, but are applicable to MySQL/MariaDB
 | 
			
		||||
with the
 | 
			
		||||
 | 
			
		||||
.. caution::
 | 
			
		||||
 | 
			
		||||
    Make sure that your SQLite database is migrated to the latest version.
 | 
			
		||||
    Starting paperless will make sure that this is the case. If your try to
 | 
			
		||||
    load data from an old database schema in SQLite into a newer database
 | 
			
		||||
    schema in PostgreSQL, you will run into trouble.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    On some database fields, PostgreSQL enforces predefined limits on maximum
 | 
			
		||||
    length, whereas SQLite does not. The fields in question are the title of documents
 | 
			
		||||
    (128 characters), names of document types, tags and correspondents (128 characters),
 | 
			
		||||
    and filenames (1024 characters). If you have data in these fields that surpasses these
 | 
			
		||||
    limits, migration to PostgreSQL is not possible and will fail with an error.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    MySQL is case insensitive by default, treating values like "Name" and "NAME" as identical.
 | 
			
		||||
    See :ref:`advanced-mysql-caveats` for details.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
1.  Stop paperless, if it is running.
 | 
			
		||||
2.  Tell paperless to use PostgreSQL:
 | 
			
		||||
 | 
			
		||||
    a)  With docker, copy the provided ``docker-compose.postgres.yml`` file to
 | 
			
		||||
        ``docker-compose.yml``. Remember to adjust the consumption directory,
 | 
			
		||||
        if necessary.
 | 
			
		||||
    b)  Without docker, configure the database in your ``paperless.conf`` file.
 | 
			
		||||
        See :ref:`configuration` for details.
 | 
			
		||||
 | 
			
		||||
3.  Open a shell and initialize the database:
 | 
			
		||||
 | 
			
		||||
    a)  With docker, run the following command to open a shell within the paperless
 | 
			
		||||
        container:
 | 
			
		||||
 | 
			
		||||
        .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
            $ cd /path/to/paperless
 | 
			
		||||
            $ docker-compose run --rm webserver /bin/bash
 | 
			
		||||
 | 
			
		||||
        This will launch the container and initialize the PostgreSQL database.
 | 
			
		||||
 | 
			
		||||
    b)  Without docker, remember to activate any virtual environment, switch to
 | 
			
		||||
        the ``src`` directory and create the database schema:
 | 
			
		||||
 | 
			
		||||
        .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
            $ cd /path/to/paperless/src
 | 
			
		||||
            $ python3 manage.py migrate
 | 
			
		||||
 | 
			
		||||
        This will not copy any data yet.
 | 
			
		||||
 | 
			
		||||
4.  Dump your data from SQLite:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ python3 manage.py dumpdata --database=sqlite --exclude=contenttypes --exclude=auth.Permission > data.json
 | 
			
		||||
 | 
			
		||||
5.  Load your data into PostgreSQL:
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ python3 manage.py loaddata data.json
 | 
			
		||||
 | 
			
		||||
6.  If operating inside Docker, you may exit the shell now.
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ exit
 | 
			
		||||
 | 
			
		||||
7.  Start paperless.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Moving back to Paperless
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
Lets say you migrated to Paperless-ngx and used it for a while, but decided that
 | 
			
		||||
you don't like it and want to move back (If you do, send me a mail about what
 | 
			
		||||
part you didn't like!), you can totally do that with a few simple steps.
 | 
			
		||||
 | 
			
		||||
Paperless-ngx modified the database schema slightly, however, these changes can
 | 
			
		||||
be reverted while keeping your current data, so that your current data will
 | 
			
		||||
be compatible with original Paperless.
 | 
			
		||||
 | 
			
		||||
Execute this:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ cd /path/to/paperless
 | 
			
		||||
    $ docker-compose run --rm webserver migrate documents 0023
 | 
			
		||||
 | 
			
		||||
Or without docker:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ cd /path/to/paperless/src
 | 
			
		||||
    $ python3 manage.py migrate documents 0023
 | 
			
		||||
 | 
			
		||||
After that, you need to clear your cookies (Paperless-ngx comes with updated
 | 
			
		||||
dependencies that do cookie-processing differently) and probably your cache
 | 
			
		||||
as well.
 | 
			
		||||
 | 
			
		||||
.. _setup-less_powerful_devices:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Considerations for less powerful devices
 | 
			
		||||
########################################
 | 
			
		||||
 | 
			
		||||
Paperless runs on Raspberry Pi. However, some things are rather slow on the Pi and
 | 
			
		||||
configuring some options in paperless can help improve performance immensely:
 | 
			
		||||
 | 
			
		||||
*   Stick with SQLite to save some resources.
 | 
			
		||||
*   Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR
 | 
			
		||||
    the first page of your documents. In most cases, this page contains enough
 | 
			
		||||
    information to be able to find it.
 | 
			
		||||
*   ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured
 | 
			
		||||
    to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that
 | 
			
		||||
    paperless will use 2 workers and 2 threads per worker. This may result in
 | 
			
		||||
    sluggish response times during consumption, so you might want to lower these
 | 
			
		||||
    settings (example: 2 workers and 1 thread to always have some computing power
 | 
			
		||||
    left for other tasks).
 | 
			
		||||
*   Keep ``PAPERLESS_OCR_MODE`` at its default value ``skip`` and consider OCR'ing
 | 
			
		||||
    your documents before feeding them into paperless. Some scanners are able to
 | 
			
		||||
    do this! You might want to even specify ``skip_noarchive`` to skip archive
 | 
			
		||||
    file generation for already ocr'ed documents entirely.
 | 
			
		||||
*   If you want to perform OCR on the device, consider using ``PAPERLESS_OCR_CLEAN=none``.
 | 
			
		||||
    This will speed up OCR times and use less memory at the expense of slightly worse
 | 
			
		||||
    OCR results.
 | 
			
		||||
*   If using docker, consider setting ``PAPERLESS_WEBSERVER_WORKERS`` to
 | 
			
		||||
    1. This will save some memory.
 | 
			
		||||
*   Consider setting ``PAPERLESS_ENABLE_NLTK`` to false, to disable the more
 | 
			
		||||
    advanced language processing, which can take more memory and processing time.
 | 
			
		||||
 | 
			
		||||
For details, refer to :ref:`configuration`.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    Updating the :ref:`automatic matching algorithm <advanced-automatic_matching>`
 | 
			
		||||
    takes quite a bit of time. However, the update mechanism checks if your
 | 
			
		||||
    data has changed before doing the heavy lifting. If you experience the
 | 
			
		||||
    algorithm taking too much cpu time, consider changing the schedule in the
 | 
			
		||||
    admin interface to daily. You can also manually invoke the task
 | 
			
		||||
    by changing the date and time of the next run to today/now.
 | 
			
		||||
 | 
			
		||||
    The actual matching of the algorithm is fast and works on Raspberry Pi as
 | 
			
		||||
    well as on any other device.
 | 
			
		||||
 | 
			
		||||
.. _redis: https://redis.io/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _setup-nginx:
 | 
			
		||||
 | 
			
		||||
Using nginx as a reverse proxy
 | 
			
		||||
##############################
 | 
			
		||||
 | 
			
		||||
If you want to expose paperless to the internet, you should hide it behind a
 | 
			
		||||
reverse proxy with SSL enabled.
 | 
			
		||||
 | 
			
		||||
In addition to the usual configuration for SSL,
 | 
			
		||||
the following configuration is required for paperless to operate:
 | 
			
		||||
 | 
			
		||||
.. code:: nginx
 | 
			
		||||
 | 
			
		||||
    http {
 | 
			
		||||
 | 
			
		||||
        # Adjust as required. This is the maximum size for file uploads.
 | 
			
		||||
        # The default value 1M might be a little too small.
 | 
			
		||||
        client_max_body_size 10M;
 | 
			
		||||
 | 
			
		||||
        server {
 | 
			
		||||
 | 
			
		||||
            location / {
 | 
			
		||||
 | 
			
		||||
                # Adjust host and port as required.
 | 
			
		||||
                proxy_pass http://localhost:8000/;
 | 
			
		||||
 | 
			
		||||
                # These configuration options are required for WebSockets to work.
 | 
			
		||||
                proxy_http_version 1.1;
 | 
			
		||||
                proxy_set_header Upgrade $http_upgrade;
 | 
			
		||||
                proxy_set_header Connection "upgrade";
 | 
			
		||||
 | 
			
		||||
                proxy_redirect off;
 | 
			
		||||
                proxy_set_header Host $host;
 | 
			
		||||
                proxy_set_header X-Real-IP $remote_addr;
 | 
			
		||||
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 | 
			
		||||
                proxy_set_header X-Forwarded-Host $server_name;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
The ``PAPERLESS_URL`` configuration variable is also required when using a reverse proxy. Please refer to the :ref:`hosting-and-security` docs.
 | 
			
		||||
 | 
			
		||||
Also read `this <https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu>`__, towards the end of the section.
 | 
			
		||||
							
								
								
									
										335
									
								
								docs/troubleshooting.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,335 @@
 | 
			
		||||
# Troubleshooting
 | 
			
		||||
 | 
			
		||||
## No files are added by the consumer
 | 
			
		||||
 | 
			
		||||
Check for the following issues:
 | 
			
		||||
 | 
			
		||||
- Ensure that the directory you're putting your documents in is the
 | 
			
		||||
  folder paperless is watching. With docker, this setting is performed
 | 
			
		||||
  in the `docker-compose.yml` file. Without docker, look at the
 | 
			
		||||
  `CONSUMPTION_DIR` setting. Don't adjust this setting if you're
 | 
			
		||||
  using docker.
 | 
			
		||||
 | 
			
		||||
- Ensure that redis is up and running. Paperless does its task
 | 
			
		||||
  processing asynchronously, and for documents to arrive at the task
 | 
			
		||||
  processor, it needs redis to run.
 | 
			
		||||
 | 
			
		||||
- Ensure that the task processor is running. Docker does this
 | 
			
		||||
  automatically. Manually invoke the task processor by executing
 | 
			
		||||
 | 
			
		||||
  ```shell-session
 | 
			
		||||
  $ celery --app paperless worker
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
- Look at the output of paperless and inspect it for any errors.
 | 
			
		||||
 | 
			
		||||
- Go to the admin interface, and check if there are failed tasks. If
 | 
			
		||||
  so, the tasks will contain an error message.
 | 
			
		||||
 | 
			
		||||
## Consumer warns `OCR for XX failed`
 | 
			
		||||
 | 
			
		||||
If you find the OCR accuracy to be too low, and/or the document consumer
 | 
			
		||||
warns that
 | 
			
		||||
`OCR for XX failed, but we're going to stick with what we've got since FORGIVING_OCR is enabled`,
 | 
			
		||||
then you might need to install the [Tesseract language
 | 
			
		||||
files](http://packages.ubuntu.com/search?keywords=tesseract-ocr)
 | 
			
		||||
marching your document's languages.
 | 
			
		||||
 | 
			
		||||
As an example, if you are running Paperless-ngx from any Ubuntu or
 | 
			
		||||
Debian box, and your documents are written in Spanish you may need to
 | 
			
		||||
run:
 | 
			
		||||
 | 
			
		||||
    apt-get install -y tesseract-ocr-spa
 | 
			
		||||
 | 
			
		||||
## Consumer fails to pickup any new files
 | 
			
		||||
 | 
			
		||||
If you notice that the consumer will only pickup files in the
 | 
			
		||||
consumption directory at startup, but won't find any other files added
 | 
			
		||||
later, you will need to enable filesystem polling with the configuration
 | 
			
		||||
option `PAPERLESS_CONSUMER_POLLING`, see
 | 
			
		||||
`[here](/configuration#polling).
 | 
			
		||||
 | 
			
		||||
This will disable listening to filesystem changes with inotify and
 | 
			
		||||
paperless will manually check the consumption directory for changes
 | 
			
		||||
instead.
 | 
			
		||||
 | 
			
		||||
## Paperless always redirects to /admin
 | 
			
		||||
 | 
			
		||||
You probably had the old paperless installed at some point. Paperless
 | 
			
		||||
installed a permanent redirect to /admin in your browser, and you need
 | 
			
		||||
to clear your browsing data / cache to fix that.
 | 
			
		||||
 | 
			
		||||
## Operation not permitted
 | 
			
		||||
 | 
			
		||||
You might see errors such as:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
chown: changing ownership of '../export': Operation not permitted
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The container tries to set file ownership on the listed directories.
 | 
			
		||||
This is required so that the user running paperless inside docker has
 | 
			
		||||
write permissions to these folders. This happens when pointing these
 | 
			
		||||
directories to NFS shares, for example.
 | 
			
		||||
 | 
			
		||||
Ensure that `chown` is possible on these directories.
 | 
			
		||||
 | 
			
		||||
## Classifier error: No training data available
 | 
			
		||||
 | 
			
		||||
This indicates that the Auto matching algorithm found no documents to
 | 
			
		||||
learn from. This may have two reasons:
 | 
			
		||||
 | 
			
		||||
- You don't use the Auto matching algorithm: The error can be safely
 | 
			
		||||
  ignored in this case.
 | 
			
		||||
- You are using the Auto matching algorithm: The classifier explicitly
 | 
			
		||||
  excludes documents with Inbox tags. Verify that there are documents
 | 
			
		||||
  in your archive without inbox tags. The algorithm will only learn
 | 
			
		||||
  from documents not in your inbox.
 | 
			
		||||
 | 
			
		||||
## UserWarning in sklearn on every single document
 | 
			
		||||
 | 
			
		||||
You may encounter warnings like this:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
/usr/local/lib/python3.7/site-packages/sklearn/base.py:315:
 | 
			
		||||
UserWarning: Trying to unpickle estimator CountVectorizer from version 0.23.2 when using version 0.24.0.
 | 
			
		||||
This might lead to breaking code or invalid results. Use at your own risk.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This happens when certain dependencies of paperless that are responsible
 | 
			
		||||
for the auto matching algorithm are updated. After updating these, your
 | 
			
		||||
current training data _might_ not be compatible anymore. This can be
 | 
			
		||||
ignored in most cases. This warning will disappear automatically when
 | 
			
		||||
paperless updates the training data.
 | 
			
		||||
 | 
			
		||||
If you want to get rid of the warning or actually experience issues with
 | 
			
		||||
automatic matching, delete the file `classification_model.pickle` in the
 | 
			
		||||
data directory and let paperless recreate it.
 | 
			
		||||
 | 
			
		||||
## 504 Server Error: Gateway Timeout when adding Office documents
 | 
			
		||||
 | 
			
		||||
You may experience these errors when using the optional TIKA
 | 
			
		||||
integration:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
requests.exceptions.HTTPError: 504 Server Error: Gateway Timeout for url: http://gotenberg:3000/forms/libreoffice/convert
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Gotenberg is a server that converts Office documents into PDF documents
 | 
			
		||||
and has a default timeout of 30 seconds. When conversion takes longer,
 | 
			
		||||
Gotenberg raises this error.
 | 
			
		||||
 | 
			
		||||
You can increase the timeout by configuring a command flag for Gotenberg
 | 
			
		||||
(see also [here](https://gotenberg.dev/docs/modules/api#properties)). If
 | 
			
		||||
using docker-compose, this is achieved by the following configuration
 | 
			
		||||
change in the `docker-compose.yml` file:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
gotenberg:
 | 
			
		||||
  image: gotenberg/gotenberg:7.6
 | 
			
		||||
  restart: unless-stopped
 | 
			
		||||
  command:
 | 
			
		||||
    - 'gotenberg'
 | 
			
		||||
    - '--chromium-disable-routes=true'
 | 
			
		||||
    - '--api-timeout=60'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Permission denied errors in the consumption directory
 | 
			
		||||
 | 
			
		||||
You might encounter errors such as:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
The following error occured while consuming document.pdf: [Errno 13] Permission denied: '/usr/src/paperless/src/../consume/document.pdf'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This happens when paperless does not have permission to delete files
 | 
			
		||||
inside the consumption directory. Ensure that `USERMAP_UID` and
 | 
			
		||||
`USERMAP_GID` are set to the user id and group id you use on the host
 | 
			
		||||
operating system, if these are different from `1000`. See
 | 
			
		||||
`setup-docker_hub`{.interpreted-text role="ref"}.
 | 
			
		||||
 | 
			
		||||
Also ensure that you are able to read and write to the consumption
 | 
			
		||||
directory on the host.
 | 
			
		||||
 | 
			
		||||
## OSError: \[Errno 19\] No such device when consuming files
 | 
			
		||||
 | 
			
		||||
If you experience errors such as:
 | 
			
		||||
 | 
			
		||||
```shell-session
 | 
			
		||||
File "/usr/local/lib/python3.7/site-packages/whoosh/codec/base.py", line 570, in open_compound_file
 | 
			
		||||
return CompoundStorage(dbfile, use_mmap=storage.supports_mmap)
 | 
			
		||||
File "/usr/local/lib/python3.7/site-packages/whoosh/filedb/compound.py", line 75, in __init__
 | 
			
		||||
self._source = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ)
 | 
			
		||||
OSError: [Errno 19] No such device
 | 
			
		||||
 | 
			
		||||
During handling of the above exception, another exception occurred:
 | 
			
		||||
 | 
			
		||||
Traceback (most recent call last):
 | 
			
		||||
File "/usr/local/lib/python3.7/site-packages/django_q/cluster.py", line 436, in worker
 | 
			
		||||
res = f(*task["args"], **task["kwargs"])
 | 
			
		||||
File "/usr/src/paperless/src/documents/tasks.py", line 73, in consume_file
 | 
			
		||||
override_tag_ids=override_tag_ids)
 | 
			
		||||
File "/usr/src/paperless/src/documents/consumer.py", line 271, in try_consume_file
 | 
			
		||||
raise ConsumerError(e)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Paperless uses a search index to provide better and faster full text
 | 
			
		||||
searching. This search index is stored inside the `data` folder. The
 | 
			
		||||
search index uses memory-mapped files (mmap). The above error indicates
 | 
			
		||||
that paperless was unable to create and open these files.
 | 
			
		||||
 | 
			
		||||
This happens when you're trying to store the data directory on certain
 | 
			
		||||
file systems (mostly network shares) that don't support memory-mapped
 | 
			
		||||
files.
 | 
			
		||||
 | 
			
		||||
## Web-UI stuck at "Loading\..."
 | 
			
		||||
 | 
			
		||||
This might have multiple reasons.
 | 
			
		||||
 | 
			
		||||
1.  If you built the docker image yourself or deployed using the bare
 | 
			
		||||
    metal route, make sure that there are files in
 | 
			
		||||
    `<paperless-root>/static/frontend/<lang-code>/`. If there are no
 | 
			
		||||
    files, make sure that you executed `collectstatic` successfully,
 | 
			
		||||
    either manually or as part of the docker image build.
 | 
			
		||||
 | 
			
		||||
    If the front end is still missing, make sure that the front end is
 | 
			
		||||
    compiled (files present in `src/documents/static/frontend`). If it
 | 
			
		||||
    is not, you need to compile the front end yourself or download the
 | 
			
		||||
    release archive instead of cloning the repository.
 | 
			
		||||
 | 
			
		||||
2.  Check the output of the web server. You might see errors like this:
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    [2021-01-25 10:08:04 +0000] [40] [ERROR] Socket error processing request.
 | 
			
		||||
    Traceback (most recent call last):
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 134, in handle
 | 
			
		||||
        self.handle_request(listener, req, client, addr)
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 190, in handle_request
 | 
			
		||||
        util.reraise(*sys.exc_info())
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 625, in reraise
 | 
			
		||||
        raise value
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 178, in handle_request
 | 
			
		||||
        resp.write_file(respiter)
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 396, in write_file
 | 
			
		||||
        if not self.sendfile(respiter):
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 386, in sendfile
 | 
			
		||||
        sent += os.sendfile(sockno, fileno, offset + sent, count)
 | 
			
		||||
    OSError: [Errno 22] Invalid argument
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    To fix this issue, add
 | 
			
		||||
 | 
			
		||||
    ```
 | 
			
		||||
    SENDFILE=0
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
    to your [docker-compose.env]{.title-ref} file.
 | 
			
		||||
 | 
			
		||||
## Error while reading metadata
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[WARNING] [paperless.parsing.tesseract] Error while reading metadata
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This indicates that paperless failed to read PDF metadata from one of
 | 
			
		||||
your documents. This happens when you open the affected documents in
 | 
			
		||||
paperless for editing. Paperless will continue to work, and will simply
 | 
			
		||||
not show the invalid metadata.
 | 
			
		||||
 | 
			
		||||
## Consumer fails with a FileNotFoundError
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[ERROR] [paperless.consumer] Error while consuming document SCN_0001.pdf: FileNotFoundError: [Errno 2] No such file or directory: '/tmp/ocrmypdf.io.yhk3zbv0/origin.pdf'
 | 
			
		||||
Traceback (most recent call last):
 | 
			
		||||
  File "/app/paperless/src/paperless_tesseract/parsers.py", line 261, in parse
 | 
			
		||||
    ocrmypdf.ocr(**args)
 | 
			
		||||
  File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/api.py", line 337, in ocr
 | 
			
		||||
    return run_pipeline(options=options, plugin_manager=plugin_manager, api=True)
 | 
			
		||||
  File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_sync.py", line 385, in run_pipeline
 | 
			
		||||
    exec_concurrent(context, executor)
 | 
			
		||||
  File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_sync.py", line 302, in exec_concurrent
 | 
			
		||||
    pdf = post_process(pdf, context, executor)
 | 
			
		||||
  File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_sync.py", line 235, in post_process
 | 
			
		||||
    pdf_out = metadata_fixup(pdf_out, context)
 | 
			
		||||
  File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_pipeline.py", line 798, in metadata_fixup
 | 
			
		||||
    with pikepdf.open(context.origin) as original, pikepdf.open(working_file) as pdf:
 | 
			
		||||
  File "/usr/local/lib/python3.8/dist-packages/pikepdf/_methods.py", line 923, in open
 | 
			
		||||
    pdf = Pdf._open(
 | 
			
		||||
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/ocrmypdf.io.yhk3zbv0/origin.pdf'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This probably indicates paperless tried to consume the same file twice.
 | 
			
		||||
This can happen for a number of reasons, depending on how documents are
 | 
			
		||||
placed into the consume folder. If paperless is using inotify (the
 | 
			
		||||
default) to check for documents, try adjusting the
 | 
			
		||||
[inotify configuration](/configuration#inotify). If polling is enabled, try adjusting the
 | 
			
		||||
[polling configuration](/configuration#polling).
 | 
			
		||||
 | 
			
		||||
## Consumer fails waiting for file to remain unmodified.
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[ERROR] [paperless.management.consumer] Timeout while waiting on file /usr/src/paperless/src/../consume/SCN_0001.pdf to remain unmodified.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This indicates paperless timed out while waiting for the file to be
 | 
			
		||||
completely written to the consume folder. Adjusting
 | 
			
		||||
[polling configuration](/configuration#polling) values should resolve the issue.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    The user will need to manually move the file out of the consume folder
 | 
			
		||||
    and back in, for the initial failing file to be consumed.
 | 
			
		||||
 | 
			
		||||
## Consumer fails reporting "OS reports file as busy still".
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[WARNING] [paperless.management.consumer] Not consuming file /usr/src/paperless/src/../consume/SCN_0001.pdf: OS reports file as busy still
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This indicates paperless was unable to open the file, as the OS reported
 | 
			
		||||
the file as still being in use. To prevent a crash, paperless did not
 | 
			
		||||
try to consume the file. If paperless is using inotify (the default) to
 | 
			
		||||
check for documents, try adjusting the
 | 
			
		||||
[inotify configuration](/configuration#inotify). If polling is enabled, try adjusting the
 | 
			
		||||
[polling configuration](/configuration#polling).
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    The user will need to manually move the file out of the consume folder
 | 
			
		||||
    and back in, for the initial failing file to be consumed.
 | 
			
		||||
 | 
			
		||||
## Log reports "Creating PaperlessTask failed".
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
[ERROR] [paperless.management.consumer] Creating PaperlessTask failed: db locked
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
You are likely using an sqlite based installation, with an increased
 | 
			
		||||
number of workers and are running into sqlite's concurrency
 | 
			
		||||
limitations. Uploading or consuming multiple files at once results in
 | 
			
		||||
many workers attempting to access the database simultaneously.
 | 
			
		||||
 | 
			
		||||
Consider changing to the PostgreSQL database if you will be processing
 | 
			
		||||
many documents at once often. Otherwise, try tweaking the
 | 
			
		||||
`PAPERLESS_DB_TIMEOUT` setting to allow more time for the database to
 | 
			
		||||
unlock. This may have minor performance implications.
 | 
			
		||||
 | 
			
		||||
## gunicorn fails to start with "is not a valid port number"
 | 
			
		||||
 | 
			
		||||
You are likely running using Kubernetes, which automatically creates an
 | 
			
		||||
environment variable named [\${serviceName}\_PORT]{.title-ref}. This is
 | 
			
		||||
the same environment variable which is used by Paperless to optionally
 | 
			
		||||
change the port gunicorn listens on.
 | 
			
		||||
 | 
			
		||||
To fix this, set [PAPERLESS_PORT]{.title-ref} again to your desired
 | 
			
		||||
port, or the default of 8000.
 | 
			
		||||
@ -1,328 +0,0 @@
 | 
			
		||||
***************
 | 
			
		||||
Troubleshooting
 | 
			
		||||
***************
 | 
			
		||||
 | 
			
		||||
No files are added by the consumer
 | 
			
		||||
##################################
 | 
			
		||||
 | 
			
		||||
Check for the following issues:
 | 
			
		||||
 | 
			
		||||
*   Ensure that the directory you're putting your documents in is the folder
 | 
			
		||||
    paperless is watching. With docker, this setting is performed in the
 | 
			
		||||
    ``docker-compose.yml`` file. Without docker, look at the ``CONSUMPTION_DIR``
 | 
			
		||||
    setting. Don't adjust this setting if you're using docker.
 | 
			
		||||
*   Ensure that redis is up and running. Paperless does its task processing
 | 
			
		||||
    asynchronously, and for documents to arrive at the task processor, it needs
 | 
			
		||||
    redis to run.
 | 
			
		||||
*   Ensure that the task processor is running. Docker does this automatically.
 | 
			
		||||
    Manually invoke the task processor by executing
 | 
			
		||||
 | 
			
		||||
    .. code:: shell-session
 | 
			
		||||
 | 
			
		||||
        $ celery --app paperless worker
 | 
			
		||||
 | 
			
		||||
*   Look at the output of paperless and inspect it for any errors.
 | 
			
		||||
*   Go to the admin interface, and check if there are failed tasks. If so, the
 | 
			
		||||
    tasks will contain an error message.
 | 
			
		||||
 | 
			
		||||
Consumer warns ``OCR for XX failed``
 | 
			
		||||
####################################
 | 
			
		||||
 | 
			
		||||
If you find the OCR accuracy to be too low, and/or the document consumer warns
 | 
			
		||||
that ``OCR for XX failed, but we're going to stick with what we've got since
 | 
			
		||||
FORGIVING_OCR is enabled``, then you might need to install the
 | 
			
		||||
`Tesseract language files <http://packages.ubuntu.com/search?keywords=tesseract-ocr>`_
 | 
			
		||||
marching your document's languages.
 | 
			
		||||
 | 
			
		||||
As an example, if you are running Paperless-ngx from any Ubuntu or Debian
 | 
			
		||||
box, and your documents are written in Spanish you may need to run::
 | 
			
		||||
 | 
			
		||||
    apt-get install -y tesseract-ocr-spa
 | 
			
		||||
 | 
			
		||||
Consumer fails to pickup any new files
 | 
			
		||||
######################################
 | 
			
		||||
 | 
			
		||||
If you notice that the consumer will only pickup files in the consumption
 | 
			
		||||
directory at startup, but won't find any other files added later, you will need to
 | 
			
		||||
enable filesystem polling with the configuration option
 | 
			
		||||
``PAPERLESS_CONSUMER_POLLING``, see :ref:`here <configuration-polling>`.
 | 
			
		||||
 | 
			
		||||
This will disable listening to filesystem changes with inotify and paperless will
 | 
			
		||||
manually check the consumption directory for changes instead.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Paperless always redirects to /admin
 | 
			
		||||
####################################
 | 
			
		||||
 | 
			
		||||
You probably had the old paperless installed at some point. Paperless installed
 | 
			
		||||
a permanent redirect to /admin in your browser, and you need to clear your
 | 
			
		||||
browsing data / cache to fix that.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Operation not permitted
 | 
			
		||||
#######################
 | 
			
		||||
 | 
			
		||||
You might see errors such as:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    chown: changing ownership of '../export': Operation not permitted
 | 
			
		||||
 | 
			
		||||
The container tries to set file ownership on the listed directories. This is
 | 
			
		||||
required so that the user running paperless inside docker has write permissions
 | 
			
		||||
to these folders. This happens when pointing these directories to NFS shares,
 | 
			
		||||
for example.
 | 
			
		||||
 | 
			
		||||
Ensure that ``chown`` is possible on these directories.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Classifier error: No training data available
 | 
			
		||||
############################################
 | 
			
		||||
 | 
			
		||||
This indicates that the Auto matching algorithm found no documents to learn from.
 | 
			
		||||
This may have two reasons:
 | 
			
		||||
 | 
			
		||||
*   You don't use the Auto matching algorithm: The error can be safely ignored in this case.
 | 
			
		||||
*   You are using the Auto matching algorithm: The classifier explicitly excludes documents
 | 
			
		||||
    with Inbox tags. Verify that there are documents in your archive without inbox tags.
 | 
			
		||||
    The algorithm will only learn from documents not in your inbox.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
UserWarning in sklearn on every single document
 | 
			
		||||
###############################################
 | 
			
		||||
 | 
			
		||||
You may encounter warnings like this:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    /usr/local/lib/python3.7/site-packages/sklearn/base.py:315:
 | 
			
		||||
    UserWarning: Trying to unpickle estimator CountVectorizer from version 0.23.2 when using version 0.24.0.
 | 
			
		||||
    This might lead to breaking code or invalid results. Use at your own risk.
 | 
			
		||||
 | 
			
		||||
This happens when certain dependencies of paperless that are responsible for the auto matching algorithm are
 | 
			
		||||
updated. After updating these, your current training data *might* not be compatible anymore. This can be ignored
 | 
			
		||||
in most cases. This warning will disappear automatically when paperless updates the training data.
 | 
			
		||||
 | 
			
		||||
If you want to get rid of the warning or actually experience issues with automatic matching, delete
 | 
			
		||||
the file ``classification_model.pickle`` in the data directory and let paperless recreate it.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
504 Server Error: Gateway Timeout when adding Office documents
 | 
			
		||||
##############################################################
 | 
			
		||||
 | 
			
		||||
You may experience these errors when using the optional TIKA integration:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    requests.exceptions.HTTPError: 504 Server Error: Gateway Timeout for url: http://gotenberg:3000/forms/libreoffice/convert
 | 
			
		||||
 | 
			
		||||
Gotenberg is a server that converts Office documents into PDF documents and has a default timeout of 30 seconds.
 | 
			
		||||
When conversion takes longer, Gotenberg raises this error.
 | 
			
		||||
 | 
			
		||||
You can increase the timeout by configuring a command flag for Gotenberg (see also `here <https://gotenberg.dev/docs/modules/api#properties>`__).
 | 
			
		||||
If using docker-compose, this is achieved by the following configuration change in the ``docker-compose.yml`` file:
 | 
			
		||||
 | 
			
		||||
.. code:: yaml
 | 
			
		||||
 | 
			
		||||
    gotenberg:
 | 
			
		||||
        image: gotenberg/gotenberg:7.6
 | 
			
		||||
        restart: unless-stopped
 | 
			
		||||
        command:
 | 
			
		||||
            - "gotenberg"
 | 
			
		||||
            - "--chromium-disable-routes=true"
 | 
			
		||||
            - "--api-timeout=60"
 | 
			
		||||
 | 
			
		||||
Permission denied errors in the consumption directory
 | 
			
		||||
#####################################################
 | 
			
		||||
 | 
			
		||||
You might encounter errors such as:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    The following error occured while consuming document.pdf: [Errno 13] Permission denied: '/usr/src/paperless/src/../consume/document.pdf'
 | 
			
		||||
 | 
			
		||||
This happens when paperless does not have permission to delete files inside the consumption directory.
 | 
			
		||||
Ensure that ``USERMAP_UID`` and ``USERMAP_GID`` are set to the user id and group id you use on the host operating system, if these are
 | 
			
		||||
different from ``1000``. See :ref:`setup-docker_hub`.
 | 
			
		||||
 | 
			
		||||
Also ensure that you are able to read and write to the consumption directory on the host.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
OSError: [Errno 19] No such device when consuming files
 | 
			
		||||
#######################################################
 | 
			
		||||
 | 
			
		||||
If you experience errors such as:
 | 
			
		||||
 | 
			
		||||
.. code:: shell-session
 | 
			
		||||
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/whoosh/codec/base.py", line 570, in open_compound_file
 | 
			
		||||
    return CompoundStorage(dbfile, use_mmap=storage.supports_mmap)
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/whoosh/filedb/compound.py", line 75, in __init__
 | 
			
		||||
    self._source = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ)
 | 
			
		||||
    OSError: [Errno 19] No such device
 | 
			
		||||
 | 
			
		||||
    During handling of the above exception, another exception occurred:
 | 
			
		||||
 | 
			
		||||
    Traceback (most recent call last):
 | 
			
		||||
    File "/usr/local/lib/python3.7/site-packages/django_q/cluster.py", line 436, in worker
 | 
			
		||||
    res = f(*task["args"], **task["kwargs"])
 | 
			
		||||
    File "/usr/src/paperless/src/documents/tasks.py", line 73, in consume_file
 | 
			
		||||
    override_tag_ids=override_tag_ids)
 | 
			
		||||
    File "/usr/src/paperless/src/documents/consumer.py", line 271, in try_consume_file
 | 
			
		||||
    raise ConsumerError(e)
 | 
			
		||||
 | 
			
		||||
Paperless uses a search index to provide better and faster full text searching. This search index is stored inside
 | 
			
		||||
the ``data`` folder. The search index uses memory-mapped files (mmap). The above error indicates that paperless
 | 
			
		||||
was unable to create and open these files.
 | 
			
		||||
 | 
			
		||||
This happens when you're trying to store the data directory on certain file systems (mostly network shares)
 | 
			
		||||
that don't support memory-mapped files.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Web-UI stuck at "Loading..."
 | 
			
		||||
############################
 | 
			
		||||
 | 
			
		||||
This might have multiple reasons.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
1.  If you built the docker image yourself or deployed using the bare metal route,
 | 
			
		||||
    make sure that there are files in ``<paperless-root>/static/frontend/<lang-code>/``.
 | 
			
		||||
    If there are no files, make sure that you executed ``collectstatic`` successfully, either
 | 
			
		||||
    manually or as part of the docker image build.
 | 
			
		||||
 | 
			
		||||
    If the front end is still missing, make sure that the front end is compiled (files present in
 | 
			
		||||
    ``src/documents/static/frontend``). If it is not, you need to compile the front end yourself
 | 
			
		||||
    or download the release archive instead of cloning the repository.
 | 
			
		||||
 | 
			
		||||
2.  Check the output of the web server. You might see errors like this:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        [2021-01-25 10:08:04 +0000] [40] [ERROR] Socket error processing request.
 | 
			
		||||
        Traceback (most recent call last):
 | 
			
		||||
        File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 134, in handle
 | 
			
		||||
            self.handle_request(listener, req, client, addr)
 | 
			
		||||
        File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 190, in handle_request
 | 
			
		||||
            util.reraise(*sys.exc_info())
 | 
			
		||||
        File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 625, in reraise
 | 
			
		||||
            raise value
 | 
			
		||||
        File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 178, in handle_request
 | 
			
		||||
            resp.write_file(respiter)
 | 
			
		||||
        File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 396, in write_file
 | 
			
		||||
            if not self.sendfile(respiter):
 | 
			
		||||
        File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 386, in sendfile
 | 
			
		||||
            sent += os.sendfile(sockno, fileno, offset + sent, count)
 | 
			
		||||
        OSError: [Errno 22] Invalid argument
 | 
			
		||||
 | 
			
		||||
    To fix this issue, add
 | 
			
		||||
 | 
			
		||||
    .. code::
 | 
			
		||||
 | 
			
		||||
        SENDFILE=0
 | 
			
		||||
 | 
			
		||||
    to your `docker-compose.env` file.
 | 
			
		||||
 | 
			
		||||
Error while reading metadata
 | 
			
		||||
############################
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    [WARNING] [paperless.parsing.tesseract] Error while reading metadata
 | 
			
		||||
 | 
			
		||||
This indicates that paperless failed to read PDF metadata from one of your documents. This happens when you
 | 
			
		||||
open the affected documents in paperless for editing. Paperless will continue to work, and will simply not
 | 
			
		||||
show the invalid metadata.
 | 
			
		||||
 | 
			
		||||
Consumer fails with a FileNotFoundError
 | 
			
		||||
#######################################
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    [ERROR] [paperless.consumer] Error while consuming document SCN_0001.pdf: FileNotFoundError: [Errno 2] No such file or directory: '/tmp/ocrmypdf.io.yhk3zbv0/origin.pdf'
 | 
			
		||||
    Traceback (most recent call last):
 | 
			
		||||
      File "/app/paperless/src/paperless_tesseract/parsers.py", line 261, in parse
 | 
			
		||||
        ocrmypdf.ocr(**args)
 | 
			
		||||
      File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/api.py", line 337, in ocr
 | 
			
		||||
        return run_pipeline(options=options, plugin_manager=plugin_manager, api=True)
 | 
			
		||||
      File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_sync.py", line 385, in run_pipeline
 | 
			
		||||
        exec_concurrent(context, executor)
 | 
			
		||||
      File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_sync.py", line 302, in exec_concurrent
 | 
			
		||||
        pdf = post_process(pdf, context, executor)
 | 
			
		||||
      File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_sync.py", line 235, in post_process
 | 
			
		||||
        pdf_out = metadata_fixup(pdf_out, context)
 | 
			
		||||
      File "/usr/local/lib/python3.8/dist-packages/ocrmypdf/_pipeline.py", line 798, in metadata_fixup
 | 
			
		||||
        with pikepdf.open(context.origin) as original, pikepdf.open(working_file) as pdf:
 | 
			
		||||
      File "/usr/local/lib/python3.8/dist-packages/pikepdf/_methods.py", line 923, in open
 | 
			
		||||
        pdf = Pdf._open(
 | 
			
		||||
    FileNotFoundError: [Errno 2] No such file or directory: '/tmp/ocrmypdf.io.yhk3zbv0/origin.pdf'
 | 
			
		||||
 | 
			
		||||
This probably indicates paperless tried to consume the same file twice.  This can happen for a number of reasons,
 | 
			
		||||
depending on how documents are placed into the consume folder.  If paperless is using inotify (the default) to
 | 
			
		||||
check for documents, try adjusting the :ref:`inotify configuration <configuration-inotify>`.  If polling is enabled,
 | 
			
		||||
try adjusting the :ref:`polling configuration <configuration-polling>`.
 | 
			
		||||
 | 
			
		||||
Consumer fails waiting for file to remain unmodified.
 | 
			
		||||
#####################################################
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    [ERROR] [paperless.management.consumer] Timeout while waiting on file /usr/src/paperless/src/../consume/SCN_0001.pdf to remain unmodified.
 | 
			
		||||
 | 
			
		||||
This indicates paperless timed out while waiting for the file to be completely written to the consume folder.
 | 
			
		||||
Adjusting :ref:`polling configuration <configuration-polling>` values should resolve the issue.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    The user will need to manually move the file out of the consume folder and
 | 
			
		||||
    back in, for the initial failing file to be consumed.
 | 
			
		||||
 | 
			
		||||
Consumer fails reporting "OS reports file as busy still".
 | 
			
		||||
#########################################################
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    [WARNING] [paperless.management.consumer] Not consuming file /usr/src/paperless/src/../consume/SCN_0001.pdf: OS reports file as busy still
 | 
			
		||||
 | 
			
		||||
This indicates paperless was unable to open the file, as the OS reported the file as still being in use.  To prevent a
 | 
			
		||||
crash, paperless did not try to consume the file.  If paperless is using inotify (the default) to
 | 
			
		||||
check for documents, try adjusting the :ref:`inotify configuration <configuration-inotify>`.  If polling is enabled,
 | 
			
		||||
try adjusting the :ref:`polling configuration <configuration-polling>`.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    The user will need to manually move the file out of the consume folder and
 | 
			
		||||
    back in, for the initial failing file to be consumed.
 | 
			
		||||
 | 
			
		||||
Log reports "Creating PaperlessTask failed".
 | 
			
		||||
#########################################################
 | 
			
		||||
 | 
			
		||||
You might find messages like these in your log files:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    [ERROR] [paperless.management.consumer] Creating PaperlessTask failed: db locked
 | 
			
		||||
 | 
			
		||||
You are likely using an sqlite based installation, with an increased number of workers and are running into sqlite's concurrency limitations.
 | 
			
		||||
Uploading or consuming multiple files at once results in many workers attempting to access the database simultaneously.
 | 
			
		||||
 | 
			
		||||
Consider changing to the PostgreSQL database if you will be processing many documents at once often.  Otherwise,
 | 
			
		||||
try tweaking the ``PAPERLESS_DB_TIMEOUT`` setting to allow more time for the database to unlock.  This may have
 | 
			
		||||
minor performance implications.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
gunicorn fails to start with "is not a valid port number"
 | 
			
		||||
#########################################################
 | 
			
		||||
 | 
			
		||||
You are likely running using Kubernetes, which automatically creates an environment variable named `${serviceName}_PORT`.
 | 
			
		||||
This is the same environment variable which is used by Paperless to optionally change the port gunicorn listens on.
 | 
			
		||||
 | 
			
		||||
To fix this, set `PAPERLESS_PORT` again to your desired port, or the default of 8000.
 | 
			
		||||
							
								
								
									
										483
									
								
								docs/usage.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,483 @@
 | 
			
		||||
# Usage Overview
 | 
			
		||||
 | 
			
		||||
Paperless is an application that manages your personal documents. With
 | 
			
		||||
the help of a document scanner (see [the scanners wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Scanner-&-Software-Recommendations)), paperless transforms your wieldy physical document binders
 | 
			
		||||
into a searchable archive and provides many utilities for finding and
 | 
			
		||||
managing your documents.
 | 
			
		||||
 | 
			
		||||
## Terms and definitions
 | 
			
		||||
 | 
			
		||||
Paperless essentially consists of two different parts for managing your
 | 
			
		||||
documents:
 | 
			
		||||
 | 
			
		||||
- The _consumer_ watches a specified folder and adds all documents in
 | 
			
		||||
  that folder to paperless.
 | 
			
		||||
- The _web server_ provides a UI that you use to manage and search for
 | 
			
		||||
  your scanned documents.
 | 
			
		||||
 | 
			
		||||
Each document has a couple of fields that you can assign to them:
 | 
			
		||||
 | 
			
		||||
- A _Document_ is a piece of paper that sometimes contains valuable
 | 
			
		||||
  information.
 | 
			
		||||
- The _correspondent_ of a document is the person, institution or
 | 
			
		||||
  company that a document either originates from, or is sent to.
 | 
			
		||||
- A _tag_ is a label that you can assign to documents. Think of labels
 | 
			
		||||
  as more powerful folders: Multiple documents can be grouped together
 | 
			
		||||
  with a single tag, however, a single document can also have multiple
 | 
			
		||||
  tags. This is not possible with folders. The reason folders are not
 | 
			
		||||
  implemented in paperless is simply that tags are much more versatile
 | 
			
		||||
  than folders.
 | 
			
		||||
- A _document type_ is used to demarcate the type of a document such
 | 
			
		||||
  as letter, bank statement, invoice, contract, etc. It is used to
 | 
			
		||||
  identify what a document is about.
 | 
			
		||||
- The _date added_ of a document is the date the document was scanned
 | 
			
		||||
  into paperless. You cannot and should not change this date.
 | 
			
		||||
- The _date created_ of a document is the date the document was
 | 
			
		||||
  initially issued. This can be the date you bought a product, the
 | 
			
		||||
  date you signed a contract, or the date a letter was sent to you.
 | 
			
		||||
- The _archive serial number_ (short: ASN) of a document is the
 | 
			
		||||
  identifier of the document in your physical document binders. See
 | 
			
		||||
  `usage-recommended_workflow`{.interpreted-text role="ref"} below.
 | 
			
		||||
- The _content_ of a document is the text that was OCR'ed from the
 | 
			
		||||
  document. This text is fed into the search engine and is used for
 | 
			
		||||
  matching tags, correspondents and document types.
 | 
			
		||||
 | 
			
		||||
## Adding documents to paperless
 | 
			
		||||
 | 
			
		||||
Once you've got Paperless setup, you need to start feeding documents
 | 
			
		||||
into it. When adding documents to paperless, it will perform the
 | 
			
		||||
following operations on your documents:
 | 
			
		||||
 | 
			
		||||
1.  OCR the document, if it has no text. Digital documents usually have
 | 
			
		||||
    text, and this step will be skipped for those documents.
 | 
			
		||||
2.  Paperless will create an archivable PDF/A document from your
 | 
			
		||||
    document. If this document is coming from your scanner, it will have
 | 
			
		||||
    embedded selectable text.
 | 
			
		||||
3.  Paperless performs automatic matching of tags, correspondents and
 | 
			
		||||
    types on the document before storing it in the database.
 | 
			
		||||
 | 
			
		||||
!!! tip
 | 
			
		||||
 | 
			
		||||
    This process can be configured to fit your needs. If you don't want
 | 
			
		||||
    paperless to create archived versions for digital documents, you can
 | 
			
		||||
    configure that by configuring `PAPERLESS_OCR_MODE=skip_noarchive`.
 | 
			
		||||
    Please read the
 | 
			
		||||
    [relevant section in the documentation](/configuration#ocr).
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    No matter which options you choose, Paperless will always store the
 | 
			
		||||
    original document that it found in the consumption directory or in the
 | 
			
		||||
    mail and will never overwrite that document. Archived versions are
 | 
			
		||||
    stored alongside the original versions.
 | 
			
		||||
 | 
			
		||||
### The consumption directory
 | 
			
		||||
 | 
			
		||||
The primary method of getting documents into your database is by putting
 | 
			
		||||
them in the consumption directory. The consumer runs in an infinite
 | 
			
		||||
loop, looking for new additions to this directory. When it finds them,
 | 
			
		||||
the consumer goes about the process of parsing them with the OCR,
 | 
			
		||||
indexing what it finds, and storing it in the media directory.
 | 
			
		||||
 | 
			
		||||
Getting stuff into this directory is up to you. If you're running
 | 
			
		||||
Paperless on your local computer, you might just want to drag and drop
 | 
			
		||||
files there, but if you're running this on a server and want your
 | 
			
		||||
scanner to automatically push files to this directory, you'll need to
 | 
			
		||||
setup some sort of service to accept the files from the scanner.
 | 
			
		||||
Typically, you're looking at an FTP server like
 | 
			
		||||
[Proftpd](http://www.proftpd.org/) or a Windows folder share with
 | 
			
		||||
[Samba](http://www.samba.org/).
 | 
			
		||||
 | 
			
		||||
### Web UI Upload
 | 
			
		||||
 | 
			
		||||
The dashboard has a file drop field to upload documents to paperless.
 | 
			
		||||
Simply drag a file onto this field or select a file with the file
 | 
			
		||||
dialog. Multiple files are supported.
 | 
			
		||||
 | 
			
		||||
You can also upload documents on any other page of the web UI by
 | 
			
		||||
dragging-and-dropping files into your browser window.
 | 
			
		||||
 | 
			
		||||
### Mobile upload {#usage-mobile_upload}
 | 
			
		||||
 | 
			
		||||
The mobile app over at <https://github.com/qcasey/paperless_share>
 | 
			
		||||
allows Android users to share any documents with paperless. This can be
 | 
			
		||||
combined with any of the mobile scanning apps out there, such as Office
 | 
			
		||||
Lens.
 | 
			
		||||
 | 
			
		||||
Furthermore, there is the [Paperless
 | 
			
		||||
App](https://github.com/bauerj/paperless_app) as well, which not only
 | 
			
		||||
has document upload, but also document browsing and download features.
 | 
			
		||||
 | 
			
		||||
### IMAP (Email) {#usage-email}
 | 
			
		||||
 | 
			
		||||
You can tell paperless-ngx to consume documents from your email
 | 
			
		||||
accounts. This is a very flexible and powerful feature, if you regularly
 | 
			
		||||
received documents via mail that you need to archive. The mail consumer
 | 
			
		||||
can be configured via the frontend settings (/settings/mail) in the following
 | 
			
		||||
manner:
 | 
			
		||||
 | 
			
		||||
1.  Define e-mail accounts.
 | 
			
		||||
2.  Define mail rules for your account.
 | 
			
		||||
 | 
			
		||||
These rules perform the following:
 | 
			
		||||
 | 
			
		||||
1.  Connect to the mail server.
 | 
			
		||||
2.  Fetch all matching mails (as defined by folder, maximum age and the
 | 
			
		||||
    filters)
 | 
			
		||||
3.  Check if there are any consumable attachments.
 | 
			
		||||
4.  If so, instruct paperless to consume the attachments and optionally
 | 
			
		||||
    use the metadata provided in the rule for the new document.
 | 
			
		||||
5.  If documents were consumed from a mail, the rule action is performed
 | 
			
		||||
    on that mail.
 | 
			
		||||
 | 
			
		||||
Paperless will completely ignore mails that do not match your filters.
 | 
			
		||||
It will also only perform the action on mails that it has consumed
 | 
			
		||||
documents from.
 | 
			
		||||
 | 
			
		||||
The actions all ensure that the same mail is not consumed twice by
 | 
			
		||||
different means. These are as follows:
 | 
			
		||||
 | 
			
		||||
- **Delete:** Immediately deletes mail that paperless has consumed
 | 
			
		||||
  documents from. Use with caution.
 | 
			
		||||
- **Mark as read:** Mark consumed mail as read. Paperless will not
 | 
			
		||||
  consume documents from already read mails. If you read a mail before
 | 
			
		||||
  paperless sees it, it will be ignored.
 | 
			
		||||
- **Flag:** Sets the 'important' flag on mails with consumed
 | 
			
		||||
  documents. Paperless will not consume flagged mails.
 | 
			
		||||
- **Move to folder:** Moves consumed mails out of the way so that
 | 
			
		||||
  paperless wont consume them again.
 | 
			
		||||
- **Add custom Tag:** Adds a custom tag to mails with consumed
 | 
			
		||||
  documents (the IMAP standard calls these "keywords"). Paperless
 | 
			
		||||
  will not consume mails already tagged. Not all mail servers support
 | 
			
		||||
  this feature!
 | 
			
		||||
 | 
			
		||||
!!! warning
 | 
			
		||||
 | 
			
		||||
    The mail consumer will perform these actions on all mails it has
 | 
			
		||||
    consumed documents from. Keep in mind that the actual consumption
 | 
			
		||||
    process may fail for some reason, leaving you with missing documents in
 | 
			
		||||
    paperless.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    With the correct set of rules, you can completely automate your email
 | 
			
		||||
    documents. Create rules for every correspondent you receive digital
 | 
			
		||||
    documents from and paperless will read them automatically. The default
 | 
			
		||||
    action "mark as read" is pretty tame and will not cause any damage or
 | 
			
		||||
    data loss whatsoever.
 | 
			
		||||
 | 
			
		||||
    You can also setup a special folder in your mail account for paperless
 | 
			
		||||
    and use your favorite mail client to move to be consumed mails into that
 | 
			
		||||
    folder automatically or manually and tell paperless to move them to yet
 | 
			
		||||
    another folder after consumption. It's up to you.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    When defining a mail rule with a folder, you may need to try different
 | 
			
		||||
    characters to define how the sub-folders are separated. Common values
 | 
			
		||||
    include ".", "/" or "\|", but this varies by the mail server.
 | 
			
		||||
    Check the documentation for your mail server. In the event of an error
 | 
			
		||||
    fetching mail from a certain folder, check the Paperless logs. When a
 | 
			
		||||
    folder is not located, Paperless will attempt to list all folders found
 | 
			
		||||
    in the account to the Paperless logs.
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    Paperless will process the rules in the order defined in the admin page.
 | 
			
		||||
 | 
			
		||||
    You can define catch-all rules and have them executed last to consume
 | 
			
		||||
    any documents not matched by previous rules. Such a rule may assign an
 | 
			
		||||
    "Unknown mail document" tag to consumed documents so you can inspect
 | 
			
		||||
    them further.
 | 
			
		||||
 | 
			
		||||
Paperless is set up to check your mails every 10 minutes. This can be
 | 
			
		||||
configured on the 'Scheduled tasks' page in the admin.
 | 
			
		||||
 | 
			
		||||
### REST API
 | 
			
		||||
 | 
			
		||||
You can also submit a document using the REST API, see
 | 
			
		||||
`api-file_uploads`{.interpreted-text role="ref"} for details.
 | 
			
		||||
 | 
			
		||||
## Best practices {#basic-searching}
 | 
			
		||||
 | 
			
		||||
Paperless offers a couple tools that help you organize your document
 | 
			
		||||
collection. However, it is up to you to use them in a way that helps you
 | 
			
		||||
organize documents and find specific documents when you need them. This
 | 
			
		||||
section offers a couple ideas for managing your collection.
 | 
			
		||||
 | 
			
		||||
Document types allow you to classify documents according to what they
 | 
			
		||||
are. You can define types such as "Receipt", "Invoice", or
 | 
			
		||||
"Contract". If you used to collect all your receipts in a single
 | 
			
		||||
binder, you can recreate that system in paperless by defining a document
 | 
			
		||||
type, assigning documents to that type and then filtering by that type
 | 
			
		||||
to only see all receipts.
 | 
			
		||||
 | 
			
		||||
Not all documents need document types. Sometimes its hard to determine
 | 
			
		||||
what the type of a document is or it is hard to justify creating a
 | 
			
		||||
document type that you only need once or twice. This is okay. As long as
 | 
			
		||||
the types you define help you organize your collection in the way you
 | 
			
		||||
want, paperless is doing its job.
 | 
			
		||||
 | 
			
		||||
Tags can be used in many different ways. Think of tags are more
 | 
			
		||||
versatile folders or binders. If you have a binder for documents related
 | 
			
		||||
to university / your car or health care, you can create these binders in
 | 
			
		||||
paperless by creating tags and assigning them to relevant documents.
 | 
			
		||||
Just as with documents, you can filter the document list by tags and
 | 
			
		||||
only see documents of a certain topic.
 | 
			
		||||
 | 
			
		||||
With physical documents, you'll often need to decide which folder the
 | 
			
		||||
document belongs to. The advantage of tags over folders and binders is
 | 
			
		||||
that a single document can have multiple tags. A physical document
 | 
			
		||||
cannot magically appear in two different folders, but with tags, this is
 | 
			
		||||
entirely possible.
 | 
			
		||||
 | 
			
		||||
!!! tip
 | 
			
		||||
 | 
			
		||||
    This can be used in many different ways. One example: Imagine you're
 | 
			
		||||
    working on a particular task, such as signing up for university. Usually
 | 
			
		||||
    you'll need to collect a bunch of different documents that are already
 | 
			
		||||
    sorted into various folders. With the tag system of paperless, you can
 | 
			
		||||
    create a new group of documents that are relevant to this task without
 | 
			
		||||
    destroying the already existing organization. When you're done with the
 | 
			
		||||
    task, you could delete the tag again, which would be equal to sorting
 | 
			
		||||
    documents back into the folder they belong into. Or keep the tag, up to
 | 
			
		||||
    you.
 | 
			
		||||
 | 
			
		||||
All of the logic above applies to correspondents as well. Attach them to
 | 
			
		||||
documents if you feel that they help you organize your collection.
 | 
			
		||||
 | 
			
		||||
When you've started organizing your documents, create a couple saved
 | 
			
		||||
views for document collections you regularly access. This is equal to
 | 
			
		||||
having labeled physical binders on your desk, except that these saved
 | 
			
		||||
views are dynamic and simply update themselves as you add documents to
 | 
			
		||||
the system.
 | 
			
		||||
 | 
			
		||||
Here are a couple examples of tags and types that you could use in your
 | 
			
		||||
collection.
 | 
			
		||||
 | 
			
		||||
- An `inbox` tag for newly added documents that you haven't manually
 | 
			
		||||
  edited yet.
 | 
			
		||||
- A tag `car` for everything car related (repairs, registration,
 | 
			
		||||
  insurance, etc)
 | 
			
		||||
- A tag `todo` for documents that you still need to do something with,
 | 
			
		||||
  such as reply, or perform some task online.
 | 
			
		||||
- A tag `bank account x` for all bank statement related to that
 | 
			
		||||
  account.
 | 
			
		||||
- A tag `mail` for anything that you added to paperless via its mail
 | 
			
		||||
  processing capabilities.
 | 
			
		||||
- A tag `missing_metadata` when you still need to add some metadata to
 | 
			
		||||
  a document, but can't or don't want to do this right now.
 | 
			
		||||
 | 
			
		||||
## Searching {#basic-usage_searching}
 | 
			
		||||
 | 
			
		||||
Paperless offers an extensive searching mechanism that is designed to
 | 
			
		||||
allow you to quickly find a document you're looking for (for example,
 | 
			
		||||
that thing that just broke and you bought a couple months ago, that
 | 
			
		||||
contract you signed 8 years ago).
 | 
			
		||||
 | 
			
		||||
When you search paperless for a document, it tries to match this query
 | 
			
		||||
against your documents. Paperless will look for matching documents by
 | 
			
		||||
inspecting their content, title, correspondent, type and tags. Paperless
 | 
			
		||||
returns a scored list of results, so that documents matching your query
 | 
			
		||||
better will appear further up in the search results.
 | 
			
		||||
 | 
			
		||||
By default, paperless returns only documents which contain all words
 | 
			
		||||
typed in the search bar. However, paperless also offers advanced search
 | 
			
		||||
syntax if you want to drill down the results further.
 | 
			
		||||
 | 
			
		||||
Matching documents with logical expressions:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
shopname AND (product1 OR product2)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Matching specific tags, correspondents or types:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
type:invoice tag:unpaid
 | 
			
		||||
correspondent:university certificate
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Matching dates:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
created:[2005 to 2009]
 | 
			
		||||
added:yesterday
 | 
			
		||||
modified:today
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Matching inexact words:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
produ*name
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
!!! note
 | 
			
		||||
 | 
			
		||||
    Inexact terms are hard for search indexes. These queries might take a
 | 
			
		||||
    while to execute. That's why paperless offers auto complete and query
 | 
			
		||||
    correction.
 | 
			
		||||
 | 
			
		||||
All of these constructs can be combined as you see fit. If you want to
 | 
			
		||||
learn more about the query language used by paperless, paperless uses
 | 
			
		||||
Whoosh's default query language. Head over to [Whoosh query
 | 
			
		||||
language](https://whoosh.readthedocs.io/en/latest/querylang.html). For
 | 
			
		||||
details on what date parsing utilities are available, see [Date
 | 
			
		||||
parsing](https://whoosh.readthedocs.io/en/latest/dates.html#parsing-date-queries).
 | 
			
		||||
 | 
			
		||||
## The recommended workflow {#usage-recommended_workflow}
 | 
			
		||||
 | 
			
		||||
Once you have familiarized yourself with paperless and are ready to use
 | 
			
		||||
it for all your documents, the recommended workflow for managing your
 | 
			
		||||
documents is as follows. This workflow also takes into account that some
 | 
			
		||||
documents have to be kept in physical form, but still ensures that you
 | 
			
		||||
get all the advantages for these documents as well.
 | 
			
		||||
 | 
			
		||||
The following diagram shows how easy it is to manage your documents.
 | 
			
		||||
 | 
			
		||||
{width=400}
 | 
			
		||||
 | 
			
		||||
### Preparations in paperless
 | 
			
		||||
 | 
			
		||||
- Create an inbox tag that gets assigned to all new documents.
 | 
			
		||||
- Create a TODO tag.
 | 
			
		||||
 | 
			
		||||
### Processing of the physical documents
 | 
			
		||||
 | 
			
		||||
Keep a physical inbox. Whenever you receive a document that you need to
 | 
			
		||||
archive, put it into your inbox. Regularly, do the following for all
 | 
			
		||||
documents in your inbox:
 | 
			
		||||
 | 
			
		||||
1.  For each document, decide if you need to keep the document in
 | 
			
		||||
    physical form. This applies to certain important documents, such as
 | 
			
		||||
    contracts and certificates.
 | 
			
		||||
2.  If you need to keep the document, write a running number on the
 | 
			
		||||
    document before scanning, starting at one and counting upwards. This
 | 
			
		||||
    is the archive serial number, or ASN in short.
 | 
			
		||||
3.  Scan the document.
 | 
			
		||||
4.  If the document has an ASN assigned, store it in a _single_ binder,
 | 
			
		||||
    sorted by ASN. Don't order this binder in any other way.
 | 
			
		||||
5.  If the document has no ASN, throw it away. Yay!
 | 
			
		||||
 | 
			
		||||
Over time, you will notice that your physical binder will fill up. If it
 | 
			
		||||
is full, label the binder with the range of ASNs in this binder (i.e.,
 | 
			
		||||
"Documents 1 to 343"), store the binder in your cellar or elsewhere,
 | 
			
		||||
and start a new binder.
 | 
			
		||||
 | 
			
		||||
The idea behind this process is that you will never have to use the
 | 
			
		||||
physical binders to find a document. If you need a specific physical
 | 
			
		||||
document, you may find this document by:
 | 
			
		||||
 | 
			
		||||
1.  Searching in paperless for the document.
 | 
			
		||||
2.  Identify the ASN of the document, since it appears on the scan.
 | 
			
		||||
3.  Grab the relevant document binder and get the document. This is easy
 | 
			
		||||
    since they are sorted by ASN.
 | 
			
		||||
 | 
			
		||||
### Processing of documents in paperless
 | 
			
		||||
 | 
			
		||||
Once you have scanned in a document, proceed in paperless as follows.
 | 
			
		||||
 | 
			
		||||
1.  If the document has an ASN, assign the ASN to the document.
 | 
			
		||||
2.  Assign a correspondent to the document (i.e., your employer, bank,
 | 
			
		||||
    etc) This isn't strictly necessary but helps in finding a document
 | 
			
		||||
    when you need it.
 | 
			
		||||
3.  Assign a document type (i.e., invoice, bank statement, etc) to the
 | 
			
		||||
    document This isn't strictly necessary but helps in finding a
 | 
			
		||||
    document when you need it.
 | 
			
		||||
4.  Assign a proper title to the document (the name of an item you
 | 
			
		||||
    bought, the subject of the letter, etc)
 | 
			
		||||
5.  Check that the date of the document is correct. Paperless tries to
 | 
			
		||||
    read the date from the content of the document, but this fails
 | 
			
		||||
    sometimes if the OCR is bad or multiple dates appear on the
 | 
			
		||||
    document.
 | 
			
		||||
6.  Remove inbox tags from the documents.
 | 
			
		||||
 | 
			
		||||
!!! tip
 | 
			
		||||
 | 
			
		||||
    You can setup manual matching rules for your correspondents and tags and
 | 
			
		||||
    paperless will assign them automatically. After consuming a couple
 | 
			
		||||
    documents, you can even ask paperless to *learn* when to assign tags and
 | 
			
		||||
    correspondents by itself. For details on this feature, see
 | 
			
		||||
    `advanced-matching`{.interpreted-text role="ref"}.
 | 
			
		||||
 | 
			
		||||
### Task management
 | 
			
		||||
 | 
			
		||||
Some documents require attention and require you to act on the document.
 | 
			
		||||
You may take two different approaches to handle these documents based on
 | 
			
		||||
how regularly you intend to scan documents and use paperless.
 | 
			
		||||
 | 
			
		||||
- If you scan and process your documents in paperless regularly,
 | 
			
		||||
  assign a TODO tag to all scanned documents that you need to process.
 | 
			
		||||
  Create a saved view on the dashboard that shows all documents with
 | 
			
		||||
  this tag.
 | 
			
		||||
- If you do not scan documents regularly and use paperless solely for
 | 
			
		||||
  archiving, create a physical todo box next to your physical inbox
 | 
			
		||||
  and put documents you need to process in the TODO box. When you
 | 
			
		||||
  performed the task associated with the document, move it to the
 | 
			
		||||
  inbox.
 | 
			
		||||
 | 
			
		||||
## Architectue
 | 
			
		||||
 | 
			
		||||
Paperless-ngx consists of the following components:
 | 
			
		||||
 | 
			
		||||
- **The webserver:** This serves the administration pages, the API,
 | 
			
		||||
  and the new frontend. This is the main tool you'll be using to interact
 | 
			
		||||
  with paperless. You may start the webserver directly with
 | 
			
		||||
 | 
			
		||||
  ```shell-session
 | 
			
		||||
  $ cd /path/to/paperless/src/
 | 
			
		||||
  $ gunicorn -c ../gunicorn.conf.py paperless.wsgi
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
  or by any other means such as Apache `mod_wsgi`.
 | 
			
		||||
 | 
			
		||||
- **The consumer:** This is what watches your consumption folder for
 | 
			
		||||
  documents. However, the consumer itself does not really consume your
 | 
			
		||||
  documents. Now it notifies a task processor that a new file is ready
 | 
			
		||||
  for consumption. I suppose it should be named differently. This was
 | 
			
		||||
  also used to check your emails, but that's now done elsewhere as
 | 
			
		||||
  well.
 | 
			
		||||
 | 
			
		||||
  Start the consumer with the management command `document_consumer`:
 | 
			
		||||
 | 
			
		||||
  ```shell-session
 | 
			
		||||
  $ cd /path/to/paperless/src/
 | 
			
		||||
  $ python3 manage.py document_consumer
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
- **The task processor:** Paperless relies on [Celery - Distributed
 | 
			
		||||
  Task Queue](https://docs.celeryq.dev/en/stable/index.html) for doing
 | 
			
		||||
  most of the heavy lifting. This is a task queue that accepts tasks
 | 
			
		||||
  from multiple sources and processes these in parallel. It also comes
 | 
			
		||||
  with a scheduler that executes certain commands periodically.
 | 
			
		||||
 | 
			
		||||
  This task processor is responsible for:
 | 
			
		||||
 | 
			
		||||
  - Consuming documents. When the consumer finds new documents, it
 | 
			
		||||
    notifies the task processor to start a consumption task.
 | 
			
		||||
  - The task processor also performs the consumption of any
 | 
			
		||||
    documents you upload through the web interface.
 | 
			
		||||
  - Consuming emails. It periodically checks your configured
 | 
			
		||||
    accounts for new emails and notifies the task processor to
 | 
			
		||||
    consume the attachment of an email.
 | 
			
		||||
  - Maintaining the search index and the automatic matching
 | 
			
		||||
    algorithm. These are things that paperless needs to do from time
 | 
			
		||||
    to time in order to operate properly.
 | 
			
		||||
 | 
			
		||||
  This allows paperless to process multiple documents from your
 | 
			
		||||
  consumption folder in parallel! On a modern multi core system, this
 | 
			
		||||
  makes the consumption process with full OCR blazingly fast.
 | 
			
		||||
 | 
			
		||||
  The task processor comes with a built-in admin interface that you
 | 
			
		||||
  can use to check whenever any of the tasks fail and inspect the
 | 
			
		||||
  errors (i.e., wrong email credentials, errors during consuming a
 | 
			
		||||
  specific file, etc).
 | 
			
		||||
 | 
			
		||||
- A [redis](https://redis.io/) message broker: This is a really
 | 
			
		||||
  lightweight service that is responsible for getting the tasks from
 | 
			
		||||
  the webserver and the consumer to the task scheduler. These run in a
 | 
			
		||||
  different process (maybe even on different machines!), and
 | 
			
		||||
  therefore, this is necessary.
 | 
			
		||||
 | 
			
		||||
- Optional: A database server. Paperless supports PostgreSQL, MariaDB
 | 
			
		||||
  and SQLite for storing its data.
 | 
			
		||||
@ -1,420 +0,0 @@
 | 
			
		||||
**************
 | 
			
		||||
Usage Overview
 | 
			
		||||
**************
 | 
			
		||||
 | 
			
		||||
Paperless is an application that manages your personal documents. With
 | 
			
		||||
the help of a document scanner (see :ref:`scanners`), paperless transforms
 | 
			
		||||
your wieldy physical document binders into a searchable archive and
 | 
			
		||||
provides many utilities for finding and managing your documents.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Terms and definitions
 | 
			
		||||
#####################
 | 
			
		||||
 | 
			
		||||
Paperless essentially consists of two different parts for managing your
 | 
			
		||||
documents:
 | 
			
		||||
 | 
			
		||||
* The *consumer* watches a specified folder and adds all documents in that
 | 
			
		||||
  folder to paperless.
 | 
			
		||||
* The *web server* provides a UI that you use to manage and search for your
 | 
			
		||||
  scanned documents.
 | 
			
		||||
 | 
			
		||||
Each document has a couple of fields that you can assign to them:
 | 
			
		||||
 | 
			
		||||
* A *Document* is a piece of paper that sometimes contains valuable
 | 
			
		||||
  information.
 | 
			
		||||
* The *correspondent* of a document is the person, institution or company that
 | 
			
		||||
  a document either originates from, or is sent to.
 | 
			
		||||
* A *tag* is a label that you can assign to documents. Think of labels as more
 | 
			
		||||
  powerful folders: Multiple documents can be grouped together with a single
 | 
			
		||||
  tag, however, a single document can also have multiple tags. This is not
 | 
			
		||||
  possible with folders. The reason folders are not implemented in paperless
 | 
			
		||||
  is simply that tags are much more versatile than folders.
 | 
			
		||||
* A *document type* is used to demarcate the type of a document such as letter,
 | 
			
		||||
  bank statement, invoice, contract, etc. It is used to identify what a document
 | 
			
		||||
  is about.
 | 
			
		||||
* The *date added* of a document is the date the document was scanned into
 | 
			
		||||
  paperless. You cannot and should not change this date.
 | 
			
		||||
* The *date created* of a document is the date the document was initially issued.
 | 
			
		||||
  This can be the date you bought a product, the date you signed a contract, or
 | 
			
		||||
  the date a letter was sent to you.
 | 
			
		||||
* The *archive serial number* (short: ASN) of a document is the identifier of
 | 
			
		||||
  the document in your physical document binders. See
 | 
			
		||||
  :ref:`usage-recommended_workflow` below.
 | 
			
		||||
* The *content* of a document is the text that was OCR'ed from the document.
 | 
			
		||||
  This text is fed into the search engine and is used for matching tags,
 | 
			
		||||
  correspondents and document types.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Frontend overview
 | 
			
		||||
#################
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
    TBD. Add some fancy screenshots!
 | 
			
		||||
 | 
			
		||||
Adding documents to paperless
 | 
			
		||||
#############################
 | 
			
		||||
 | 
			
		||||
Once you've got Paperless setup, you need to start feeding documents into it.
 | 
			
		||||
When adding documents to paperless, it will perform the following operations on
 | 
			
		||||
your documents:
 | 
			
		||||
 | 
			
		||||
1.  OCR the document, if it has no text. Digital documents usually have text,
 | 
			
		||||
    and this step will be skipped for those documents.
 | 
			
		||||
2.  Paperless will create an archivable PDF/A document from your document.
 | 
			
		||||
    If this document is coming from your scanner, it will have embedded selectable text.
 | 
			
		||||
3.  Paperless performs automatic matching of tags, correspondents and types on the
 | 
			
		||||
    document before storing it in the database.
 | 
			
		||||
 | 
			
		||||
.. hint::
 | 
			
		||||
 | 
			
		||||
    This process can be configured to fit your needs. If you don't want paperless
 | 
			
		||||
    to create archived versions for digital documents, you can configure that by
 | 
			
		||||
    configuring ``PAPERLESS_OCR_MODE=skip_noarchive``. Please read the
 | 
			
		||||
    :ref:`relevant section in the documentation <configuration-ocr>`.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    No matter which options you choose, Paperless will always store the original
 | 
			
		||||
    document that it found in the consumption directory or in the mail and
 | 
			
		||||
    will never overwrite that document. Archived versions are stored alongside the
 | 
			
		||||
    original versions.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The consumption directory
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
The primary method of getting documents into your database is by putting them in
 | 
			
		||||
the consumption directory.  The consumer runs in an infinite loop, looking for new
 | 
			
		||||
additions to this directory. When it finds them, the consumer goes about the process
 | 
			
		||||
of parsing them with the OCR, indexing what it finds, and storing it in the media directory.
 | 
			
		||||
 | 
			
		||||
Getting stuff into this directory is up to you.  If you're running Paperless
 | 
			
		||||
on your local computer, you might just want to drag and drop files there, but if
 | 
			
		||||
you're running this on a server and want your scanner to automatically push
 | 
			
		||||
files to this directory, you'll need to setup some sort of service to accept the
 | 
			
		||||
files from the scanner.  Typically, you're looking at an FTP server like
 | 
			
		||||
`Proftpd`_ or a Windows folder share with `Samba`_.
 | 
			
		||||
 | 
			
		||||
.. _Proftpd: http://www.proftpd.org/
 | 
			
		||||
.. _Samba: http://www.samba.org/
 | 
			
		||||
 | 
			
		||||
.. TODO: hyperref to configuration of the location of this magic folder.
 | 
			
		||||
 | 
			
		||||
Web UI Upload
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
The dashboard has a file drop field to upload documents to paperless. Simply drag a file
 | 
			
		||||
onto this field or select a file with the file dialog. Multiple files are supported.
 | 
			
		||||
 | 
			
		||||
You can also upload documents on any other page of the web UI by dragging-and-dropping
 | 
			
		||||
files into your browser window.
 | 
			
		||||
 | 
			
		||||
.. _usage-mobile_upload:
 | 
			
		||||
 | 
			
		||||
Mobile upload
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
The mobile app over at `<https://github.com/qcasey/paperless_share>`_ allows Android users
 | 
			
		||||
to share any documents with paperless. This can be combined with any of the mobile
 | 
			
		||||
scanning apps out there, such as Office Lens.
 | 
			
		||||
 | 
			
		||||
Furthermore, there is the  `Paperless App <https://github.com/bauerj/paperless_app>`_ as well,
 | 
			
		||||
which not only has document upload, but also document browsing and download features.
 | 
			
		||||
 | 
			
		||||
.. _usage-email:
 | 
			
		||||
 | 
			
		||||
IMAP (Email)
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
You can tell paperless-ngx to consume documents from your email accounts.
 | 
			
		||||
This is a very flexible and powerful feature, if you regularly received documents
 | 
			
		||||
via mail that you need to archive. The mail consumer can be configured by using the
 | 
			
		||||
admin interface in the following manner:
 | 
			
		||||
 | 
			
		||||
1.  Define e-mail accounts.
 | 
			
		||||
2.  Define mail rules for your account.
 | 
			
		||||
 | 
			
		||||
These rules perform the following:
 | 
			
		||||
 | 
			
		||||
1.  Connect to the mail server.
 | 
			
		||||
2.  Fetch all matching mails (as defined by folder, maximum age and the filters)
 | 
			
		||||
3.  Check if there are any consumable attachments.
 | 
			
		||||
4.  If so, instruct paperless to consume the attachments and optionally
 | 
			
		||||
    use the metadata provided in the rule for the new document.
 | 
			
		||||
5.  If documents were consumed from a mail, the rule action is performed
 | 
			
		||||
    on that mail.
 | 
			
		||||
 | 
			
		||||
Paperless will completely ignore mails that do not match your filters. It will also
 | 
			
		||||
only perform the action on mails that it has consumed documents from.
 | 
			
		||||
 | 
			
		||||
The actions all ensure that the same mail is not consumed twice by different means.
 | 
			
		||||
These are as follows:
 | 
			
		||||
 | 
			
		||||
*   **Delete:** Immediately deletes mail that paperless has consumed documents from.
 | 
			
		||||
    Use with caution.
 | 
			
		||||
*   **Mark as read:** Mark consumed mail as read. Paperless will not consume documents
 | 
			
		||||
    from already read mails. If you read a mail before paperless sees it, it will be
 | 
			
		||||
    ignored.
 | 
			
		||||
*   **Flag:** Sets the 'important' flag on mails with consumed documents. Paperless
 | 
			
		||||
    will not consume flagged mails.
 | 
			
		||||
*   **Move to folder:** Moves consumed mails out of the way so that paperless wont
 | 
			
		||||
    consume them again.
 | 
			
		||||
*   **Add custom Tag:** Adds a custom tag to mails with consumed documents (the IMAP
 | 
			
		||||
    standard calls these "keywords"). Paperless will not consume mails already tagged.
 | 
			
		||||
    Not all mail servers support this feature!
 | 
			
		||||
 | 
			
		||||
.. caution::
 | 
			
		||||
 | 
			
		||||
    The mail consumer will perform these actions on all mails it has consumed
 | 
			
		||||
    documents from. Keep in mind that the actual consumption process may fail
 | 
			
		||||
    for some reason, leaving you with missing documents in paperless.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    With the correct set of rules, you can completely automate your email documents.
 | 
			
		||||
    Create rules for every correspondent you receive digital documents from and
 | 
			
		||||
    paperless will read them automatically. The default action "mark as read" is
 | 
			
		||||
    pretty tame and will not cause any damage or data loss whatsoever.
 | 
			
		||||
 | 
			
		||||
    You can also setup a special folder in your mail account for paperless and use
 | 
			
		||||
    your favorite mail client to move to be consumed mails into that folder
 | 
			
		||||
    automatically or manually and tell paperless to move them to yet another folder
 | 
			
		||||
    after consumption. It's up to you.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    When defining a mail rule with a folder, you may need to try different characters to
 | 
			
		||||
    define how the sub-folders are separated.  Common values include ".", "/" or "|", but
 | 
			
		||||
    this varies by the mail server.  Check the documentation for your mail server.  In the
 | 
			
		||||
    event of an error fetching mail from a certain folder, check the Paperless logs.  When
 | 
			
		||||
    a folder is not located, Paperless will attempt to list all folders found in the account
 | 
			
		||||
    to the Paperless logs.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
    Paperless will process the rules in the order defined in the admin page.
 | 
			
		||||
 | 
			
		||||
    You can define catch-all rules and have them executed last to consume
 | 
			
		||||
    any documents not matched by previous rules. Such a rule may assign an "Unknown
 | 
			
		||||
    mail document" tag to consumed documents so you can inspect them further.
 | 
			
		||||
 | 
			
		||||
Paperless is set up to check your mails every 10 minutes. This can be configured on the
 | 
			
		||||
'Scheduled tasks' page in the admin.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
REST API
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
You can also submit a document using the REST API, see :ref:`api-file_uploads` for details.
 | 
			
		||||
 | 
			
		||||
.. _basic-searching:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Best practices
 | 
			
		||||
##############
 | 
			
		||||
 | 
			
		||||
Paperless offers a couple tools that help you organize your document collection. However,
 | 
			
		||||
it is up to you to use them in a way that helps you organize documents and find specific
 | 
			
		||||
documents when you need them. This section offers a couple ideas for managing your collection.
 | 
			
		||||
 | 
			
		||||
Document types allow you to classify documents according to what they are. You can define
 | 
			
		||||
types such as "Receipt", "Invoice", or "Contract". If you used to collect all your receipts
 | 
			
		||||
in a single binder, you can recreate that system in paperless by defining a document type,
 | 
			
		||||
assigning documents to that type and then filtering by that type to only see all receipts.
 | 
			
		||||
 | 
			
		||||
Not all documents need document types. Sometimes its hard to determine what the type of a
 | 
			
		||||
document is or it is hard to justify creating a document type that you only need once or twice.
 | 
			
		||||
This is okay. As long as the types you define help you organize your collection in the way
 | 
			
		||||
you want, paperless is doing its job.
 | 
			
		||||
 | 
			
		||||
Tags can be used in many different ways. Think of tags are more versatile folders or binders.
 | 
			
		||||
If you have a binder for documents related to university / your car or health care, you can
 | 
			
		||||
create these binders in paperless by creating tags and assigning them to relevant documents.
 | 
			
		||||
Just as with documents, you can filter the document list by tags and only see documents of
 | 
			
		||||
a certain topic.
 | 
			
		||||
 | 
			
		||||
With physical documents, you'll often need to decide which folder the document belongs to.
 | 
			
		||||
The advantage of tags over folders and binders is that a single document can have multiple
 | 
			
		||||
tags. A physical document cannot magically appear in two different folders, but with tags,
 | 
			
		||||
this is entirely possible.
 | 
			
		||||
 | 
			
		||||
.. hint::
 | 
			
		||||
 | 
			
		||||
  This can be used in many different ways. One example: Imagine you're working on a particular
 | 
			
		||||
  task, such as signing up for university. Usually you'll need to collect a bunch of different
 | 
			
		||||
  documents that are already sorted into various folders. With the tag system of paperless,
 | 
			
		||||
  you can create a new group of documents that are relevant to this task without destroying
 | 
			
		||||
  the already existing organization. When you're done with the task, you could delete the
 | 
			
		||||
  tag again, which would be equal to sorting documents back into the folder they belong into.
 | 
			
		||||
  Or keep the tag, up to you.
 | 
			
		||||
 | 
			
		||||
All of the logic above applies to correspondents as well. Attach them to documents if you
 | 
			
		||||
feel that they help you organize your collection.
 | 
			
		||||
 | 
			
		||||
When you've started organizing your documents, create a couple saved views for document collections
 | 
			
		||||
you regularly access. This is equal to having labeled physical binders on your desk, except
 | 
			
		||||
that these saved views are dynamic and simply update themselves as you add documents to the system.
 | 
			
		||||
 | 
			
		||||
Here are a couple examples of tags and types that you could use in your collection.
 | 
			
		||||
 | 
			
		||||
* An ``inbox`` tag for newly added documents that you haven't manually edited yet.
 | 
			
		||||
* A tag ``car`` for everything car related (repairs, registration, insurance, etc)
 | 
			
		||||
* A tag ``todo`` for documents that you still need to do something with, such as reply, or
 | 
			
		||||
  perform some task online.
 | 
			
		||||
* A tag ``bank account x`` for all bank statement related to that account.
 | 
			
		||||
* A tag ``mail`` for anything that you added to paperless via its mail processing capabilities.
 | 
			
		||||
* A tag ``missing_metadata`` when you still need to add some metadata to a document, but can't
 | 
			
		||||
  or don't want to do this right now.
 | 
			
		||||
 | 
			
		||||
.. _basic-usage_searching:
 | 
			
		||||
 | 
			
		||||
Searching
 | 
			
		||||
#########
 | 
			
		||||
 | 
			
		||||
Paperless offers an extensive searching mechanism that is designed to allow you to quickly
 | 
			
		||||
find a document you're looking for (for example, that thing that just broke and you bought
 | 
			
		||||
a couple months ago, that contract you signed 8 years ago).
 | 
			
		||||
 | 
			
		||||
When you search paperless for a document, it tries to match this query against your documents.
 | 
			
		||||
Paperless will look for matching documents by inspecting their content, title, correspondent,
 | 
			
		||||
type and tags. Paperless returns a scored list of results, so that documents matching your query
 | 
			
		||||
better will appear further up in the search results.
 | 
			
		||||
 | 
			
		||||
By default, paperless returns only documents which contain all words typed in the search bar.
 | 
			
		||||
However, paperless also offers advanced search syntax if you want to drill down the results
 | 
			
		||||
further.
 | 
			
		||||
 | 
			
		||||
Matching documents with logical expressions:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
  shopname AND (product1 OR product2)
 | 
			
		||||
 | 
			
		||||
Matching specific tags, correspondents or types:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
  type:invoice tag:unpaid
 | 
			
		||||
  correspondent:university certificate
 | 
			
		||||
 | 
			
		||||
Matching dates:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
  created:[2005 to 2009]
 | 
			
		||||
  added:yesterday
 | 
			
		||||
  modified:today
 | 
			
		||||
 | 
			
		||||
Matching inexact words:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
  produ*name
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
 | 
			
		||||
  Inexact terms are hard for search indexes. These queries might take a while to execute. That's why paperless offers
 | 
			
		||||
  auto complete and query correction.
 | 
			
		||||
 | 
			
		||||
All of these constructs can be combined as you see fit.
 | 
			
		||||
If you want to learn more about the query language used by paperless, paperless uses Whoosh's default query language.
 | 
			
		||||
Head over to `Whoosh query language <https://whoosh.readthedocs.io/en/latest/querylang.html>`_.
 | 
			
		||||
For details on what date parsing utilities are available, see
 | 
			
		||||
`Date parsing <https://whoosh.readthedocs.io/en/latest/dates.html#parsing-date-queries>`_.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. _usage-recommended_workflow:
 | 
			
		||||
 | 
			
		||||
The recommended workflow
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
Once you have familiarized yourself with paperless and are ready to use it
 | 
			
		||||
for all your documents, the recommended workflow for managing your documents
 | 
			
		||||
is as follows. This workflow also takes into account that some documents
 | 
			
		||||
have to be kept in physical form, but still ensures that you get all the
 | 
			
		||||
advantages for these documents as well.
 | 
			
		||||
 | 
			
		||||
The following diagram shows how easy it is to manage your documents.
 | 
			
		||||
 | 
			
		||||
.. image:: _static/recommended_workflow.png
 | 
			
		||||
 | 
			
		||||
Preparations in paperless
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
* Create an inbox tag that gets assigned to all new documents.
 | 
			
		||||
* Create a TODO tag.
 | 
			
		||||
 | 
			
		||||
Processing of the physical documents
 | 
			
		||||
====================================
 | 
			
		||||
 | 
			
		||||
Keep a physical inbox. Whenever you receive a document that you need to
 | 
			
		||||
archive, put it into your inbox. Regularly, do the following for all documents
 | 
			
		||||
in your inbox:
 | 
			
		||||
 | 
			
		||||
1.  For each document, decide if you need to keep the document in physical
 | 
			
		||||
    form. This applies to certain important documents, such as contracts and
 | 
			
		||||
    certificates.
 | 
			
		||||
2.  If you need to keep the document, write a running number on the document
 | 
			
		||||
    before scanning, starting at one and counting upwards. This is the archive
 | 
			
		||||
    serial number, or ASN in short.
 | 
			
		||||
3.  Scan the document.
 | 
			
		||||
4.  If the document has an ASN assigned, store it in a *single* binder, sorted
 | 
			
		||||
    by ASN. Don't order this binder in any other way.
 | 
			
		||||
5.  If the document has no ASN, throw it away. Yay!
 | 
			
		||||
 | 
			
		||||
Over time, you will notice that your physical binder will fill up. If it is
 | 
			
		||||
full, label the binder with the range of ASNs in this binder (i.e., "Documents
 | 
			
		||||
1 to 343"), store the binder in your cellar or elsewhere, and start a new
 | 
			
		||||
binder.
 | 
			
		||||
 | 
			
		||||
The idea behind this process is that you will never have to use the physical
 | 
			
		||||
binders to find a document. If you need a specific physical document, you
 | 
			
		||||
may find this document by:
 | 
			
		||||
 | 
			
		||||
1.  Searching in paperless for the document.
 | 
			
		||||
2.  Identify the ASN of the document, since it appears on the scan.
 | 
			
		||||
3.  Grab the relevant document binder and get the document. This is easy since
 | 
			
		||||
    they are sorted by ASN.
 | 
			
		||||
 | 
			
		||||
Processing of documents in paperless
 | 
			
		||||
====================================
 | 
			
		||||
 | 
			
		||||
Once you have scanned in a document, proceed in paperless as follows.
 | 
			
		||||
 | 
			
		||||
1.  If the document has an ASN, assign the ASN to the document.
 | 
			
		||||
2.  Assign a correspondent to the document (i.e., your employer, bank, etc)
 | 
			
		||||
    This isn't strictly necessary but helps in finding a document when you need
 | 
			
		||||
    it.
 | 
			
		||||
3.  Assign a document type (i.e., invoice, bank statement, etc) to the document
 | 
			
		||||
    This isn't strictly necessary but helps in finding a document when you need
 | 
			
		||||
    it.
 | 
			
		||||
4.  Assign a proper title to the document (the name of an item you bought, the
 | 
			
		||||
    subject of the letter, etc)
 | 
			
		||||
5.  Check that the date of the document is correct. Paperless tries to read
 | 
			
		||||
    the date from the content of the document, but this fails sometimes if the
 | 
			
		||||
    OCR is bad or multiple dates appear on the document.
 | 
			
		||||
6.  Remove inbox tags from the documents.
 | 
			
		||||
 | 
			
		||||
.. hint::
 | 
			
		||||
 | 
			
		||||
    You can setup manual matching rules for your correspondents and tags and
 | 
			
		||||
    paperless will assign them automatically. After consuming a couple documents,
 | 
			
		||||
    you can even ask paperless to *learn* when to assign tags and correspondents
 | 
			
		||||
    by itself. For details on this feature, see :ref:`advanced-matching`.
 | 
			
		||||
 | 
			
		||||
Task management
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
Some documents require attention and require you to act on the document. You
 | 
			
		||||
may take two different approaches to handle these documents based on how
 | 
			
		||||
regularly you intend to scan documents and use paperless.
 | 
			
		||||
 | 
			
		||||
* If you scan and process your documents in paperless regularly, assign a
 | 
			
		||||
  TODO tag to all scanned documents that you need to process. Create a saved
 | 
			
		||||
  view on the dashboard that shows all documents with this tag.
 | 
			
		||||
* If you do not scan documents regularly and use paperless solely for archiving,
 | 
			
		||||
  create a physical todo box next to your physical inbox and put documents you
 | 
			
		||||
  need to process in the TODO box. When you performed the task associated with
 | 
			
		||||
  the document, move it to the inbox.
 | 
			
		||||
							
								
								
									
										60
									
								
								mkdocs.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,60 @@
 | 
			
		||||
site_name: Paperless-ngx
 | 
			
		||||
theme:
 | 
			
		||||
  name: material
 | 
			
		||||
  logo: assets/logo.svg
 | 
			
		||||
  font:
 | 
			
		||||
    text: Roboto
 | 
			
		||||
    code: Roboto Mono
 | 
			
		||||
  palette:
 | 
			
		||||
    # Palette toggle for light mode
 | 
			
		||||
    - media: "(prefers-color-scheme: light)"
 | 
			
		||||
      scheme: default
 | 
			
		||||
      toggle:
 | 
			
		||||
        icon: material/brightness-7
 | 
			
		||||
        name: Switch to dark mode
 | 
			
		||||
 | 
			
		||||
    # Palette toggle for dark mode
 | 
			
		||||
    - media: "(prefers-color-scheme: dark)"
 | 
			
		||||
      scheme: slate
 | 
			
		||||
      toggle:
 | 
			
		||||
        icon: material/brightness-4
 | 
			
		||||
        name: Switch to light mode
 | 
			
		||||
  features:
 | 
			
		||||
    - navigation.tabs
 | 
			
		||||
    - navigation.top
 | 
			
		||||
    - toc.integrate
 | 
			
		||||
  icon:
 | 
			
		||||
    repo: fontawesome/brands/github
 | 
			
		||||
repo_url: https://github.com/paperless-ngx/paperless-ngx
 | 
			
		||||
extra_css:
 | 
			
		||||
  - assets/extra.css
 | 
			
		||||
markdown_extensions:
 | 
			
		||||
  - attr_list
 | 
			
		||||
  - md_in_html
 | 
			
		||||
  - def_list
 | 
			
		||||
  - admonition
 | 
			
		||||
  - tables
 | 
			
		||||
  - pymdownx.highlight:
 | 
			
		||||
      anchor_linenums: true
 | 
			
		||||
  - pymdownx.superfences
 | 
			
		||||
nav:
 | 
			
		||||
    - index.md
 | 
			
		||||
    - setup.md
 | 
			
		||||
    - 'Basic Usage': usage.md
 | 
			
		||||
    - configuration.md
 | 
			
		||||
    - administration.md
 | 
			
		||||
    - advanced_usage.md
 | 
			
		||||
    - 'REST API': api.md
 | 
			
		||||
    - development.md
 | 
			
		||||
    - 'FAQs': faq.md
 | 
			
		||||
    - troubleshooting.md
 | 
			
		||||
    - changelog.md
 | 
			
		||||
copyright: Copyright © 2016 - 2022 Daniel Quinn, Jonas Winkler, and the Paperless-ngx team
 | 
			
		||||
extra:
 | 
			
		||||
  social:
 | 
			
		||||
    - icon: fontawesome/brands/github
 | 
			
		||||
      link: https://github.com/paperless-ngx/paperless-ngx
 | 
			
		||||
    - icon: fontawesome/brands/docker
 | 
			
		||||
      link: https://hub.docker.com/r/paperlessngx/paperless-ngx
 | 
			
		||||
    - icon: material/chat
 | 
			
		||||
      link: https://matrix.to/#/#paperless:matrix.org
 | 
			
		||||