diff --git a/kubernetes/app/paperless/cronjob-backup.yaml b/kubernetes/app/paperless/cronjob-backup.yaml new file mode 100644 index 0000000..1af8145 --- /dev/null +++ b/kubernetes/app/paperless/cronjob-backup.yaml @@ -0,0 +1,141 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: paperless-db-backup + namespace: paperless + labels: + app: paperless-backup +spec: + schedule: "0 2 * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + template: + metadata: + labels: + app: paperless-backup + spec: + restartPolicy: OnFailure + initContainers: + - name: pg-dump + image: postgres:18 + env: + - name: PGHOST + value: paperless-db + - name: PGUSER + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_USERNAME + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_PASSWORD + - name: PGDATABASE + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_DATABASE_NAME + command: + - sh + - -c + - pg_dump --clean --if-exists > /backup/dump.sql + volumeMounts: + - name: backup-tmp + mountPath: /backup + containers: + - name: resticprofile + image: creativeprojects/resticprofile:0.32.0 + command: + - sh + - -c + - | + resticprofile -c /secrets/profiles.yaml -n paperless-db backup + resticprofile -c /secrets/profiles.yaml -n paperless-db copy + env: + - name: B2_ACCOUNT_ID + valueFrom: + secretKeyRef: + name: paperless-backup-config + key: B2_ACCOUNT_ID + - name: B2_ACCOUNT_KEY + valueFrom: + secretKeyRef: + name: paperless-backup-config + key: B2_ACCOUNT_KEY + volumeMounts: + - name: secrets + mountPath: /secrets + readOnly: true + - name: backup-tmp + mountPath: /backup + volumes: + - name: secrets + secret: + secretName: paperless-backup-config + - name: backup-tmp + emptyDir: {} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: paperless-media-backup + namespace: paperless + labels: + app: paperless-backup +spec: + schedule: "0 3 * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + template: + metadata: + labels: + app: paperless-backup + spec: + restartPolicy: OnFailure + containers: + - name: resticprofile + image: creativeprojects/resticprofile:0.32.0 + command: + - sh + - -c + - | + resticprofile -c /secrets/profiles.yaml -n paperless-media backup + resticprofile -c /secrets/profiles.yaml -n paperless-media copy + env: + - name: B2_ACCOUNT_ID + valueFrom: + secretKeyRef: + name: paperless-backup-config + key: B2_ACCOUNT_ID + - name: B2_ACCOUNT_KEY + valueFrom: + secretKeyRef: + name: paperless-backup-config + key: B2_ACCOUNT_KEY + volumeMounts: + - name: secrets + mountPath: /secrets + readOnly: true + - name: media + mountPath: /media + readOnly: true + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + memory: 1Gi + volumes: + - name: secrets + secret: + secretName: paperless-backup-config + - name: media + persistentVolumeClaim: + claimName: paperless-media diff --git a/kubernetes/app/paperless/deployment-gotenberg.yaml b/kubernetes/app/paperless/deployment-gotenberg.yaml new file mode 100644 index 0000000..7d0dc07 --- /dev/null +++ b/kubernetes/app/paperless/deployment-gotenberg.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: paperless-gotenberg + namespace: paperless + labels: + app: paperless-gotenberg +spec: + replicas: 1 + selector: + matchLabels: + app: paperless-gotenberg + template: + metadata: + labels: + app: paperless-gotenberg + spec: + securityContext: + # gotenberg image uses a non-numeric username so runAsNonRoot cannot be verified by kubelet + seccompProfile: + type: RuntimeDefault + containers: + - name: gotenberg + image: gotenberg/gotenberg:8 + command: + - gotenberg + - --chromium-disable-javascript=true + - --chromium-allow-list=file:///tmp/.* + env: + - name: TZ + value: Europe/Kyiv + ports: + - containerPort: 3000 + name: http + resources: + requests: + cpu: 50m + memory: 256Mi + limits: + memory: 1Gi diff --git a/kubernetes/app/paperless/deployment-redis.yaml b/kubernetes/app/paperless/deployment-redis.yaml new file mode 100644 index 0000000..6a7bad5 --- /dev/null +++ b/kubernetes/app/paperless/deployment-redis.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: paperless-redis + namespace: paperless + labels: + app: paperless-redis +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: paperless-redis + template: + metadata: + labels: + app: paperless-redis + spec: + securityContext: + # redis image starts as root to set up permissions then drops to the redis user internally + seccompProfile: + type: RuntimeDefault + containers: + - name: redis + image: redis:8 + env: + - name: TZ + value: Europe/Kyiv + ports: + - containerPort: 6379 + name: redis + volumeMounts: + - name: data + mountPath: /data + resources: + requests: + cpu: 20m + memory: 64Mi + limits: + memory: 256Mi + volumes: + - name: data + persistentVolumeClaim: + claimName: paperless-redis diff --git a/kubernetes/app/paperless/deployment-tika.yaml b/kubernetes/app/paperless/deployment-tika.yaml new file mode 100644 index 0000000..4f9dabe --- /dev/null +++ b/kubernetes/app/paperless/deployment-tika.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: paperless-tika + namespace: paperless + labels: + app: paperless-tika +spec: + replicas: 1 + selector: + matchLabels: + app: paperless-tika + template: + metadata: + labels: + app: paperless-tika + spec: + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - name: tika + image: apache/tika:3.2.3.0 + env: + - name: TZ + value: Europe/Kyiv + ports: + - containerPort: 9998 + name: http + resources: + requests: + cpu: 50m + memory: 256Mi + limits: + memory: 1Gi diff --git a/kubernetes/app/paperless/deployment.yaml b/kubernetes/app/paperless/deployment.yaml new file mode 100644 index 0000000..84dcdce --- /dev/null +++ b/kubernetes/app/paperless/deployment.yaml @@ -0,0 +1,128 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: paperless + namespace: paperless + labels: + app: paperless +spec: + replicas: 0 + strategy: + type: Recreate + selector: + matchLabels: + app: paperless + template: + metadata: + labels: + app: paperless + spec: + enableServiceLinks: false + securityContext: + # paperless-ngx starts as root to remap USERMAP_UID/GID — runAsNonRoot is intentionally omitted + seccompProfile: + type: RuntimeDefault + containers: + - name: paperless + image: ghcr.io/paperless-ngx/paperless-ngx:2.20 + ports: + - containerPort: 8000 + name: http + env: + - name: PAPERLESS_REDIS + value: redis://paperless-redis:6379 + - name: PAPERLESS_DBHOST + value: paperless-db + - name: PAPERLESS_DBUSER + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_USERNAME + - name: PAPERLESS_DBPASS + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_PASSWORD + - name: PAPERLESS_DBNAME + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_DATABASE_NAME + - name: PAPERLESS_SECRET_KEY + valueFrom: + secretKeyRef: + name: paperless-credentials + key: PAPERLESS_SECRET_KEY + - name: PAPERLESS_SOCIALACCOUNT_PROVIDERS + valueFrom: + secretKeyRef: + name: paperless-credentials + key: PAPERLESS_SOCIALACCOUNT_PROVIDERS + - name: PAPERLESS_TIKA_ENABLED + value: "1" + - name: PAPERLESS_TIKA_GOTENBERG_ENDPOINT + value: http://paperless-gotenberg:3000 + - name: PAPERLESS_TIKA_ENDPOINT + value: http://paperless-tika:9998 + - name: PAPERLESS_OCR_LANGUAGE + value: ukr+eng + - name: PAPERLESS_OCR_LANGUAGES + value: ukr rus + - name: PAPERLESS_TIME_ZONE + value: Europe/Kyiv + - name: PAPERLESS_URL + value: https://${PAPERLESS_HOST} + - name: PAPERLESS_CONSUMER_BARCODE_SCANNER + value: ZXING + - name: PAPERLESS_TASK_WORKERS + value: "2" + - name: PAPERLESS_CONSUMER_POLLING + value: "10" + - name: USERMAP_UID + value: "1027" + - name: USERMAP_GID + value: "100" + - name: PAPERLESS_APPS + value: allauth.socialaccount.providers.openid_connect + - name: PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS + value: "false" + volumeMounts: + - name: data + mountPath: /usr/src/paperless/data + - name: media + mountPath: /usr/src/paperless/media + - name: consume + mountPath: /usr/src/paperless/consume + - name: export + mountPath: /usr/src/paperless/export + livenessProbe: + httpGet: + port: 8000 + path: /api/ + initialDelaySeconds: 60 + periodSeconds: 30 + readinessProbe: + httpGet: + port: 8000 + path: /api/ + initialDelaySeconds: 30 + periodSeconds: 10 + resources: + requests: + cpu: 100m + memory: 512Mi + limits: + memory: 5Gi + volumes: + - name: data + persistentVolumeClaim: + claimName: paperless-data + - name: media + persistentVolumeClaim: + claimName: paperless-media + - name: consume + persistentVolumeClaim: + claimName: paperless-consume + - name: export + persistentVolumeClaim: + claimName: paperless-export diff --git a/kubernetes/app/paperless/ingress.yaml b/kubernetes/app/paperless/ingress.yaml new file mode 100644 index 0000000..cdc9ced --- /dev/null +++ b/kubernetes/app/paperless/ingress.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: paperless + namespace: paperless + annotations: + cert-manager.io/cluster-issuer: letsencrypt +spec: + tls: + - hosts: + - ${PAPERLESS_HOST} + secretName: paperless-tls + rules: + - host: ${PAPERLESS_HOST} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: paperless + port: + number: 8000 diff --git a/kubernetes/app/paperless/namespace.yaml b/kubernetes/app/paperless/namespace.yaml new file mode 100644 index 0000000..37ad102 --- /dev/null +++ b/kubernetes/app/paperless/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: paperless diff --git a/kubernetes/app/paperless/networkpolicy.yaml b/kubernetes/app/paperless/networkpolicy.yaml new file mode 100644 index 0000000..f45668f --- /dev/null +++ b/kubernetes/app/paperless/networkpolicy.yaml @@ -0,0 +1,136 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny-ingress + namespace: paperless +spec: + podSelector: {} + policyTypes: + - Ingress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-ingress-controller + namespace: paperless +spec: + podSelector: + matchLabels: + app: paperless + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: traefik +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-app-to-db + namespace: paperless +spec: + podSelector: + matchLabels: + app: paperless-db + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: paperless + - podSelector: + matchLabels: + app: paperless-backup + ports: + - port: 5432 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-app-to-redis + namespace: paperless +spec: + podSelector: + matchLabels: + app: paperless-redis + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: paperless + ports: + - port: 6379 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-app-to-gotenberg + namespace: paperless +spec: + podSelector: + matchLabels: + app: paperless-gotenberg + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: paperless + ports: + - port: 3000 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-app-to-tika + namespace: paperless +spec: + podSelector: + matchLabels: + app: paperless-tika + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: paperless + ports: + - port: 9998 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-backup-egress + namespace: paperless +spec: + podSelector: + matchLabels: + app: paperless-backup + policyTypes: + - Egress + egress: + - ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + - ports: + - port: 8000 + protocol: TCP + - ports: + - port: 443 + protocol: TCP + - ports: + - port: 5432 + protocol: TCP + to: + - podSelector: + matchLabels: + app: paperless-db diff --git a/kubernetes/app/paperless/pv-consume.yaml b/kubernetes/app/paperless/pv-consume.yaml new file mode 100644 index 0000000..45bd588 --- /dev/null +++ b/kubernetes/app/paperless/pv-consume.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: paperless-consume-nfs +spec: + capacity: + storage: 50Gi + accessModes: + - ReadWriteMany + storageClassName: "" + persistentVolumeReclaimPolicy: Retain + mountOptions: + - hard + - intr + - timeo=30 + - retrans=3 + nfs: + server: synology.storage.lviv + path: ${PAPERLESS_CONSUME_NFS_PATH} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: paperless-consume + namespace: paperless +spec: + accessModes: + - ReadWriteMany + storageClassName: "" + volumeName: paperless-consume-nfs + resources: + requests: + storage: 50Gi diff --git a/kubernetes/app/paperless/pv-data.yaml b/kubernetes/app/paperless/pv-data.yaml new file mode 100644 index 0000000..743364e --- /dev/null +++ b/kubernetes/app/paperless/pv-data.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: paperless-data-nfs +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + storageClassName: "" + persistentVolumeReclaimPolicy: Retain + mountOptions: + - hard + - intr + - timeo=30 + - retrans=3 + nfs: + server: synology.storage.lviv + path: ${PAPERLESS_DATA_NFS_PATH} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: paperless-data + namespace: paperless +spec: + accessModes: + - ReadWriteOnce + storageClassName: "" + volumeName: paperless-data-nfs + resources: + requests: + storage: 10Gi diff --git a/kubernetes/app/paperless/pv-media.yaml b/kubernetes/app/paperless/pv-media.yaml new file mode 100644 index 0000000..97a8d9f --- /dev/null +++ b/kubernetes/app/paperless/pv-media.yaml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: paperless-media-nfs +spec: + capacity: + storage: 100Gi + accessModes: + - ReadWriteMany + storageClassName: "" + persistentVolumeReclaimPolicy: Retain + mountOptions: + - hard + - intr + - timeo=30 + - retrans=3 + nfs: + server: synology.storage.lviv + path: ${PAPERLESS_MEDIA_NFS_PATH} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: paperless-media + namespace: paperless +spec: + accessModes: + - ReadWriteMany + storageClassName: "" + volumeName: paperless-media-nfs + resources: + requests: + storage: 100Gi diff --git a/kubernetes/app/paperless/pvc.yaml b/kubernetes/app/paperless/pvc.yaml new file mode 100644 index 0000000..2085762 --- /dev/null +++ b/kubernetes/app/paperless/pvc.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: paperless-export + namespace: paperless +spec: + accessModes: + - ReadWriteOnce + storageClassName: nfs-synology-ssd + resources: + requests: + storage: 5Gi +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: paperless-redis + namespace: paperless +spec: + accessModes: + - ReadWriteOnce + storageClassName: nfs-synology-ssd + resources: + requests: + storage: 1Gi diff --git a/kubernetes/app/paperless/secret-backup.sops.yaml b/kubernetes/app/paperless/secret-backup.sops.yaml new file mode 100644 index 0000000..02dacb4 --- /dev/null +++ b/kubernetes/app/paperless/secret-backup.sops.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Secret +metadata: + name: paperless-backup-config + namespace: paperless +stringData: + profiles.yaml: ENC[AES256_GCM,data:RMCLu/OXCXil3GXg/OK/iknpJFDe0jN9V9kF3VY27VkiKhP+VibgGgXL1e16uRahQgiMyUDjrqSA/gLF43xPW1NJL+igr4hn2gJC8CWdu8QVbWWFwswJJMkaEFrIfFHI4RuL9KOd8XOE1ZUSoV33kGKP2kVgRays5H2oTkafBjGzFFPt5kVbj90Q0J4Pn09NcR1Ik7m1aqzSS5Ci7rHnlvXVUPubT9mdZSAChPuCUG34JYLV71SD5jvO1rFyhHWaC//oc6K18v4l2C2t+JPBEzJJd8eVZ+XnbZk8+IDDyHT0/JzInZI3bMj6jva5Y4fIS4044O+LLuiAsD6V7Ax1MTM3ORbDZciP/Kr0VQY/6EVs1wbFccJzPCQmVljWeBW3GzqjbuMzOkvUGCmrW1UbcK47mDqhava3oi4Nz/cTuHAM9pqc1R8keC0p3eN/ekZTyd+ByDl8Cfm0ec6YobCI8kIhmadsPPreO/x6vDTCUHmoqeabO54ISixvozEla06vMMuG/0cEqWrtKFBCWw/llnf/RaFskqZ++5kA6NfizZGohhvqBv9TLEsD/oFyBY1itHFPpmi5kcYZhp32GDUCa7nn4A8UfLRu1XGHl5u4wapZ9JPlzCy88dWjQN9jjpINwqkj9CMmNf99SLUzGEBocSuD5JkhS1DnP/17G7qu0jQnISZ83xHlyOzDVwnZL2Fxthn158p7jAwI+qjzsk+QFtXZsxq4WuN+kVUyjZPu8pz7qOWr9Y6H3nAOP1RBrAEd6fjOoAs5wQNePLrHxiMWM99sXf4fLWY59+wvLMhfBvN+yCDK9QaENu04W/5QElE/Htu07pRZyiPQpSCizntbttlIFgXQKKcz88Z0oq2pQmuwPCXQ66hfcoQHK55p9ganhOi65jVPdCDXhXrBdzpGg+LB7zrUEXNw+wixTEH+RkFrKZX3k4SwrQZriFtXaNun/cHfPW8qe/wX9N5wPtEy0QXFkKZeFRWt+6fO9Zx2Zw9jUEDgDNovWoxFQralKWeNPen97Na8DyAzhNBWNFSfc0+V/mP7SvjHO3U6QHW1bjHLB7NPOEw5vP8kLQCVkeUvhW6bneSHboCz8Orw2YUNR2qqLr88Vt47j5yEa8uJf3zX1LFBlIcK2/nACsgwdCEuHkZFSKncthAzzWJq+biJ18bSAbLdHtPGhms7bFd/lXxXdCQ4qzQUUbZm725Lw2W+oB3pPB/qR+TcW2e4bBTHtCTOtYeN/nfih7lzH+Y93ceGSVLsSNwy4tUZbZ/kJqmT/0hlew==,iv:u22vr6RJs9P3Ld6QF2tc8uWsxspVhVhOZPr+8jGEUzs=,tag:UAA8IvcNgjTnXCAwEC6WKw==,type:str] + restic-password-nas: ENC[AES256_GCM,data:giPA+M18Hy4YKZD1vdB3ZH5cHtjF/4BU1pFVswLI+eFIBVYnBvPBUwQw0veuDHlT2uSZjRxgwietXOFsrQqhAw==,iv:bRIyljpontXfg+aGQSFO2xOd+5qr9A1jnz7lFSHPOmE=,tag:uDq7jrv8amQKK6xhxULbSQ==,type:str] + restic-password-b2: ENC[AES256_GCM,data:nh43a0h7esMgHD9l4HcS2D3hzveoP9/KVEgHDgOtOIxDYwb1t2W7fsHSiNk7TKAXg0t/OiS5xV8cKF5xCGakvw==,iv:8kiKWYRHiMwQJzZcwr8rVjvvlpjAGf+p/1GZHbDSGqw=,tag:QvakLN/wCciPqj2tC8dM+A==,type:str] + B2_ACCOUNT_ID: ENC[AES256_GCM,data:AXnixNRDDfSVsmzv7kiSIfpWyeeA/0OYNA==,iv:eGpFqxiV3DOz4DcldsC3gbF0bLyScaljagWXicI29nY=,tag:063z8kIC0vVTqqzMctItJw==,type:str] + B2_ACCOUNT_KEY: ENC[AES256_GCM,data:N/DqdOJRWow7WxSnfkMpV8KmqU1lMNuMeju7vvDW/g==,iv:nP076fQTEGmTkPlHFns/yWsPL0IWyRZ8YzsoW3PkP0w=,tag:dF/7LzqOC61pziYGmaKAEg==,type:str] +sops: + age: + - recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBacmJVbllKQ3diS1RPQXZq + ZXlPOGlRMXgxUTdTbHI2SDQwZjE2V1ZCU0M4ClNjZ01jcVRxQklWL3MyVFd6d2VN + VkYxejlEejV2VjVsRUxiSVNHaC92aU0KLS0tIDhrUjNPRHJPUnFKV2ZaaFc4VUxB + aUp3cTlCZDhtWU5ub2d6UmhobnZiS1EKr1ua46Om9jtPhkUJsT0xA64Cn+uCK3T6 + wgvx/dtvSgiZhctA/WrUneeRcSmmQmV8GywA4moGYfyRy+8WmyGdhQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-11T12:59:46Z" + mac: ENC[AES256_GCM,data:W+7ZEiJn+FAPwFGMBkDojOncT3hs6ZGjLmKZHfK0laVLmqFQVw83nov/NeCGMtWPI+57KCrVwoinhdKBAzW090sHE9BdxsolcT7KgyIQvXBjcrlXtWK4jLijAwiT4hbdr266LvON+MaM/Qm5RsFWKNZVThxp8AxhRXalfWKYkJA=,iv:WMt79oGLg9G4RZZvXro3WVtnCvgDA+rDNEqyzngPylg=,tag:KXV/RTlmKHIbkP2j6dAA3g==,type:str] + encrypted_regex: ^(data|stringData|email)$ + version: 3.12.1 diff --git a/kubernetes/app/paperless/secret.sops.yaml b/kubernetes/app/paperless/secret.sops.yaml new file mode 100644 index 0000000..a4ca42f --- /dev/null +++ b/kubernetes/app/paperless/secret.sops.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Secret +metadata: + name: paperless-credentials + namespace: paperless +stringData: + DB_USERNAME: ENC[AES256_GCM,data:rQ+OaNpb1oMv,iv:GA3HVRFNeY0Sk2h3aUKsDidDmUIHsDY4jBB0L2bXXi0=,tag:I5+Th/sdPDXX8nTUR5gn6Q==,type:str] + DB_PASSWORD: ENC[AES256_GCM,data:SOj7NJMaLypZXO/35ZLQOvEtVzTG0hb8oUHw7umFQdM1pLT28u8h6OPV8gaxccmPoSKb5tDEfsPQFybxGPcv5w==,iv:0vTapzDX2Abq/YZHYph+m3ERHGVXIEjzS/amAii00bE=,tag:vGy6y+rYAq4RTeWj6Lwhsw==,type:str] + DB_DATABASE_NAME: ENC[AES256_GCM,data:uLS32XZBHNuz,iv:TkynDQNDafivNROzpa1CJlrQXzPN+K/I9rSwStJ4hS4=,tag:Xp9nWh9R5u4s6GbleW8Y2g==,type:str] + PAPERLESS_SECRET_KEY: ENC[AES256_GCM,data:xda21hgfizCH5nBoJZIGWF7hooldk532IW+LcXK5dSmToRNmZB9JS9xW6592c7sFsBWeDakN9w5bR6r/ROzg1EOTNoYBJSD/9MpAUvx3zFMrQnexFFS3BxR2Gg021kg9y/VZDivw9+y/K/T4YRrQkitcgaO5CPOW6h6w7zyQ9cE=,iv:L9nhq6kl4zJAY8qt/7mOmS0jhSr62QpAMAVRA21/o10=,tag:eZFjcZ5bA3MbVLTY+ilJgw==,type:str] + PAPERLESS_SOCIALACCOUNT_PROVIDERS: ENC[AES256_GCM,data:71iqbb97bSVJaeGzrW6WvkJTyILyEMWCYR263/k9JcozdxV/BM0S/1MM5KAiMJSoNLCl+p1ve3N+cH4CUbvsLnvvEbVuVRlkZ3EbvDLiepJFQjobKOHQKKV1ag844Lboa4KgPC1Q0pCoD+7w5GAA1f19GBesqfhP6OhozXJ3Q4xlU9rbF+kJM6f5gTbEji+A8SWa9J0iDk1r1Ehm9Unlq78KcCdqH8hDSge0+dKezlJ0PaoGnvu+dwiuxa9TkZClUU3CjyXuxcnkgBr3KbCZKsWzsLAtwjkM1ifff22v2nzGOaZDbjdUdc5jmgf3KQr6Sp4N68RisTo/OWP7y9EpHO2vk83UuzrTBdxpMQGfzqXj16IztIyrSVRsbi6Hvb3wXBV3I7J+fxDA9c0HKYa9YLJaQ3Yz+X8Pzdpwnrub5ZC5uZosYya9bb1KPzVGzLtswWTYS6j3AirKyMGyTSKF60id2+Z+rl7VvWytgQ==,iv:Y7Zi5Q4cEcTbLMUUH7BJ5p0OqBYRwl4jsETJ7cASG0Y=,tag:QySlG72Q/f2MoNWWN7JWWg==,type:str] +sops: + age: + - recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRbXQ4VzBrQzJPeHBHSzdS + d3pvMkxXS1NoS3pYVGFoT1p1b0prRklXaWlRCmN0cnR4dy9KTHhEM2MvWEsxMkJS + dHljeDY3RnIwRDJOLzFHU1pDNXhEaU0KLS0tIElXdHFtQUphQXNoVHljZkZUSzRR + Y1EraGNRU0RDTVpoeG94WjZuYzJRczQKecyrOUBpDBPddN8H7IqCttJ0/yhIr/Og + QOjtSNjJJF23Wtbtty/4VdLzETnhC3bBipORVattkpVqnSbXOEGd6g== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2026-03-11T10:54:47Z" + mac: ENC[AES256_GCM,data:j9m8LLDSBSxWHOQnYvl80tWCb20XUll60M3+vgWjfCOqXzKyYDOR4LQJreZncsSuw9Y7Ua1tK8/ZgDP4upOTx52He+lEL53wETuTzQCUzvrY9rkCtcR5A5EGWyusbn729+pk7HFxIvibZzRVhitP9z8w0+J5V13nUe/xMAsJQ5U=,iv:IyNbAoSNE6xuDJdcAPeCDt3EVuTAwkXpoQVXByjkN1Y=,tag:N6EHm3g0tkyz4uI9MZ9nYA==,type:str] + encrypted_regex: ^(data|stringData|email)$ + version: 3.12.1 diff --git a/kubernetes/app/paperless/service.yaml b/kubernetes/app/paperless/service.yaml new file mode 100644 index 0000000..0c5b0ae --- /dev/null +++ b/kubernetes/app/paperless/service.yaml @@ -0,0 +1,64 @@ +apiVersion: v1 +kind: Service +metadata: + name: paperless + namespace: paperless +spec: + selector: + app: paperless + ports: + - port: 8000 + targetPort: 8000 + name: http +--- +apiVersion: v1 +kind: Service +metadata: + name: paperless-db + namespace: paperless +spec: + clusterIP: None + selector: + app: paperless-db + ports: + - port: 5432 + targetPort: 5432 +--- +apiVersion: v1 +kind: Service +metadata: + name: paperless-redis + namespace: paperless +spec: + selector: + app: paperless-redis + ports: + - port: 6379 + targetPort: 6379 + name: redis +--- +apiVersion: v1 +kind: Service +metadata: + name: paperless-gotenberg + namespace: paperless +spec: + selector: + app: paperless-gotenberg + ports: + - port: 3000 + targetPort: 3000 + name: http +--- +apiVersion: v1 +kind: Service +metadata: + name: paperless-tika + namespace: paperless +spec: + selector: + app: paperless-tika + ports: + - port: 9998 + targetPort: 9998 + name: http diff --git a/kubernetes/app/paperless/statefulset-db.yaml b/kubernetes/app/paperless/statefulset-db.yaml new file mode 100644 index 0000000..a37a920 --- /dev/null +++ b/kubernetes/app/paperless/statefulset-db.yaml @@ -0,0 +1,65 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: paperless-db + namespace: paperless + labels: + app: paperless-db +spec: + serviceName: paperless-db + replicas: 1 + selector: + matchLabels: + app: paperless-db + template: + metadata: + labels: + app: paperless-db + spec: + securityContext: + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + containers: + - name: postgres + image: postgres:18 + env: + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_USERNAME + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_PASSWORD + - name: POSTGRES_DB + valueFrom: + secretKeyRef: + name: paperless-credentials + key: DB_DATABASE_NAME + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + - name: TZ + value: Europe/Kyiv + ports: + - containerPort: 5432 + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data + resources: + requests: + cpu: 50m + memory: 256Mi + limits: + memory: 1Gi + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: nfs-synology-ssd + resources: + requests: + storage: 5Gi diff --git a/kubernetes/config/cluster-vars.sops.yaml b/kubernetes/config/cluster-vars.sops.yaml index c285567..bf55794 100644 --- a/kubernetes/config/cluster-vars.sops.yaml +++ b/kubernetes/config/cluster-vars.sops.yaml @@ -20,6 +20,11 @@ stringData: PODSYNC_HOST: ENC[AES256_GCM,data:MK+WWo8R2uS45U8suBDusOp922YqngM=,iv:7QfuVU6ICEmpNwtgpnXa2phwP0+0pcmv8w3CJSLwvrA=,tag:z6qizhm8fzzDZq/726kKsQ==,type:str] PODSYNC_NFS_PATH: ENC[AES256_GCM,data:O1ZHSOsmwe57nY0T42pHOHcc/aB9,iv:FS4Yb9F4mzrvKni0hg6HD22R83v3YoGlDAeEPBc4RzE=,tag:f+Wi8BOPIVod/8upGZmw5A==,type:str] PIHOLE_HOST: ENC[AES256_GCM,data:LysbDrRB3nmYkuJrvnNTJ4mlN52w3Q==,iv:pp1wubLuF69IRFJtkLlPC1EeW/whQNLwXeOiNvFlsPI=,tag:rSgcD4HmzJsynuPwU4GB/Q==,type:str] + PAPERLESS_HOST: ENC[AES256_GCM,data:M9h/Z0jesAhEZ30Lm9SrXesfVlTrL3EkxA==,iv:ZOXF8kRnD/2tu/uFRJ1IAwQ1MYR29aepJleBxfrBm8k=,tag:3NQ742Y2zeJbyz/8Od2GLA==,type:str] + PAPERLESS_MEDIA_NFS_PATH: ENC[AES256_GCM,data:anrMuEdnLC5+EmvgZeXXsJSg,iv:aJDdeUAvMy33HKGd+y4YWFVPRqgxi7hAuTVZSEnHKMI=,tag:zCKmxT0BCyzUubaQOkbXHg==,type:str] + PAPERLESS_CONSUME_NFS_PATH: ENC[AES256_GCM,data:q4s+bz9Zvs9dXRoCO3MyaHe/7h5fw0Y=,iv:CkInWz0cI1FMWMxJD+Tfy+IJL3HXQX0fY3iEZ4qed3A=,tag:PwCnIqMoRiVWPFQ1uNGODA==,type:str] + PAPERLESS_DATA_NFS_PATH: ENC[AES256_GCM,data:N3JgoXmQkZULA0WcABuPcuKkoOgoyPcnB5EpqS3iykGRtU0=,iv:BUnaXtjMWibztesA6T5nm76RDZFfp+gmzSPa8oinyKc=,tag:yb7/3aHut+y0JJXjqinVag==,type:str] + BACKUP_LOCAL_HOST: ENC[AES256_GCM,data:kqF7CnCVd4lEnQL2xWOibTR4jG25obGjVLA=,iv:EPlmBtLeNtEu5e9AGuMt7hjn53dgpWSVScOfPDqQMwM=,tag:fHdiv+FPG/6MlJiS4dcPTg==,type:str] sops: age: - recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc @@ -31,7 +36,7 @@ sops: LzhUN3Z4cExIL1IyS3ZCNWh5aWpLbDgKQ7c3MmLykA00NaLoctKVDfJvPqTqh3Ia cDZJUc6jYJXOJYM6YYyZOYcCL2z8V2RpIfA9sPg8PB2eiipZxjk+Cg== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-03-01T10:40:47Z" - mac: ENC[AES256_GCM,data:ilvEM/b6Dvl6FpScV4yZAFnhv6M3M+UQ+sFIUT0wN95N1ndN0uk0vcgSlyaxKMZIj0UjepTqgu2i+/88WKuOlkDWoyRjdXsTcQvaOyVM5UQWwilzCZjpRCuYE+4UVQM6DVvBTplkhaBZcDSrTpBnHFyTdEb9iysTt2Ki6UP7Dx8=,iv:4UJ/fjG2KUQVBZIFwgCReZlgdItCTkh47HLJvPP7HoI=,tag:WGRK1ulWu2FgxawKpq3BfA==,type:str] + lastmodified: "2026-03-11T12:09:29Z" + mac: ENC[AES256_GCM,data:slhySxwlC4iLR/Qcr6jloPWbJiWiIyy1aMsYYqatu3pM1bOyBDkrILU8SmsFky8lRG1ImYPMxb6nSmWqkA1i8QWQE9mkGSRN6dUw6E33d2l9ooA6vz5EMmjmO5vg/tzyrskCkeZqdGP+XecvwZMKkA4bLYr7ChKCRlmLOn9O4g4=,iv:b92aWORGUORbCNCg3ZcHr+BRntaOYg3q3aBPV84q680=,tag:OidID+Ymq38NpGH006CaJQ==,type:str] encrypted_regex: ^(data|stringData|email)$ version: 3.12.1