summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorAndreas Rammhold <andreas@rammhold.de>2021-08-18 14:39:03 +0200
committerGitHub <noreply@github.com>2021-08-18 14:39:03 +0200
commitae61a14242cdc2ee37910ee20b750d1adef1bc66 (patch)
tree05cf1023432c9512ec308741e7042d2ebd0e10bf /nixos
parentbefd4f60c6ae2903f60d46f3f558084bbb70d1f8 (diff)
parent6e1421013a299668fd610dd70b3ebd9105891b90 (diff)
downloadnixpkgs-ae61a14242cdc2ee37910ee20b750d1adef1bc66.tar
nixpkgs-ae61a14242cdc2ee37910ee20b750d1adef1bc66.tar.gz
nixpkgs-ae61a14242cdc2ee37910ee20b750d1adef1bc66.tar.bz2
nixpkgs-ae61a14242cdc2ee37910ee20b750d1adef1bc66.tar.lz
nixpkgs-ae61a14242cdc2ee37910ee20b750d1adef1bc66.tar.xz
nixpkgs-ae61a14242cdc2ee37910ee20b750d1adef1bc66.tar.zst
nixpkgs-ae61a14242cdc2ee37910ee20b750d1adef1bc66.zip
Merge pull request #123046 from Flakebi/paperless
paperless-ng: init at 1.4.5
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2111.section.xml84
-rw-r--r--nixos/doc/manual/release-notes/rl-2111.section.md47
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/services/misc/paperless-ng.nix304
-rw-r--r--nixos/modules/services/misc/paperless.nix183
-rw-r--r--nixos/tests/all-tests.nix2
-rw-r--r--nixos/tests/paperless-ng.nix36
-rw-r--r--nixos/tests/paperless.nix36
8 files changed, 473 insertions, 221 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
index 03773f96420..86031791b1a 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
@@ -188,6 +188,90 @@
     <itemizedlist>
       <listitem>
         <para>
