Paperless workflow with paperless-ngx
Posted in linux on February 15, 2026 by Adrian Wyssmann ‐ 4 min read
What is paperless and how I use it
I use paperless-ngx since years to organize my documents.
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.
It’s an amazing tools which helps me to organize my papers digitally. For convenience, I had a local instance on my nnotebook running, with the disadvantage that only I had access to the user interface and could see the documents. In addition, up-to now my workflow was still pretty manual:
- Add the papers in the trail of the printer
- Open Document Scanner on my notebook and trigger the scan (output =
.png) - Convert all
.png-files into.pdfusing cfgnunes nautilus-scripts - Manually merge together
.pdf-files that belong togehter using cfgnunes nautilus-scripts - Place files into
consumefolder of paperless
While step 3. and 4. are theoretically not necessary as Merge multiple documents into a single one]( https://github.com/paperless-ngx/paperless-ngx/discussions/367) is meanwhile available in paperless, the workflow invovles still manual steps (scanning) and my personal notebook. In addition since Document Scanner broke the multi-feed scanning with scanner, it got bad.
So I tried to solve various problems
- use an instance which allows the whole family to see the documents
- no more opening my notebook to scan
Full automation
As I use nixos anywhere I started to install a remote server with a paperless instance. I use a dedicate user for the service, an ensures the default nixos-user - which I use for login remotely - has read access to the archive folder
{ config, pkgs, ... }:
{
users.users.paperless = {
isSystemUser = true;
group = "paperless";
description = "Service account for non-privileged tasks";
# Prevents interactive login
shell = pkgs.shadow;
# If the service needs a home directory for config/state
createHome = true;
home = "/var/lib/paperless";
};
# Corresponding group
users.groups.paperless = {};
users.users.nixos = {
extraGroups = [
"wheel"
"networkmanager"
"video"
"paperless"
];
};
services.paperless = {
enable = true;
address = "0.0.0.0";
port = 58080;
consumptionDirIsPublic = true;
user = "paperless";
domain = "paperless.home";
settings = {
PAPERLESS_OCR_LANGUAGE = "deu+eng";
PAPERLESS_TIME_ZONE = "Europe/Zurich";
PAPERLESS_FILENAME_FORMAT = "{document_type}/{correspondent}/{created_year}/{correspondent}_{created_year}{created_month}{created_day}_{title}";
PAPERLESS_CONSUMER_RECURSIVE = true;
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS = true;
# UMASK 0027 allows the group to read/list, but keeps 'others' blocked.
PAPERLESS_UMASK = "0027";
};
};
systemd.services.paperless-scheduler.after = ["var-lib-paperless.mount"];
systemd.services.paperless-consumer.after = ["var-lib-paperless.mount"];
systemd.services.paperless-web.after = ["var-lib-paperless.mount"];
networking.firewall.allowedTCPPorts = [ 58080 ];
# This ensures that every time the system boots or you run nixos-rebuild, the permissions are enforced
systemd.tmpfiles.rules = [
# Type | Path | Mode | User | Group | Age | Argument
"d /var/lib/paperless 0750 paperless paperless -"
"d /var/lib/paperless/media/documents 0750 paperless paperless -"
"Z /var/lib/paperless - paperless paperless -"
];
}In order to eliminate the need to use Document Scanner I had the idea to use the “Scan to Computer” funcitionality. Unfortunately this does not work with Linux, so I had to search trough the internet and found manuc66 node-hp-scan-to.
node-hp-scan-to is a Node.js application that replicates HP’s “Scan to Computer” functionality by reverse engineering HP’s proprietary protocols and supporting the standardized eSCL protocol, allowing you to scan documents directly from your HP printer’s scanner to your Linux, Windows, or macOS computer.
So let’s start that thing as a systemd service and ensure it places the files scanned into the consume folder and hence its picked up by paperless-ngx:
{ config, pkgs, ... }:
let
printer_ip = "10.0.0.100";
target_dir = "/var/lib/paperless/consume";
in
{
environment.systemPackages = with pkgs; [
nodejs
];
# Allow the specific port HP uses for the "Scan to Computer" handshake
networking.firewall.allowedTCPPorts = [ 8080 3389 445 ]; # 8080 is often used by the listener
networking.firewall.allowedUDPPorts = [ 137 138 5353 ]; # mDNS/Avahi for discovery
# 2. Create a background service to listen for the printer
systemd.services.hp-push-scan = {
description = "HP Scan-to-Computer Push Listener";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
# Ensure the service has the necessary binaries in its environment
path = with pkgs; [
bash
coreutils
nodejs
];
serviceConfig = {
# We use npx to run the tool without 'installing' it globally
ExecStart = "${pkgs.nodejs}/bin/npx node-hp-scan-to adf-autoscan -n'HP ENVY Inspire 7900e' -a ${printer_ip} -d ${target_dir} -r 300 --pdf -p yyyymmdd_hhMMdss";
User = "paperless";
Group = "paperless";
# Hardening: prevent the service from gaining new privileges
NoNewPrivileges = true;
ProtectSystem = "strict";
# This creates a virtual /tmp just for this service
PrivateTmp = true;
StateDirectory = "paperless"; # Creates /var/lib/paperless with correct perms
Restart = "always";
# This environment variable helps npm/npx know where to store its cache:with ; ;
Environment = "HOME=/var/lib/paperless";
};
};
}So now I place my papers on the feeder of the printe and they are automatically pulled in. Cool isn’t it!?