Files
homey/modules/services/paperless.nix
T
Aner Zakobar 08e8b5edbe REWRITE
2026-05-20 23:21:36 +03:00

163 lines
6.6 KiB
Nix

{ config, lib, pkgs, homeyConfig, ... }:
# Paperless-ngx — document management with OCR.
#
# Auth model: HTTP Remote User SSO. Authelia authenticates via Caddy
# forward_auth and sets the Remote-User header; Paperless trusts it and
# auto-creates/logs in the user. No separate Paperless login needed.
#
# The admin user (set via homey.paperless.adminUser) is created as a
# superuser on first start. Its password is randomly generated and never
# used — all logins go through Authelia.
#
# Requires a Redis sidecar for Celery task workers.
#
# Volume layout:
# <dataDir>/paperless/data/ → /usr/src/paperless/data (DB, index)
# <dataDir>/paperless/media/ → /usr/src/paperless/media (document files)
# <dataDir>/paperless/consume/ → /usr/src/paperless/consume (drop folder)
# <dataDir>/paperless/export/ → /usr/src/paperless/export (export output)
#
# Secrets consumed from sops:
# paperless/secret_key
let
cfg = config.homey.paperless;
dataDir = config.homey.storage.mountPoint;
domain = homeyConfig.domain;
in
{
options.homey.paperless = {
enable = lib.mkEnableOption "Paperless-ngx document management" // { default = true; };
image = lib.mkOption {
type = lib.types.str;
default = "ghcr.io/paperless-ngx/paperless-ngx:latest";
};
redisImage = lib.mkOption {
type = lib.types.str;
default = "docker.io/redis:7-alpine";
};
port = lib.mkOption {
type = lib.types.port;
default = 8083;
description = "Host port Paperless listens on (bound to 127.0.0.1).";
};
};
config = lib.mkIf cfg.enable {
# -----------------------------------------------------------------------
# Secrets
# -----------------------------------------------------------------------
sops.secrets."paperless/secret_key" = { owner = "root"; };
# -----------------------------------------------------------------------
# Redis — Celery task queue, stateless (no persistent storage)
# -----------------------------------------------------------------------
virtualisation.oci-containers.containers.paperless-redis = {
image = cfg.redisImage;
extraOptions = [ "--network=homey" ];
};
systemd.services."podman-paperless-redis" = {
after = lib.mkAfter [ "podman-homey-network.service" ];
requires = lib.mkAfter [ "podman-homey-network.service" ];
};
# -----------------------------------------------------------------------
# Paperless container
# -----------------------------------------------------------------------
virtualisation.oci-containers.containers.paperless = {
image = cfg.image;
ports = [ "127.0.0.1:${toString cfg.port}:8000" ];
environment = {
PAPERLESS_REDIS = "redis://paperless-redis:6379";
PAPERLESS_URL = "https://paperless.${domain}";
PAPERLESS_ALLOWED_HOSTS = "paperless.${domain}";
PAPERLESS_CORS_ALLOWED_HOSTS = "https://paperless.${domain}";
PAPERLESS_TIME_ZONE = homeyConfig.timezone;
PAPERLESS_OCR_LANGUAGE = "eng";
USERMAP_UID = "1000";
USERMAP_GID = "1000";
# SSO via Authelia: Caddy's forward_auth copies Remote-User from
# Authelia's response; Gunicorn/WSGI exposes it as HTTP_REMOTE_USER.
PAPERLESS_ENABLE_HTTP_REMOTE_USER = "true";
PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME = "HTTP_REMOTE_USER";
# Redirect to Authelia on logout so the SSO session is also cleared.
PAPERLESS_LOGOUT_REDIRECT_URL = "https://auth.${domain}";
};
environmentFiles = [ "/run/paperless-secrets.env" ];
volumes = [
"${dataDir}/paperless/data:/usr/src/paperless/data"
"${dataDir}/paperless/media:/usr/src/paperless/media"
"${dataDir}/paperless/consume:/usr/src/paperless/consume"
"${dataDir}/paperless/export:/usr/src/paperless/export"
];
extraOptions = [ "--network=homey" ];
};
# -----------------------------------------------------------------------
# ExecStartPre: write ephemeral secrets env file
# -----------------------------------------------------------------------
systemd.services."podman-paperless" = {
serviceConfig = {
ExecStartPre = [
(pkgs.writeShellScript "paperless-write-secrets" ''
set -euo pipefail
install -m 600 /dev/null /run/paperless-secrets.env
printf '%s\n' \
"PAPERLESS_SECRET_KEY=$(cat ${config.sops.secrets."paperless/secret_key".path})" \
>> /run/paperless-secrets.env
'')
];
};
postStop = "rm -f /run/paperless-secrets.env";
after = lib.mkAfter [ "mnt-data.mount" "podman-paperless-redis.service" "podman-homey-network.service" ];
requires = lib.mkAfter [ "mnt-data.mount" "podman-paperless-redis.service" "podman-homey-network.service" ];
};
# -----------------------------------------------------------------------
# Caddy virtual host — forward_auth; Remote-User passed to Paperless for SSO
# -----------------------------------------------------------------------
homey.caddy.virtualHosts = [{
subdomain = "paperless";
port = cfg.port;
auth = true;
}];
# -----------------------------------------------------------------------
# Storage directories (UID 1000 = USERMAP_UID in container)
# -----------------------------------------------------------------------
homey.storage.extraDirs = [
{ path = "paperless"; }
{ path = "paperless/data"; user = "1000"; group = "1000"; }
{ path = "paperless/media"; user = "1000"; group = "1000"; }
{ path = "paperless/consume"; user = "1000"; group = "1000"; }
{ path = "paperless/export"; user = "1000"; group = "1000"; }
];
# -----------------------------------------------------------------------
# Backup — exclude consume dir (unprocessed drops, usually empty)
# -----------------------------------------------------------------------
homey.backup.extraPaths = [ "${dataDir}/paperless" ];
homey.backup.extraExcludePaths = [ "${dataDir}/paperless/consume" ];
# -----------------------------------------------------------------------
# Uptime Kuma monitor
# -----------------------------------------------------------------------
homey.monitoring.monitors = [{
name = "Paperless";
url = "https://paperless.${domain}";
interval = 60;
}];
};
}