+          The <literal>paperless</literal> module and package have been
+          removed. All users should migrate to the successor
+          <literal>paperless-ng</literal> instead. The Paperless project
+          <link xlink:href="https://github.com/the-paperless-project/paperless/commit/9b0063c9731f7c5f65b1852cb8caff97f5e40ba4">has
+          been archived</link> and advises all users to use
+          <literal>paperless-ng</literal> instead.
+        </para>
+        <para>
+          Users can use the <literal>services.paperless-ng</literal>
+          module as a replacement while noting the following
+          incompatibilities:
+        </para>
+        <itemizedlist spacing="compact">
+          <listitem>
+            <para>
+              <literal>services.paperless.ocrLanguages</literal> has no
+              replacement. Users should migrate to
+              <link xlink:href="options.html#opt-services.paperless-ng.extraConfig"><literal>services.paperless-ng.extraConfig</literal></link>
+              instead:
+            </para>
+          </listitem>
+        </itemizedlist>
+        <programlisting language="bash">
+{
+  services.paperless-ng.extraConfig = {
+    # Provide languages as ISO 639-2 codes
+    # separated by a plus (+) sign.
+    # https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
+    PAPERLESS_OCR_LANGUAGE = &quot;deu+eng+jpn&quot;; # German &amp; English &amp; Japanse
+  };
+}
+</programlisting>
+        <itemizedlist>
+          <listitem>
+            <para>
+              If you previously specified
+              <literal>PAPERLESS_CONSUME_MAIL_*</literal> settings in
+              <literal>services.paperless.extraConfig</literal> you
+              should remove those options now. You now
+              <emphasis>must</emphasis> define those settings in the
+              admin interface of paperless-ng.
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              Option <literal>services.paperless.manage</literal> no
+              longer exists. Use the script at
+              <literal>${services.paperless-ng.dataDir}/paperless-ng-manage</literal>
+              instead. Note that this script only exists after the
+              <literal>paperless-ng</literal> service has been started
+              at least once.
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              After switching to the new system configuration you should
+              run the Django management command to reindex your
+              documents and optionally create a user, if you don’t have
+              one already.
+            </para>
+            <para>
+              To do so, enter the data directory (the value of
+              <literal>services.paperless-ng.dataDir</literal>,
+              <literal>/var/lib/paperless</literal> by default), switch
+              to the paperless user and execute the management command
+              like below:
+            </para>
+            <programlisting>
+$ cd /var/lib/paperless
+$ su paperless -s /bin/sh
+$ ./paperless-ng-manage document_index reindex
+# if not already done create a user account, paperless-ng requires a login
+$ ./paperless-ng-manage createsuperuser
+Username (leave blank to use 'paperless'): my-user-name
+Email address: me@example.com
+Password: **********
+Password (again): **********
+Superuser created successfully.
+</programlisting>
+          </listitem>
+        </itemizedlist>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>staticjinja</literal> package has been upgraded
           from 1.0.4 to 3.0.1
         </para>
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 6d42dfc2dc5..231fc05f88b 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -58,6 +58,53 @@ subsonic-compatible api. Available as [navidrome](#opt-services.navidrome.enable
 
 ## Backward Incompatibilities {#sec-release-21.11-incompatibilities}
 
+- The `paperless` module and package have been removed. All users should migrate to the
+  successor `paperless-ng` instead. The Paperless project [has been
+  archived](https://github.com/the-paperless-project/paperless/commit/9b0063c9731f7c5f65b1852cb8caff97f5e40ba4)
+  and advises all users to use `paperless-ng` instead.
+
+  Users can use the `services.paperless-ng` module as a replacement while noting the following incompatibilities:
+    - `services.paperless.ocrLanguages` has no replacement. Users should migrate to [`services.paperless-ng.extraConfig`](options.html#opt-services.paperless-ng.extraConfig) instead:
+     ```nix
+     {
+       services.paperless-ng.extraConfig = {
+         # Provide languages as ISO 639-2 codes
+         # separated by a plus (+) sign.
+         # https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
+         PAPERLESS_OCR_LANGUAGE = "deu+eng+jpn"; # German & English & Japanse
+       };
+     }
+     ```
+
+    - If you previously specified `PAPERLESS_CONSUME_MAIL_*` settings in
+      `services.paperless.extraConfig` you should remove those options now. You
+      now *must* define those settings in the admin interface of paperless-ng.
+
+    - Option `services.paperless.manage` no longer exists.
+      Use the script at `${services.paperless-ng.dataDir}/paperless-ng-manage` instead.
+      Note that this script only exists after the `paperless-ng` service has been
+      started at least once.
+
+    - After switching to the new system configuration you should run the Django
+      management command to reindex your documents and optionally create a user,
+      if you don't have one already.
+
+      To do so, enter the data directory (the value of
+      `services.paperless-ng.dataDir`, `/var/lib/paperless` by default), switch
+      to the paperless user and execute the management command like below:
+      ```
+      $ cd /var/lib/paperless
+      $ su paperless -s /bin/sh
+      $ ./paperless-ng-manage document_index reindex
+      # if not already done create a user account, paperless-ng requires a login
+      $ ./paperless-ng-manage createsuperuser
+      Username (leave blank to use 'paperless'): my-user-name
+      Email address: me@example.com
+      Password: **********
+      Password (again): **********
+      Superuser created successfully.
+      ```
+
 - The `staticjinja` package has been upgraded from 1.0.4 to 3.0.1
 
 - The `erigon` ethereum node has moved to a new database format in `2021-05-04`, and requires a full resync
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 7b638afc207..f26977b1244 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -551,7 +551,7 @@
   ./services/misc/ombi.nix
   ./services/misc/osrm.nix
   ./services/misc/packagekit.nix
-  ./services/misc/paperless.nix
+  ./services/misc/paperless-ng.nix
   ./services/misc/parsoid.nix
   ./services/misc/plex.nix
   ./services/misc/plikd.nix
diff --git a/nixos/modules/services/misc/paperless-ng.nix b/nixos/modules/services/misc/paperless-ng.nix
new file mode 100644
index 00000000000..9eaf8fa8859
--- /dev/null
+++ b/nixos/modules/services/misc/paperless-ng.nix
@@ -0,0 +1,304 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.services.paperless-ng;
+
+  defaultUser = "paperless";
+
+  env = {
+    PAPERLESS_DATA_DIR = cfg.dataDir;
+    PAPERLESS_MEDIA_ROOT = cfg.mediaDir;
+    PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
+    GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}";
+  } // lib.mapAttrs (_: toString) cfg.extraConfig;
+
+  manage = let
+    setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env);
+  in pkgs.writeShellScript "manage" ''
+    ${setupEnv}
+    exec ${cfg.package}/bin/paperless-ng "$@"
+  '';
+
+  # Secure the services
+  defaultServiceConfig = {
+    TemporaryFileSystem = "/:ro";
+    BindReadOnlyPaths = [
+      "/nix/store"
+      "-/etc/resolv.conf"
+      "-/etc/nsswitch.conf"
+      "-/etc/hosts"
+      "-/etc/localtime"
+    ];
+    BindPaths = [
+      cfg.consumptionDir
+      cfg.dataDir
+      cfg.mediaDir
+    ];
+    CapabilityBoundingSet = "";
+    # ProtectClock adds DeviceAllow=char-rtc r
+    DeviceAllow = "";
+    LockPersonality = true;
+    MemoryDenyWriteExecute = true;
+    NoNewPrivileges = true;
+    PrivateDevices = true;
+    PrivateMounts = true;
+    # Needs to connect to redis
+    # PrivateNetwork = true;
+    PrivateTmp = true;
+    PrivateUsers = true;
+    ProcSubset = "pid";
+    ProtectClock = true;
+    # Breaks if the home dir of the user is in /home
+    # Also does not add much value in combination with the TemporaryFileSystem.
+    # ProtectHome = true;
+    ProtectHostname = true;
+    # Would re-mount paths ignored by temporary root
+    #ProtectSystem = "strict";
+    ProtectControlGroups = true;
+    ProtectKernelLogs = true;
+    ProtectKernelModules = true;
+    ProtectKernelTunables = true;
+    ProtectProc = "invisible";
+    RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+    RestrictNamespaces = true;
+    RestrictRealtime = true;
+    RestrictSUIDSGID = true;
+    SystemCallArchitectures = "native";
+    SystemCallFilter = [ "@system-service" "~@privileged @resources @setuid @keyring" ];
+    # Does not work well with the temporary root
+    #UMask = "0066";
+  };
+in
+{
+  meta.maintainers = with maintainers; [ earvstedt Flakebi ];
+
+  imports = [
+    (mkRemovedOptionModule [ "services" "paperless"] ''
+      The paperless module has been removed as the upstream project died.
+      Users should migrate to the paperless-ng module (services.paperless-ng).
+      More information can be found in the NixOS 21.11 release notes.
+    '')
+  ];
+
+  options.services.paperless-ng = {
+    enable = mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enable Paperless-ng.
+
+        When started, the Paperless database is automatically created if it doesn't
+        exist and updated if the Paperless package has changed.
+        Both tasks are achieved by running a Django migration.
+
+        A script to manage the Paperless instance (by wrapping Django's manage.py) is linked to
+        <literal>''${dataDir}/paperless-ng-manage</literal>.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = types.str;
+      default = "/var/lib/paperless";
+      description = "Directory to store the Paperless data.";
+    };
+
+    mediaDir = mkOption {
+      type = types.str;
+      default = "${cfg.dataDir}/media";
+      defaultText = "\${dataDir}/consume";
+      description = "Directory to store the Paperless documents.";
+    };
+
+    consumptionDir = mkOption {
+      type = types.str;
+      default = "${cfg.dataDir}/consume";
+      defaultText = "\${dataDir}/consume";
+      description = "Directory from which new documents are imported.";
+    };
+
+    consumptionDirIsPublic = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether all users can write to the consumption dir.";
+    };
+
+    passwordFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/keys/paperless-ng-password";
+      description = ''
+        A file containing the superuser password.
+
+        A superuser is required to access the web interface.
+        If unset, you can create a superuser manually by running
+        <literal>''${dataDir}/paperless-ng-manage createsuperuser</literal>.
+
+        The default superuser name is <literal>admin</literal>. To change it, set
+        option <option>extraConfig.PAPERLESS_ADMIN_USER</option>.
+        WARNING: When changing the superuser name after the initial setup, the old superuser
+        will continue to exist.
+
+        To disable login for the web interface, set the following:
+        <literal>extraConfig.PAPERLESS_AUTO_LOGIN_USERNAME = "admin";</literal>.
+        WARNING: Only use this on a trusted system without internet access to Paperless.
+      '';
+    };
+
+    address = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Web interface address.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 28981;
+      description = "Web interface port.";
+    };
+
+    extraConfig = mkOption {
+      type = types.attrs;
+      default = {};
+      description = ''
+        Extra paperless-ng config options.
+
+        See <link xlink:href="https://paperless-ng.readthedocs.io/en/latest/configuration.html">the documentation</link>
+        for available options.
+      '';
+      example = literalExample ''
+        {
+          PAPERLESS_OCR_LANGUAGE = "deu+eng";
+        }
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = "User under which Paperless runs.";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.paperless-ng;
+      defaultText = "pkgs.paperless-ng";
+      description = "The Paperless package to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # Enable redis if no special url is set
+    services.redis.enable = mkIf (!hasAttr "PAPERLESS_REDIS" env) true;
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      "d '${cfg.mediaDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      (if cfg.consumptionDirIsPublic then
+        "d '${cfg.consumptionDir}' 777 - - - -"
+      else
+        "d '${cfg.consumptionDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      )
+    ];
+
+    systemd.services.paperless-ng-server = {
+      description = "Paperless document server";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = "${cfg.package}/bin/paperless-ng qcluster";
+        Restart = "on-failure";
+      };
+      environment = env;
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "paperless-ng-consumer.service" "paperless-ng-web.service" ];
+
+      preStart = ''
+        ln -sf ${manage} ${cfg.dataDir}/paperless-ng-manage
+
+        # Auto-migrate on first run or if the package has changed
+        versionFile="${cfg.dataDir}/src-version"
+        if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
+          ${cfg.package}/bin/paperless-ng migrate
+          echo ${cfg.package} > "$versionFile"
+        fi
+      ''
+      + optionalString (cfg.passwordFile != null) ''
+        export PAPERLESS_ADMIN_USER="''${PAPERLESS_ADMIN_USER:-admin}"
+        export PAPERLESS_ADMIN_PASSWORD=$(cat "${cfg.dataDir}/superuser-password")
+        superuserState="$PAPERLESS_ADMIN_USER:$PAPERLESS_ADMIN_PASSWORD"
+        superuserStateFile="${cfg.dataDir}/superuser-state"
+
+        if [[ $(cat "$superuserStateFile" 2>/dev/null) != $superuserState ]]; then
+          ${cfg.package}/bin/paperless-ng manage_superuser
+          echo "$superuserState" > "$superuserStateFile"
+        fi
+      '';
+    };
+
+    # Password copying can't be implemented as a privileged preStart script
+    # in 'paperless-ng-server' because 'defaultServiceConfig' limits the filesystem
+    # paths accessible by the service.
+    systemd.services.paperless-ng-copy-password = mkIf (cfg.passwordFile != null) {
+      requiredBy = [ "paperless-ng-server.service" ];
+      before = [ "paperless-ng-server.service" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.coreutils}/bin/install --mode 600 --owner '${cfg.user}' --compare \
+            '${cfg.passwordFile}' '${cfg.dataDir}/superuser-password'
+        '';
+        Type = "oneshot";
+      };
+    };
+
+    systemd.services.paperless-ng-consumer = {
+      description = "Paperless document consumer";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = "${cfg.package}/bin/paperless-ng document_consumer";
+        Restart = "on-failure";
+      };
+      environment = env;
+      # Bind to `paperless-ng-server` so that the consumer never runs
+      # during migrations
+      bindsTo = [ "paperless-ng-server.service" ];
+      after = [ "paperless-ng-server.service" ];
+    };
+
+    systemd.services.paperless-ng-web = {
+      description = "Paperless web server";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = ''
+          ${pkgs.python3Packages.gunicorn}/bin/gunicorn \
+            -c ${cfg.package}/lib/paperless-ng/gunicorn.conf.py paperless.asgi:application
+        '';
+        Restart = "on-failure";
+
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
+        # gunicorn needs setuid
+        SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "@setuid" ];
+      };
+      environment = env // {
+        PATH = mkForce cfg.package.path;
+        PYTHONPATH = "${cfg.package.pythonPath}:${cfg.package}/lib/paperless-ng/src";
+      };
+      # Bind to `paperless-ng-server` so that the web server never runs
+      # during migrations
+      bindsTo = [ "paperless-ng-server.service" ];
+      after = [ "paperless-ng-server.service" ];
+    };
+
+    users = optionalAttrs (cfg.user == defaultUser) {
+      users.${defaultUser} = {
+        group = defaultUser;
+        uid = config.ids.uids.paperless;
+        home = cfg.dataDir;
+      };
+
+      groups.${defaultUser} = {
+        gid = config.ids.gids.paperless;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix
deleted file mode 100644
index 43730b80eb2..00000000000
--- a/nixos/modules/services/misc/paperless.nix
+++ /dev/null
@@ -1,183 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-let
-  cfg = config.services.paperless;
-
-  defaultUser = "paperless";
-
-  manage = cfg.package.withConfig {
-    config = {
-      PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
-      PAPERLESS_INLINE_DOC = "true";
-      PAPERLESS_DISABLE_LOGIN = "true";
-    } // cfg.extraConfig;
-    inherit (cfg) dataDir ocrLanguages;
-    paperlessPkg = cfg.package;
-  };
-in
-{
-  options.services.paperless = {
-    enable = mkOption {
-      type = lib.types.bool;
-      default = false;
-      description = ''
-        Enable Paperless.
-
-        When started, the Paperless database is automatically created if it doesn't
-        exist and updated if the Paperless package has changed.
-        Both tasks are achieved by running a Django migration.
-      '';
-    };
-
-    dataDir = mkOption {
-      type = types.str;
-      default = "/var/lib/paperless";
-      description = "Directory to store the Paperless data.";
-    };
-
-    consumptionDir = mkOption {
-      type = types.str;
-      default = "${cfg.dataDir}/consume";
-      defaultText = "\${dataDir}/consume";
-      description = "Directory from which new documents are imported.";
-    };
-
-    consumptionDirIsPublic = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Whether all users can write to the consumption dir.";
-    };
-
-    ocrLanguages = mkOption {
-      type = with types; nullOr (listOf str);
-      default = null;
-      description = ''
-        Languages available for OCR via Tesseract, specified as
-        <literal>ISO 639-2/T</literal> language codes.
-        If unset, defaults to all available languages.
-      '';
-      example = [ "eng" "spa" "jpn" ];
-    };
-
-    address = mkOption {
-      type = types.str;
-      default = "localhost";
-      description = "Server listening address.";
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 28981;
-      description = "Server port to listen on.";
-    };
-
-    extraConfig = mkOption {
-      type = types.attrs;
-      default = {};
-      description = ''
-        Extra paperless config options.
-
-        The config values are evaluated as double-quoted Bash string literals.
-
-        See <literal>paperless-src/paperless.conf.example</literal> for available options.
-
-        To enable user authentication, set <literal>PAPERLESS_DISABLE_LOGIN = "false"</literal>
-        and run the shell command <literal>$dataDir/paperless-manage createsuperuser</literal>.
-
-        To define secret options without storing them in /nix/store, use the following pattern:
-        <literal>PAPERLESS_PASSPHRASE = "$(&lt; /etc/my_passphrase_file)"</literal>
-      '';
-      example = literalExample ''
-        {
-          PAPERLESS_OCR_LANGUAGE = "deu";
-        }
-      '';
-    };
-
-    user = mkOption {
-      type = types.str;
-      default = defaultUser;
-      description = "User under which Paperless runs.";
-    };
-
-    package = mkOption {
-      type = types.package;
-      default = pkgs.paperless;
-      defaultText = "pkgs.paperless";
-      description = "The Paperless package to use.";
-    };
-
-    manage = mkOption {
-      type = types.package;
-      readOnly = true;
-      default = manage;
-      description = ''
-        A script to manage the Paperless instance.
-        It wraps Django's manage.py and is also available at
-        <literal>$dataDir/manage-paperless</literal>
-      '';
-    };
-  };
-
-  config = mkIf cfg.enable {
-
-    systemd.tmpfiles.rules = [
-      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
-    ] ++ (optional cfg.consumptionDirIsPublic
-      "d '${cfg.consumptionDir}' 777 - - - -"
-      # If the consumption dir is not created here, it's automatically created by
-      # 'manage' with the default permissions.
-    );
-
-    systemd.services.paperless-consumer = {
-      description = "Paperless document consumer";
-      serviceConfig = {
-        User = cfg.user;
-        ExecStart = "${manage} document_consumer";
-        Restart = "always";
-      };
-      after = [ "systemd-tmpfiles-setup.service" ];
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        if [[ $(readlink ${cfg.dataDir}/paperless-manage) != ${manage} ]]; then
-          ln -sf ${manage} ${cfg.dataDir}/paperless-manage
-        fi
-
-        ${manage.setupEnv}
-        # Auto-migrate on first run or if the package has changed
-        versionFile="$PAPERLESS_DBDIR/src-version"
-        if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
-          python $paperlessSrc/manage.py migrate
-          echo ${cfg.package} > "$versionFile"
-        fi
-      '';
-    };
-
-    systemd.services.paperless-server = {
-      description = "Paperless document server";
-      serviceConfig = {
-        User = cfg.user;
-        ExecStart = "${manage} runserver --noreload ${cfg.address}:${toString cfg.port}";
-        Restart = "always";
-      };
-      # Bind to `paperless-consumer` so that the server never runs
-      # during migrations
-      bindsTo = [ "paperless-consumer.service" ];
-      after = [ "paperless-consumer.service" ];
-      wantedBy = [ "multi-user.target" ];
-    };
-
-    users = optionalAttrs (cfg.user == defaultUser) {
-      users.${defaultUser} = {
-        group = defaultUser;
-        uid = config.ids.uids.paperless;
-        home = cfg.dataDir;
-      };
-
-      groups.${defaultUser} = {
-        gid = config.ids.gids.paperless;
-      };
-    };
-  };
-}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index c3dd50007aa..e1ad011b22d 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -336,7 +336,7 @@ in
   pam-oath-login = handleTest ./pam-oath-login.nix {};
   pam-u2f = handleTest ./pam-u2f.nix {};
   pantheon = handleTest ./pantheon.nix {};
-  paperless = handleTest ./paperless.nix {};
+  paperless-ng = handleTest ./paperless-ng.nix {};
   pdns-recursor = handleTest ./pdns-recursor.nix {};
   peerflix = handleTest ./peerflix.nix {};
   pgjwt = handleTest ./pgjwt.nix {};
diff --git a/nixos/tests/paperless-ng.nix b/nixos/tests/paperless-ng.nix
new file mode 100644
index 00000000000..d8aafc2a08f
--- /dev/null
+++ b/nixos/tests/paperless-ng.nix
@@ -0,0 +1,36 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "paperless-ng";
+  meta.maintainers = with lib.maintainers; [ earvstedt Flakebi ];
+
+  nodes.machine = { pkgs, ... }: {
+    environment.systemPackages = with pkgs; [ imagemagick jq ];
+    services.paperless-ng = {
+      enable = true;
+      passwordFile = builtins.toFile "password" "admin";
+    };
+    virtualisation.memorySize = 1024;
+  };
+
+  testScript = ''
+    machine.wait_for_unit("paperless-ng-consumer.service")
+
+    with subtest("Create test doc"):
+        machine.succeed(
+            "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black "
+            "-annotate +5+20 'hello world 16-10-2005' /var/lib/paperless/consume/doc.png"
+        )
+
+    with subtest("Web interface gets ready"):
+        machine.wait_for_unit("paperless-ng-web.service")
+        # Wait until server accepts connections
+        machine.wait_until_succeeds("curl -fs localhost:28981")
+
+    with subtest("Document is consumed"):
+        machine.wait_until_succeeds(
+            "(($(curl -u admin:admin -fs localhost:28981/api/documents/ | jq .count) == 1))"
+        )
+        assert "2005-10-16" in machine.succeed(
+            "curl -u admin:admin -fs localhost:28981/api/documents/ | jq '.results | .[0] | .created'"
+        )
+  '';
+})
diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix
deleted file mode 100644
index fb83e6f976d..00000000000
--- a/nixos/tests/paperless.nix
+++ /dev/null
@@ -1,36 +0,0 @@
-import ./make-test-python.nix ({ lib, ... } : {
-  name = "paperless";
-  meta = with lib.maintainers; {
-    maintainers = [ earvstedt ];
-  };
-
-  machine = { pkgs, ... }: {
-    environment.systemPackages = with pkgs; [ imagemagick jq ];
-    services.paperless = {
-      enable = true;
-      ocrLanguages = [ "eng" ];
-    };
-  };
-
-  testScript = ''
-    machine.wait_for_unit("paperless-consumer.service")
-
-    # Create test doc
-    machine.succeed(
-        "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black -annotate +5+20 'hello world 16-10-2005' /var/lib/paperless/consume/doc.png"
-    )
-
-    with subtest("Service gets ready"):
-        machine.wait_for_unit("paperless-server.service")
-        # Wait until server accepts connections
-        machine.wait_until_succeeds("curl -fs localhost:28981")
-
-    with subtest("Test document is consumed"):
-        machine.wait_until_succeeds(
-            "(($(curl -fs localhost:28981/api/documents/ | jq .count) == 1))"
-        )
-        assert "2005-10-16" in machine.succeed(
-            "curl -fs localhost:28981/api/documents/ | jq '.results | .[0] | .created'"
-        )
-  '';
-})