Change:
# during migrations bindsTo = [ "paperless-scheduler.service" ]; after = [ "paperless-scheduler.service" ]; <ins> # Setup PAPERLESS_SECRET_KEY. # If this environment variable is left unset, paperless-ngx defaults # to a well-known value, which is insecure. script = let secretKeyFile = "${cfg.dataDir}/nixos-paperless-secret-key"; in '' if [[ ! -f '${secretKeyFile}' ]]; then ( umask 0377 tr -dc A-Za-z0-9 < /dev/urandom | head -c64 | ${pkgs.moreutils}/bin/sponge '${secretKeyFile}' ) fi export PAPERLESS_SECRET_KEY=$(cat '${secretKeyFile}') if [[ ! $PAPERLESS_SECRET_KEY ]]; then echo "PAPERLESS_SECRET_KEY is empty, refusing to start." exit 1 fi exec ${pkg.python.pkgs.gunicorn}/bin/gunicorn  -c ${pkg}/lib/paperless-ngx/gunicorn.conf.py paperless.asgi:application ''; </ins> serviceConfig = defaultServiceConfig // { User = cfg.user; <del> ExecStart = '' ${pkg.python.pkgs.gunicorn}/bin/gunicorn  -c ${pkg}/lib/paperless-ngx/gunicorn.conf.py paperless.asgi:application ''; </del> Restart = "on-failure"; # gunicorn needs setuid, liblapack needs mbind

Discussion:
The paperless module does not provide a way to safely set (i.e. in a way that does not end up in the nix store, at least I couldn't think of any). Since paperless also does not support reading the key from a file something like sops-nix can't be used. To make matters worse, paperless defaults to a hard-coded and publicly known secret key (as documented in https://docs.paperless-). That means the only options are to set the value directly in the nix config and have it exposed in the nix store or use the default, which makes paperless insecure to open to the internet. Since the documentation of the NixOS module does not mention the PAPERLESSSECRETKEY at all I'd imagine there might be a few unknowingly unsafe instances out there. This is my understanding from reading the docs. If this is not an issue because of something I missed please correct me. Steps to reproduce the behavior: paperless using the module PAPERLESSSECRETKEY that it gets put into the systemd file directly Having some way to specify this secret safely. The best fix would be if paperless was able to read the secret from a file. There is a request to add that feature in Until then, it might be useful to address this issue in the NixOS module, if possible. <!-- Please @ people who are in the list of the offending package or module. If in doubt, check for whoever last touched something. --
Now that I thought about this a bit more I think I could use systemd's to load the secret safely (from sops-nix). That the "path of least resistance" is rather unsafe is still suboptimal though, in my opinion.
We could fail in prestart, if there is no secret key in the environment. patch diff --git index .. --- +++ -252,6 +252,11 in echo versionFile" fi + if [ -z "$PAPERLESSSECRETKEY" ]; then echo "Refusing to start without PAPERLESSSECRETKEY. See https://docs.paperless-" exit 1 fi '' optionalString (cfg.passwordFile != null) '' export PAPERLESSADMINUSER="''${PAPERLESSADMINUSER:-admin}"
Why not provide a secretKeyFile attribute similar to services.paperless.passwordFile?

The way that I handle this typically is to create a file on disk during the first startup and then use an exported environment variable every time.
Note: I started a discussion upstream about this, since I feel like paperless should enforce a randomized secret in any case (even when in local network). See
I support this. It could be a mandatory option (or at least show a warning if unset) that explains the issue in its description. As far as I understand it, it is more or less safe to change the secret key even for existing instances. This would just invalidate everything that is signed using the secret key, so e.g. existing user sessions get logged out.
This is the solution, that should be implemented. It fixes the situation for everyone and makes it an implementation detail, not another pointless setting.
I think it is possible to set (see ) if our package uses the docker-
I did not double check, but I strongly assume that the nix package is not using the docker entrypoint script. is a "standard" buildPythonApplication without any reference to docker. See: