From b10e790c7eff42f431318be9989003868459a3d4 Mon Sep 17 00:00:00 2001 From: varpon Date: Fri, 24 Jul 2026 22:13:51 +0000 Subject: [PATCH 1/2] build v3 of paperless-ngx --- .daemonless/baseline-latest.png | Bin 24012 -> 25392 bytes .daemonless/compose.yaml | 15 +- Containerfile | 420 +++++++++++++++++++++++---- Containerfile.j2 | 418 +++++++++++++++++++++++--- README.md | 161 ++++++++-- compose.yaml | 10 +- root/etc/cont-init.d/20-paperlessngx | 4 +- root/etc/services.d/consumer/run | 5 +- root/etc/services.d/flower/run | 5 +- root/etc/services.d/paperlessngx/run | 6 +- root/etc/services.d/scheduler/run | 5 +- root/etc/services.d/worker/run | 5 +- 12 files changed, 913 insertions(+), 141 deletions(-) mode change 100755 => 100644 root/etc/cont-init.d/20-paperlessngx mode change 100755 => 100644 root/etc/services.d/consumer/run mode change 100755 => 100644 root/etc/services.d/flower/run mode change 100755 => 100644 root/etc/services.d/paperlessngx/run mode change 100755 => 100644 root/etc/services.d/scheduler/run mode change 100755 => 100644 root/etc/services.d/worker/run diff --git a/.daemonless/baseline-latest.png b/.daemonless/baseline-latest.png index 6c301e85855a39af7266b789b37c23588c229d38..6678a050e20e151981868febf233c24a881a7d88 100644 GIT binary patch literal 25392 zcmeIbcT|*V_bphofmT~E0V>IEMNpE0NTy7Hh=713iHd;aoI}wjR1gdVk)%Wk0!oer zk|awkGDs>?Q52yHsC3Wk_kA;fqkHB?sA)zy9I(OToL5e>m|w%W>pq@QIxd zke{fB2R^VMKYzdS&kO!phkxSXpS<`79{>mb!O4H<;2%2phYtRsgMaAY|66o0@K)6D z%Y*tzXAu5gPez~)s7cJG;OMvb*Vd+1$pt`eB3nJdP6~XqGr6bC*f%ke0H+{ zeD)>Icno7}SHW@>Wi0f1@*a1=amuo#F@bh>wiG^d`Vdqn!HO;0<~VO!XTu_9G{29* zZ_VIgLA^5R4?g`H>dicQaH$;8&~GC}Hn`yCDfK-}W4Q^~K)2!hI+? zc6gZ?^wbJI?2QHx+x4vGw}P0hnV2r~jRDID4xw8UIRzc%YaR0HkwOPqmCl{oujw$X(d@l^;g--n}t4B8jFYR|s%N_YcCu zcsWat`AmIj;xjIG7<`smI?q15%rEEhfJ!Dv%GtL@iyo}KX?|gGleN>N+mwZlwK z3wiw0zrKE~KgY7v_@%d8xtcP`Kq zl|wl--d{LFG7_&^?>6-w)yuk_`8!#nhpXL=n#UHZFWNl4Idew%FBIy{b@-0&Dtx}v zuj|;+0oe&~BNacNa&MCfaTE2IAMG~U^S-Jbo{DGfQd1iaZ`krH9@I`jJlyvdxDh$m zmyHWMCaDt66WjH!6U|iM-aWVH@aZsaX(tA4D_j3_OA?y7M=(BrJcz1{se2=!Zrz!v zXy2bB>pq*Bnp$s=ZCc?qTJD*mI8^Srn643b(Rr+L)8+)@LbBUTizoAyxOw#h@rwbg z(!6}cO1IGkkpv_bPsKuD=lhqgeA{<;=iIJ+oT-jX3YMvHPC{d(s5M61yvlE52fM<# zmUs?feFge@&oN%d;bOwa5We^2iY^m1f=L0Zi+Na0^hCJ)@+WgFqtC#6^owPaQ0P-O zUO$|vH@rn_nD2$W11v~{?CiVEuMICExrP7Z1-#%;cX@4K9w@bp8& zd;(*0DgsSgsoNw?Njs03q+WlWsOaAqp?#=|)W|Jfy*c^p;K^eDLZbp}RsKK^nw~+9 z;rW(wB9_U>&Z~ZbZ7iK7x~%9Km~vo(^6Ty{;_@QIWvtuY&9r(4plgPmDnz%lw_gTe zd)7>oa}Fa(auarNK(RO5K)kj~W`BkwX0=(6xz&kDzA1d`L(OYdzQNDNC32n%r}&Ox zTj-SWqzO6KV6zG@LX?5o)@+=!llRRLck$ARhm>&))|)t{JzL*ZW{=A~raUkgF($}V)8;=CLzO1xeb4r2RBfj{Awb}MzySA1nVb};!+{5JhE=r)JQ}2c@5^*C%5CRHx~M0%!9TXxXk05Yct~AZs0o-3T`JIj+1q_ zhFI=Uq>PC4;juYu`*4hzZ5{+LUwHr?*DT*%nW)_p4!2O%G1-C|Mg^zw4sr zv`tbVx&Ha#l$+16@y54$ z+ORQMccNb1W^C?4<#QheczM_mW7!@jtu@S$PgqZnbGlRRVXLBg*jzRo2e)MuX^X5* zZ>FxKoF9DyM_I&)Z>x(zFc3N#HC7n9JJu~D6gjO#_DFtU-GAI;&?-h5qefXCW&Pvm zD~lRfe+G4&sUqJ83nS^fIxSS`>VBFp2>0AFojLk^cx!d~C4!*#2wI%XtKMFK(C8)_ zLGZ!jQ(&1-DUnL2!b82DZY<)^8@;-K1jEZ+^(=Bq(k;obnA9Si*{~P$)t^@r1IdGP z&ZXobd-T@y+s|I76Ipnh#$~!xj_p(2;n$U{%w_7Ky)Iqf$9QKMeg-1nE8=uV52l?h z#(40I(&lo?KC@DcC+$u$0lJIvO}4G_XPo4t3vs#UtZ(2NGZkT9kMbig4)| zKT-o1LmLGnz06zfXf^A zBC@>8avraCozNT&&kOkZge%dl<;<|D&-P4=`PkV@g1^sY8{{zmBmMur-eb^7$qncZe2qw`PGnEMvR)=X zzEfo9SRu){{6eB1pb^3zC;;?RhGQ%{FN!Wh&PkYVn0Zeq2ksaa}aCl*`2nDK9kQsUJVg5tMtjHlVWJ| z8A-*qJ=VjEy}72;lnA94#ex_HjYK(jIe0Ah5U|`CU2BN2VZ}SJ)dYrjb816&bRlNx zA<;VQrB{}O@(mDXr(VY$_zi?YxXk3U5dI*UmgKQ2f1BG$DOfI@5_t-FdofSTrDf%3 zUCGcItewpT$eu=Xhm!&VX*Z(I4)bFB9uCRVx^v4Hvd&vJrrtjhKB|4rRjOm|mdV=^ zdX{Pg-)O4ZTSEA_7isY}0;9Y04rs*7oM)_@U>Mygu-1s=APkpu&iw$4$4tLg4Wf*C zYxlR`k;rIJ%vf6A<`Fh1^Z?y+2tlp<3OUFk>iYcKG+iKF^28(dMp(&rJNIdvVk>>J zmt)8&T*S0|9d=sMW9|nbu?R39kWs*~#mgk=@XgxgA0Mwqa-e}GjcgL4$QMY*`=tSp zc)dps67~w~=UmUfw6T%^ndpWF98kMY_;5;fwL=W#BZ_J^${-)O%W z&HWtRt(guvbAHJ8P(WX&!rIN|Hv*DM8&Q)9@Is-A5jnEN2mFWoaQV?b@k&q^e;+=5^8qf` z)Vkw68pCk!RP>(v7}B`wT&?1AH?_572+=$$A&2tRsvyM+2_Rp9HTBg`3BIc}{d}h= zCpWnAuwzA}`-kiX^I4DyN0=hYdo+@i=xvgn3IHxP9r4Eq->qUyqkW>HZYKq8+rB-m zQSLGS6(mzwW5CL&sw1QC?#6bc(7OeztZPVYBCGfEk7E+2Qg%JdfDMOKS0dFP9-6J2nj@vImav7p_8$4( zrVJ`pr_60eNcaV(=y8G8@iJe_hjZreTTqTFM(gmt(=q_cgA6(qLmsp#0XT1 zb}VT)34}9RUpJ{G-}sW73%9U5T9LuLcGp|L?e`M)m0zjmsb=&oG>DOQX#VhZ%)MZJnat4g*GXLr@_%)18)b#xWwefX(^CRMB z3MqFXK_{=WD;^;{zY!_)y6Yo+DV9zi)<(hpZZBl#DQ!+X@?S0!BrUWd2o4B@sG|p} zxn;BO_W=QeZu13B>CZKd>&nka^gA0ESY3+utilEf?(O{J0hBofNSVVo1JSQ_D=Cyy z)UScVAOO~T|M3gYyoy1C-}jiqCt*4^D?i@VO~zmiv!Habj7OG8RIv)NtPORJcp2S50ONPDZnv%^l#4%71^6ePSFfKg>Qdy0nyQzf2CXvWaJYVrDP}XmMy5N*=46Ox`ALd;6X4n{7p^tUsf}Zhw$<3RugoT z%2d!C&~D@uH;XSPwFu{3a2QybONkg;kc>bQ9?H4v*bE4G!FZP|l?O3=uYy+MPL2i6 zJ<3QqC0D3l2XOPt{Rmd9U+Bx0yu)&m3{+y`QoAd>`cnJy~%?%rGCGZMW;)Fn%OC z3+b>S&kwzzS{GQiZ*MHdlxL~Xg37%Jat;IeJE2TyIb%$arHr^AzObnO`Oc_6u`0&Q z&-U-%c1>m=r~`$TEQn(QTe!MkVSyqj1^@>H;xai`%iWr zl2~Z4wZr8mfEiHlYyphDwT6$7u7(KTeEU@BN_R-SS z^mn_2xm25_n6XJOrpFc2>wu}%L~2=3`S?%9B~A}h-0>as)mHHyE<6wgnzWZgK7KX+ z^?Xl8Q(jc5Qd4e~mwb=KREYll z*%jA+=x@l9fu@&M>Sg)`b2|aX@7o22^MW9GPBc6k2YLW0DU$vM!7z?t(ru}o0SjNJ zad>@Cc7r6aiS{5;xU;J|klMzsgpYTJ#|{K!KXvqN+Q1M9Kq29l$TRM>ySN-EKLlb- zy0i4Mp)A_yH};lMmaedcI)E+P=^jM?H!tR+i;hDV*m}Cg-SG+D>6qivGdDZ>F#vnr z%RyV~s^USiwznfa1f^u;enVLpAkzh89-N801_)9K$<^Q;Y(9WsQ!lxlO8*?|cwiX) z+xpKC{=_u3CelRVv7+IHo(!#r($NF983|CwmMGtS9LK^-*Y=e6m^(lh090CO701SR zb;;b!5PeAE@_>@+Zrx|+Nea{vJl-R&&$S$IxuadG%wrgg(S@~+5~dR?)4oH7)(c5$ zdKvLG+>Z#|viR}gGE^063L<S5xbHQ z@I$0B&KyGr1z15~Y|e}U1NO!VS*Q9_mTiX<5-a@ZdECjV@&)ybWggdUw%|Uhn)T9%1l>8YwwPWn3(1Bo*38F7t~FF*=ATTYk?a z%+6cXb?)ZZF)O z0ES=b7^j$t%uo-avbNXfGlxmi1t2KE!g}eI2<2!~l3b4Am%rF1f$)dVjCYqNHW4K~ zD~KyB3H}~0y`HdYVd*WFa^T%B{Z$5Sfq`QZcj6{%cCd$eC1L|?zKz~VLtm>s!n|6j zo7RUP*g<3p4spET5*aGnh(*k@aL_0<9beqJRIMY7PucEiuK_5#$v&An`2l2R=}I!~@5?2TJpjMYf5h z;wh#-%MXi*>XqK#iK-E^+QSAt3&=eUr-8Z+UefZ=%f&l5`{*rQ&Ri1(FLJ%md<*9p z&vsWXP9qZS8?o`N{$)37>a}$G`X^IxxEY{7OZH z-~Ppf=7Q*qoUk{!IOPZ^$?voOpWi$1{fuRFu2CX+eZjz)Y1jR!Exy!aV^yeZnbEYL zRX>*-1dd|qo=P+g<$R+^#7!-+vHG`F@+IhY(|%m^GeMp565KWJbN?u z>MW}XW^xABeefx)`z7lrVFM>1%!B?(U))bS71=Zl=?QwF{Z}-cd(W5)*FNXQP^E4} z-`n+~(rc>+dE0Y%8_KBQe)F3MrR`4;N+CRM*M~aHs{9ZGdJW%cUSy{SIS{V2JqNXA zzD4arZO+d|1?gHzN@ym{eIad9F-<*Y863RfVns&!7hMtSf1&fh*wc$~$-RS$n{7U#oM5UIw`qW)>czJJUZAv}_twST! zM5yoY5&JCy&=`y-y?F1b<&hNC2#*E7b!gq=b7rq%UF@gSM*1C;E$3 zu6#>Y4oycNCsqQEX6b((0t$Y8Qnk^aOU92~jdKIO5Tl5cZQ0^eNk>bfI)l+bO3y?L!)xDrNlu0lOYwuDL-b-x{K zAW$4k&=`)C?P~lg}i3xmOGF>#biS$D?48R=xay4BukJ> zc;G%n$EjkbiQrBxf$jy<-bywoV5OF|#D5)(z|e6_e4?&+wW%;J_l4K=ix}s!**=2F zN1f-Z+7|c=i!PWZVq24?8(qX(hkhTeJp;Ofp z0Pk9g8@ikOR1|b63G3n!j`P;SH*9qMGOcVSvk`YnAGU8P^ zqY<7R6mbc}?&;d^&?{_=Kzt>is&N}>t#{{sf)r8u`S|dm2n9fNq$erVv*?^!HlH3h z+0?}6FGBAC>LI4Zm?8eIA9*Qub3K4M9V%fn-Fv=$S48;sQ>=3w}P3#2&q2_an6qhIFPP=cTCb6=_P@wI|1ekC*e9U+i8t$x^=M8 z{vU|NHB>kcBC!-)onFOG@Wi17b%1pp0%>_=J$ct&qRY+N%3V6r*5Yxv?P~HkV-@Q= zzCWA)$SxPRN0wt{8*K=_rbcX-J-6j#8yxloSMw>Mhb%3{l2TRh5Q<-1dGcY(Yb8P$aL=zn%* zJYC80PRixN0#8|TvXX%!7eF5Y>4v^CQjZpiRq%BKEXlr|`L(Y0u%Kob<6UulS1|O^ z5a*Q?4A~*p^0@a*pV1wZ9Q5lP*+9d{Tx#0bN)8nRmr#$%ps&reE=?ZYIJ-QN7-Ft@ zysw%>;wn@)xnT?KLjs?y^S6;^5?O73c(_-VU`JJ_DJuwHE`;vhe1didAeQSjvOBah z(>k0qEuh{cO=+w0a#($Xw1EoM82W8ty8}R*l0f$GrnDJV`FYZgdQZ=Z`-0I1>Vy-A z)Q*aDkN#*)C!MKR(3KG2bH@*}(^qORi;;Q0mPzAWS>fF4yE@G!ClhO5+VkiX-A+5E zZmo(j{_>l$S@k+vm3Ke_WU=(0KuD+bVkNAcs`{qemxS1U$n(4{M|l8{dCRyh4SaDG z*R0fUgquEyPW0$j)1HOuQ(!w=@`EK}KVuuq-0_Ipqs_^yT|rV$B3cy9ZD5^BXZz&0 z&8d@Fp4miy3dyHat3Yray8PezZ4z8JGubPdNpnP7ZTycIG2dj9FA@m3NAUb>TKLr- zNHPYXSu|Luzr3}j?lg6PjShviwy?$vP9r{zVaGzb#KR@<(2it+tg&f-A7!EMsRF<6 zfq49F*IuapPiYS9U}bCToj#xjzE-j_#TpXF#Xo+wNgw6UJ{tg85I zl(f3Ei#_^`&ej*P&Oh3J*l+{<_BD{(2&ZWO6XLg zIz%eR@b{gOteed)W(ujMfvJrO2PbiwFwt1wHmgxoC?o(sB z@G7XyDghezaf!8cr!DAzN@(6qPx@0aCdX_$;;Omivu=G9+VRU{eLxs*E%R98dt}8iDN@H%Uz}p%88q+}#r` z>vVQ#tda-7jO1rsG%)kEs@MTlaV6wseO+>ne8!e7#iP_Uq^>&YyjRE9Es{!{Uwe-i zQm}8IPum+a=SLHms=yq~z&|SZdIcm!#Xe+LyhJwG9;@=WUOTO7eVVgLmI@-0U*5~X zBvv=ZM>7u$S=f#DV0nY7J9S<=eaAX{r#naq#PmZbe<^p!E#^EwESN6lkf4W4 za2W(Ob6sJ!^*JWzs!q1{kWNQIblbeNoyCIJ)WU|p>H1t7GpvkK7?&NHbp*VXkQQDz zrXarx?cLiD=TN}O^T@&ogOEGrY?va zh?t2F06*gM_^-2xZz8rBm|s9I+!r3)GKoDLR=M=G)o*={1g@*~>Sw1ks0=8gX5^)e zJi`jt(PC9f-mMCpjq^23>Ox_{DB9<=XNulv9qZ_TJfx5KtCMdjqrkIOgILB!9$3^# zpatc5wm>q3b4xG7NPy_A50@fkU7doUTnDMW)|=t#3dr>~uXOSSxCJtvTHr%XU8I8! zSzgg`?Fwf;=9iuWo`(4i#AAl804PHs0%&9P&TolL&E6bZ?s{~#kJq&`N!Uc(tSHqk zrZIDvXi8~$&06>jVtU{22$p^hgbhI&o&TydIjz*C!E~48$5s*;wbEoKBTDCdpZ5ka zQEo^1<-Q%*C2|l{WMj3zfcSHo*jYM`v*mjDvW=HNvD|>1cT1k$F|}Btk=@zaFQO&f z%T*#P2uqNM5Zk5Vi*D0I?=|`LA6HWxmcH+}6W~7n!@Ht7;2Hsq8)Zh@Xo;JAoh?zS$rp*eTN_0tefGKXqHl5V`dJ@H`Kq(EEFRkO9I7};? z+!gxkJ#J9ETq3Xa8#^PD79ehpowK;~@ye6r(Hlh)QS6@#Gay2yJKthv%KI|qJDq)@ z=Tl>*f(Wqtap0ax;Y{9I)If8HQ^fEVbS=RiMgn$df5OmasXwcN*RU~rs*_HRUmXwioNcuOO*K~NTw13VJ zi$CbxD`LFSgkgu_mjSBb!yWKrD_#73)SV>Dm_kKqWY>_%E0p~B1izBn=%-tiBQvTA z{E&18zozdJ^88}Ywo~knJIDX3H)_UOkf?xb=W9viJQFGyg{&i7|9?76^xaDgo7Kv) z!QH^*>)y;iJX}QdqEtaRs41>{FeA$xd=Ghh6FNhvL2YPr$6d7LaV~W1 z+3xicYaiP|Qayp+|dXK+p4_?i80s7n%v;=b<+m&W{o+?Hts8yiY!{|0h2|Yml6+j%hEUvd@iX z5P3^RSAWz+XeN-==XKJCifujnIAR>BY#`mJi0k})Sw9aMcX<&=5iKEnw9l>hiFch6 zUk(TYk~a67=?fLdu8x+B!+Ku|ctCsn+^RKFcK-1cF4uUt0M{x#;;|T9G9E^uENyhx zTz({8z4GvQUp5R9ZY};isU9u#diyMwnVT=^UPEq0aqEIqb`r76p7cf_0^J>$ZBV(G zh)GLLHTf|=A`_a2m~-{R)!E|JwC!SfqMi5ZbDYVj+jiKol5t;L`NrZDD&eBV1nVOR zAK%9L4A{RV`rnVrN8J&thMPwReM3lW*3`N5Ol}{SnF|AVQl*l%u=r#jSM_vq2c-Rd zgI{qnMBBA*a@IKOHV9%l*X*m~;ag7i-?bwY)@#nI@^hda2UcDctMW6+^29`caxAGQ z7iW@iQNp}B0PchGO*wS(l%nNq+YJukguvNjvToDQ*rntrS9;G^jn7qpeHs*KCa#;L~=*gPVvTX8FSc@;+a`G%{e0e;*9}ly^7hm!>y^PE6OB*V*+vpa~GxMjv zbf0Z|DX~%)ftIozks;VFq%k_FnRf(=l$!Q(oxNS5|G6PF=31=F=Esn9M}8eFV^Sbi zZXc*44oM`~!Wa4niQBojdTx-5iklyH=-LQ7Tg`y7kao%$pZl>cFMNAbaw$k=I>mdO zU&eEx@JT3V8o$fZc~v4braLKfgIklWYziO7j`1>mSJebl%?M3Njg8In^a-dv95<3n zM%^r@RQ4wZEk8LtROIYcf0xU%`UHLG_!mfPr?JX1!>(jo|4C`Ao}#!;smpc-48smo zTDeE=QGUU>g|>0*=rw{F?`%X4+*bwD#c?Rrb@F^eD2K2~>B>}uLS&GQ-4=naGLG@a zOs}eoSASaDV&(OzDJ325m|w@Hot;dkx4qS>m-k=yBlXLdgN1k|{5z@dhV%pbX1+OmHbB+=05W@;U;&)oW$owY8p73!;lT&MicyavR3xnm9uaHnc<;o1WTM zcXJZ2M??cM^O9d-H}Xk4!c2Uo`7Qp{HmkGeT=vhC$EEFj1_ij%loHOn&lYFkhOx^j zqx=p(rdKj`Ga6Xr_{K3c{?xwQggdVe!}Pa7=&b4yp;uU4d?=UL9h0IPGlf^7Ue497 z+MeqgPZd+<%}Aa0NwZyvCv z7ypJ^%I+u;jn9z``GdZhWRn=!e=MUZqsjki#8FReyr%MtBWld#2nBMIgw>6{Ts`}q z^!B_;vfm7JL7gSlx3h%BDrU>M&HT3dpr&)CJ>HX7#ZWP6PI)C(Ma$yH9H-9YYf0#K z0^->s1ngx18S69s5rbYGIGk6_XjIRu)K9xH^VO>5f+LOkR|h`HdwI^eiO08%7%DGq zH!zVY$fsCzwW+t@qQk&ZZ#>Qqdys2-^M|@(l6nHxq|)b$f%&p8Zj|DQnf_AgyVn27 z>@)Zf$V`I1c=`SLH`*zxT1n?6`7oPzL&io4WJg@jvw}_Ra#^-&9v8md{D3*F+P{3% zmr|vD9eNXSivAtNL{7NV&;M2_2Xs=d#+mwVr6qJIf`ksPdVUPT^FaTcbG0dy*H2bw zz`q%2MPfGp?M6@v(Hl+kxlVs{RzC;HAv5u6lQg#(&h`jUx)$OmA%bc&^{f1fl$@)j z)>9&;2&>fP;d0Mb1m~cU@;eGemOyN0xK2Dn;03OoNOu6P1CJ5;3EJ)e5(aQxM!puV zortJ~>;J(EUc+8-5+_s-Jk=e@rXQweHrGA7o4A)i`)Wp%V(0^l#{=0ojmcX4?x&3%92KlZ&-6aYdY6C z<2-5JxM{FjvuX@C_gevSzQ85BU)2&%cg~%86B!>@JT15QV~dW+1GDi_b?XsMGYnkQFAzUmiRY%n?hyvYpRgSvDAw)v8aeUC=@%`GXLtx3io zyDKhj-IML<3^psM9lr`^;yai$6zcKDV)}#8JdBg`;GvoC2cv|9{A%Z@${r7af zKw2}CjVeOFc-rLpf4qQ1kbS<@O`#CFHBSFIvj1!Z{DT~ z&O`SAz3K8D5V~@{anWtIZL5Enn99}~BW+&xbw&(3J(-Hvd8SJ8}9o*0Fo0h=Uq zm$5PfbJwkhPxdi4h#@dV7exImUG%`q$E0NHO2TFzG!(0%0S5A^gD@mWGyGujx8b9! z3kKPD1cKyXaoJ@)nAPnLIU-^>u{jmq93_l@{QND02YdG_eyvk}u4c!LD3RFm4871< zU_%k3ri>u%dbrjW@s!%!*b>tex!j4iG|6nA>oV8Ue{oRdrEhKU@mQ5 zT`9=p?eO!;lSy)dy!Y~;`|Rl;PZZQ>NOLrJRIBf--f<~~>5n1&_PzO+;p2s|#A;GU zDOW_&xw~!N;~D#n-@bWMt_}Jb8ZLr#j+Q9VxN`0C+nHdGHYm%3fQ1s0N8$clp)FR# zsQiM`Z9vdMlGTkE%~2ukyAfaV<}S1v}up@`nt4qIT0 zSHid?%p*b}rVu^*>7;5H4}BApsI>ep#kj=oAkm@kYeObMbBL^C+=ri|GDl%y}c1ss3+pi8Wh81p=38PR@Pk~ zJdy51&NDjG?@}UYHX-CX)$p*S=rg)@vu&|m=0(;TE#IDm4kdWp8(t%_02v0S>;SU(>W3Hf*&bK2;+;aSA!~ z1=s(D#x8kz(3&&AWdI9S`p=ypWMLWt3^&x$EOIgcuK&GH!vE!sG(gvC{plychZv2A z+jI$!w|2UIhTLwewR7;F~&)XywpjB}L_?}*+N2t zFXpq(pTBuM|I0W?)byTvIj<}b;}s-g9|{Avytcnu&0ITjJ$)vK1{{lJk zKQCyek!EHzLw>1%!pe4IR17=|5NTR6%L98kT2v7ZqAetNk7H=BL*Q8~obMqlPmY55 zHeqq>^qWw*k_TfBquU#!{DHKXc}aLp@F=3YUVZM%GI9qN8Y*`3oUw|D6f&a~bYfv` zQC^3mCm_xhr(2@3b<$qI^pIwtoIg1pxY40MXE0ko58yOl_~K`Fg%4&6AH}hJ3_3fy zBSBsq!wBfh*0Uc>HUa~KOWa6ev+iSob*E90oz+%gD=t%)T51^{^bsG*ebK3MVO3pM zDL6z;wG7j!?1rf8dR_c1jUI#|RM_^F#^h@sa zM5%!K?|G_WOC$4@e?QtC!7W`4{bi<1(I_M`wR(e`+N^scTBz&O-A7Qc!YpLXm?|XK z05U9_YWU?|=5VV)&OrX%t8x9GVJMwBV_=Sj;?{e_&;83i~@m|WVemHMqS<-Px+;vt=J9!iANNVL)KMYO!ftF9Nj*Sx5SE62taIUHT#Za~iIE=G$^u5#1AJXBqH2bHjt)4)Xj)wp!51I+UU z1v*cxACsJHQ3z~olex*?SKR)1&tXjk#=z!WuPwHo3Pfr%n`1Q2T$%F3nV6QjrS-;i z#7Pr_DPzt9`IpDg=@;|N3TEN_i2{M2peru#+tOlScHCR?zSs0*ich;eL3gBm@G^H17!s3G%BN&2XnoB{0Dup zLs;?j^8wSJU^n%%Zl531b!O5tVAvO;m6B~LTqJ|b(GIL@e5sqxa6X79O$pZf0^#cn z2l~&nM8C{K+jNW+D!$Fod;HZ%;DNZ-KS%8VQhfpK{J*7p-kgPyhq0dU;M7pe#-w=-A~X=re{&Xw!0At49(?6N$}^+w`~5$H zBg#7|u>bS;0T#6qC;E1!FBz%bk<*PQGWT+R=VqPjv)B3S#B=>zvjQi2-wxK4r(sF5jJRe?&b&LvS>(0m{}SLP;vO(>cxo!F zBY#f9^pxt28#h3hb!)troT*&qa-w-oy@-q=^@x(tH60K^f-hchoQ?KQQpg+oQK0|i zJwWiUzk#vzTZ-VzvQ1K}!DmxmmY*CQ=A}5#PN^)<>+orAh|D`NSRKoQ!wdJ?Tz}uU zsBgYGmo~0NN6d(_UuSHKABj0uS|^|%UH-};YB)``Q9-upH39-^8{a0l_D!ET%Ayt{ z?LC&rUyb~Ii)#p=>b!iv*$&6C|2i?x`PoFlu6$AMz<<1e$vJqWQ%G0o)ityRKQ~Lo z_XzCfuAQjsXOR;7jST2ICWSJ8v;5^f&dPeMpll5;5}>HLDeT<@s@$eYK(s`eS*Q#(vuu zEgoNO(TKrvlP6ypOr0Cey>Gs?P12tj>=0$3};>s#C58T-YmOD-LRZ5E-1QNt|6OwyEFP3b93d zmQvfK7W-3|+hPQrWA9mYH-;r{Iz}E*^Zyex?9&`Lv09Uu?m_A%O$lE$*UIkvvcWCg zcISe;XNAwld;7NAeJX~z=R55@T|#lrzP6L<=pe>G6UEfUATv2F5lhbO(!#`EocO8E z_l`VN3Kg?Sz!f};H+HLa@o=@jQ@x}CT08gJVAxo5vH1Gn$*be#QyV_a1pLUh8s)O2 zpVo2Gw2gF95M8N^0tsyq@oS3H|NWLazA1vLmld_8D<(_&=7b+9|I2Dda6It6q-sYB zcd=ujOX&DT=OH`8+JIf=$aOpYy9RAWEZGR>$vA#T zb=+#;B&lI9M-`(`tW{rJexA(M!`1eTa^$T-SXl*;q!FQUjuF<+6fybWLS&LP;EycG z>_pT0r-pfH{dl_WqPs&~9{1RG4yIDua@?y@HKhg}mmXOJ^*9#8#ngLZi4DbL0c2N;msTJAo}%rxu(cS6x~7*1okiJ5K&F!fYBFeL^$pXW_wm#`%-qMf5kGO_LpcQ+9AU?Z>Hij`;`o z6n`rGbAPCiVZO!XT~$0s{!n`*q$cs~kjM+RE86l4r~W$mtWiYKC0xI}{iAL`K*Ge1 zhtI?X{b^$uo}|uNPnX(2pQM1YO?o(5!aKLZg|KG0 z8GEI#R`gk$L=4%pF2>vyH@@t<`~>gqROQvM%3kSLhVQ9>D%~Q8lMRJ(@!KLT$LUvl zH=y~4FmvBOW)`IPoBGVBt6%IGC6>)_JIe)Z{X7}sL?dX7S22buOVlZIHrmGAVbxA^ zKStDIuFpbj#(%kmk}==m@CehjpXwK#h6_D&W%%V?mzJr^RU5hHH2U9|%05AK#ac0w zMyVCYtUeW8l*YQOHk6&Jn(|Mg(kaBFpwxV2EWNzoRJMf(7x9k#3LEJMfx@pu#%7b9 z_+?)5(E6}&|BaKopleY1T-_i?)_-BCE`p8$G}Vj}c#J}wGYM=EOy?5!CA8y|Hu}oe z;0D0D3D%uK-t)(Jr4uot`YW-fj)LCae0^9e$#APWYWSk#C}yR`nL(=nJ4Qwi*Z}p1 zH1nq*E?|A3k1J;=fBUOsr-{q@^As$tCl91{HN5T}t2!LTP_f$c=gny3t1Tt|?Lhpe zx$z&*7}0fMc7tW=tQJ>j!6?*8gx*z(*5fIE)I1i6Lr_5l>BxoI(X(22TU@!AJ9ZA9 z(DFc`cs{0?fK>Y)i|F8^_ z3;!_e|0;Fi{u+}9gA@tK|3~1`Kd6g3@$a_QKO*ga8^$q%QjEzw?laa3nZFwe)Ey}K XeIhJ9!XLO0Fi=z0yq0^#;^BV-D86_} literal 24012 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/usr/local/etc/pkg/repos/FreeBSD.conf +FreeBSD-Ports: { + url: "http://${PKG_CACHE_URL}/\${ABI}/latest", + mirror_type: "none", + signature_type: "fingerprints", + fingerprints: "/usr/share/keys/pkg", + enabled: yes +} +EOF +CMD_EOF + # Install build tools -RUN pkg update && pkg install -y FreeBSD-set-devel rust gmake python312 libxslt && \ - pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/* +RUN < /tmp/freebsd-toolchain.cmake <<'EOF' +set(CMAKE_C_FLAGS_INIT "-ftls-model=initial-exec") +set(CMAKE_CXX_FLAGS_INIT "-ftls-model=initial-exec -I/usr/local/include") +EOF + +mkdir src && cd src +git clone https://github.com/qpdf/qpdf.git --branch v12.4.1 +cd qpdf +export CFLAGS="-ftls-model=initial-exec" +export CXXFLAGS="-ftls-model=initial-exec" +cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_BUILD_PARALLEL_LEVEL=$(sysctl -n hw.ncpu) \ + -DCMAKE_TOOLCHAIN_FILE=/tmp/freebsd-toolchain.cmake + +cmake --build build --parallel $(sysctl -n hw.ncpu) +cmake --install build + +ldconfig -m /usr/local/lib + +cd build +cpack -G TXZ + +# Cleanup to save space +cd .. +rm -fr qpdf +CMD_EOF + +# Build pikepdf wheel +RUN < +# ' src/binding/JbgDecoder.hpp +# mydir=$(pwd) +# cd ../.. + +# pip wheel --wheel-dir wheels/ \ +# -Ccmake.define.CMAKE_TOOLCHAIN_FILE=/tmp/freebsd-toolchain.cmake \ +# pngquant-py ${mydir} || exit 1 + +# version=$(ls -1 /wheels/pngquant_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') +# mv /wheels/pngquant_py*.whl /wheels/pngquant_py-${version}-py3-none-any.whl || exit 1 +# version=$(ls -1 /wheels/jbigkit_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') +# mv /wheels/jbigkit_py*.whl /wheels/jbigkit_py-${version}-py3-none-any.whl || exit 1 +# CMD_EOF + + +FROM ghcr.io/daemonless/base:${BASE_VERSION} + + +# Use local package cache +ARG PKG_CACHE_URL +RUN </usr/local/etc/pkg/repos/FreeBSD.conf +FreeBSD-Ports: { + url: "http://${PKG_CACHE_URL}/\${ABI}/latest", + mirror_type: "none", + signature_type: "fingerprints", + fingerprints: "/usr/share/keys/pkg", + enabled: yes +} +EOF +CMD_EOF + +RUN <=17.11.0" + else . end + ) +# Remove dependencies on some transient dependencies +' pyproject.toml +# Replace versions +tomlq -t -i ' + .project."optional-dependencies".postgres + |= map( + if test("^psycopg\\[c,pool\\]==") then "psycopg[c,pool]~=3.3.0" + elif test("^psycopg-c==") then "psycopg-c~=3.3.0" + elif test("^psycopg-pool==") then "psycopg-pool~=3.3.1" + elif test("^redis\\[hiredis\\]") then "redis[hiredis]~=5.3.1" + else . end + ) +# Remove dependencies on some transient dependencies +' pyproject.toml +tomlq -t -i ' + .tool.uv."exclude-dependencies" += ["hiredis","msgpack","pyyaml","regex","jiter","safetensors","greenlet", + "zxing-cpp","time-machine","cffi","zstandard","cryptography","brotli","ujson","aiohttp","ruff","pi-heif", + "lxml","scikit-learn","autobahn","prek","scipy","numpy","pillow","rapidfuzz","zensical","ninja","uvloop", + "granian","pywavelets","uharfbuzz","langdetect","tokenizers","tiktoken","tornado","pydantic", + "llama-index-core","rpds-py","hf-xet","watchfiles"] +' pyproject.toml +uv venv --system-site-packages || exit 1 +uv lock --upgrade-package ocrmypdf --upgrade-package pikepdf --upgrade-package uharfbuzz --upgrade-package tantivy +uv sync || exit 1 +uv export -o requirements.txt || exit 1 +uv cache clean +CMD_EOF -FROM ghcr.io/daemonless/base:${BASE_VERSION} ARG FREEBSD_ARCH=amd64 -ARG UPSTREAM_URL -ARG UPSTREAM_JQ -ARG PACKAGES="python312 py312-sqlite3 libxslt mariadb118-client postgresql18-client openblas libheif qpdf gnupg ImageMagick7-nox11 optipng tesseract unpaper poppler-utils liberation-fonts-ttf" -ARG APP_VERSION="latest" LABEL org.opencontainers.image.title="Paperless-ngx" \ org.opencontainers.image.description="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." \ @@ -73,24 +391,14 @@ LABEL org.opencontainers.image.title="Paperless-ngx" \ io.daemonless.upstream-url="${UPSTREAM_URL}" \ io.daemonless.upstream-jq="${UPSTREAM_JQ}" -# Update to latest patch level -RUN pkg update -r FreeBSD-base && \ - pkg upgrade -y -r FreeBSD-base && \ - pkg clean -ay && \ - rm -rf /var/cache/pkg/* /var/db/pkg/repos/* - -# Install run dependencies -RUN pkg update && \ - pkg install -y ${PACKAGES} && \ - pkg clean -ay && \ - rm -rf /var/cache/pkg/* /var/db/pkg/repos/* - -# Add python3 symlink -RUN cd /usr/local/bin && ln -sf python3.12 python3 - -COPY --from=builder --chown=bsd:bsd --chmod=755 /config/.local/ /app/.local/ -COPY --from=builder --chown=bsd:bsd --chmod=755 /src/static/ /app/static/ -COPY --from=builder --chown=bsd:bsd --chmod=755 /src/src/ /app/src/ +# Update to latest patch level and install PAM +RUN < /app/version" +RUN su -m bsd -c "cd /app/src/src && uv run python -c 'from paperless import version; print(version.__full_version_str__)' > /app/version" # Copy root filesystem COPY root/ / @@ -123,8 +440,13 @@ COPY root/ / ## Set permissions RUN chmod +x /etc/services.d/*/run /etc/cont-init.d/* /healthz 2>/dev/null || true +## Temporary fix for regex timeout +RUN </usr/local/etc/pkg/repos/FreeBSD.conf +FreeBSD-Ports: { + url: "http://${PKG_CACHE_URL}/\${ABI}/latest", + mirror_type: "none", + signature_type: "fingerprints", + fingerprints: "/usr/share/keys/pkg", + enabled: yes +} +EOF +CMD_EOF + # Install build tools -RUN pkg update && pkg install -y FreeBSD-set-devel rust gmake python312 libxslt && \ - pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/* +RUN < /tmp/freebsd-toolchain.cmake <<'EOF' +set(CMAKE_C_FLAGS_INIT "-ftls-model=initial-exec") +set(CMAKE_CXX_FLAGS_INIT "-ftls-model=initial-exec -I/usr/local/include") +EOF + +mkdir src && cd src +git clone https://github.com/qpdf/qpdf.git --branch v12.4.1 +cd qpdf +export CFLAGS="-ftls-model=initial-exec" +export CXXFLAGS="-ftls-model=initial-exec" +cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr/local \ + -DCMAKE_BUILD_PARALLEL_LEVEL=$(sysctl -n hw.ncpu) \ + -DCMAKE_TOOLCHAIN_FILE=/tmp/freebsd-toolchain.cmake + +cmake --build build --parallel $(sysctl -n hw.ncpu) +cmake --install build + +ldconfig -m /usr/local/lib + +cd build +cpack -G TXZ + +# Cleanup to save space +cd .. +rm -fr qpdf +CMD_EOF + +# Build pikepdf wheel +RUN < +# ' src/binding/JbgDecoder.hpp +# mydir=$(pwd) +# cd ../.. + +# pip wheel --wheel-dir wheels/ \ +# -Ccmake.define.CMAKE_TOOLCHAIN_FILE=/tmp/freebsd-toolchain.cmake \ +# pngquant-py ${mydir} || exit 1 + +# version=$(ls -1 /wheels/pngquant_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') +# mv /wheels/pngquant_py*.whl /wheels/pngquant_py-${version}-py3-none-any.whl || exit 1 +# version=$(ls -1 /wheels/jbigkit_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') +# mv /wheels/jbigkit_py*.whl /wheels/jbigkit_py-${version}-py3-none-any.whl || exit 1 +# CMD_EOF + + +FROM ghcr.io/daemonless/base:${BASE_VERSION} + + +# Use local package cache +ARG PKG_CACHE_URL +RUN </usr/local/etc/pkg/repos/FreeBSD.conf +FreeBSD-Ports: { + url: "http://${PKG_CACHE_URL}/\${ABI}/latest", + mirror_type: "none", + signature_type: "fingerprints", + fingerprints: "/usr/share/keys/pkg", + enabled: yes +} +EOF +CMD_EOF + +RUN <=17.11.0" + else . end + ) +# Remove dependencies on some transient dependencies +' pyproject.toml +# Replace versions +tomlq -t -i ' + .project."optional-dependencies".postgres + |= map( + if test("^psycopg\\[c,pool\\]==") then "psycopg[c,pool]~=3.3.0" + elif test("^psycopg-c==") then "psycopg-c~=3.3.0" + elif test("^psycopg-pool==") then "psycopg-pool~=3.3.1" + elif test("^redis\\[hiredis\\]") then "redis[hiredis]~=5.3.1" + else . end + ) +# Remove dependencies on some transient dependencies +' pyproject.toml +tomlq -t -i ' + .tool.uv."exclude-dependencies" += ["hiredis","msgpack","pyyaml","regex","jiter","safetensors","greenlet", + "zxing-cpp","time-machine","cffi","zstandard","cryptography","brotli","ujson","aiohttp","ruff","pi-heif", + "lxml","scikit-learn","autobahn","prek","scipy","numpy","pillow","rapidfuzz","zensical","ninja","uvloop", + "granian","pywavelets","uharfbuzz","langdetect","tokenizers","tiktoken","tornado","pydantic", + "llama-index-core","rpds-py","hf-xet","watchfiles"] +' pyproject.toml +uv venv --system-site-packages || exit 1 +uv lock --upgrade-package ocrmypdf --upgrade-package pikepdf --upgrade-package uharfbuzz --upgrade-package tantivy +uv sync || exit 1 +uv export -o requirements.txt || exit 1 +uv cache clean +CMD_EOF -FROM ghcr.io/daemonless/base:${BASE_VERSION} ARG FREEBSD_ARCH=amd64 -ARG UPSTREAM_URL -ARG UPSTREAM_JQ -ARG PACKAGES="python312 py312-sqlite3 libxslt mariadb118-client postgresql18-client openblas libheif qpdf gnupg ImageMagick7-nox11 optipng tesseract unpaper poppler-utils liberation-fonts-ttf" -ARG APP_VERSION="latest" LABEL org.opencontainers.image.title="Paperless-ngx" \ org.opencontainers.image.description="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." \ @@ -69,24 +387,14 @@ LABEL org.opencontainers.image.title="Paperless-ngx" \ io.daemonless.upstream-url="${UPSTREAM_URL}" \ io.daemonless.upstream-jq="${UPSTREAM_JQ}" -# Update to latest patch level -RUN pkg update -r FreeBSD-base && \ - pkg upgrade -y -r FreeBSD-base && \ - pkg clean -ay && \ - rm -rf /var/cache/pkg/* /var/db/pkg/repos/* - -# Install run dependencies -RUN pkg update && \ - pkg install -y ${PACKAGES} && \ - pkg clean -ay && \ - rm -rf /var/cache/pkg/* /var/db/pkg/repos/* - -# Add python3 symlink -RUN cd /usr/local/bin && ln -sf python3.12 python3 - -COPY --from=builder --chown=bsd:bsd --chmod=755 /config/.local/ /app/.local/ -COPY --from=builder --chown=bsd:bsd --chmod=755 /src/static/ /app/static/ -COPY --from=builder --chown=bsd:bsd --chmod=755 /src/src/ /app/src/ +# Update to latest patch level and install PAM +RUN < /app/version" +RUN su -m bsd -c "cd /app/src/src && uv run python -c 'from paperless import version; print(version.__full_version_str__)' > /app/version" # Copy root filesystem COPY root/ / @@ -119,6 +436,11 @@ COPY root/ / ## Set permissions RUN chmod +x /etc/services.d/*/run /etc/cont-init.d/* /healthz 2>/dev/null || true +## Temporary fix for regex timeout +RUN < + - PUID=1000 # User ID for the application process + - PGID=1000 # Group ID for the application process + - TZ=UTC # Timezone for the container + - PAPERLESS_ADMIN_USER=admin # Optional - Set name of the admin user on first start + - PAPERLESS_ADMIN_PASSWORD= # Optional - Set password of the admin user on first start volumes: - "/path/to/containers/paperless-ngx:/config" - - "/path/to/containers/paperless-ngx/config/media/documents:/config/media/documents" ports: - - 8000:8000 - - 5555:5555 - restart: unless-stopped + - "8000:8000" + - "5555:5555" + # always (not unless-stopped) so FreeBSD's podman rc.d auto-starts it at boot + restart: always +``` + +Save as `compose.yaml`, then run `podman-compose up -d`. + +### AppJail Director +**.env**: + +``` +# .env + +DIRECTOR_PROJECT=paperless-ngx +PUID=1000 +PGID=1000 +TZ=UTC +PAPERLESS_ADMIN_USER=admin +PAPERLESS_ADMIN_PASSWORD= +``` + +**appjail-director.yml**: + +```yaml +# appjail-director.yml + +options: + - virtualnet: ': default' + - nat: +services: + paperless-ngx: + name: paperless_ngx + options: + - container: 'boot args:--pull' + - expose: '8000:8000 proto:tcp' + - expose: '5555:5555 proto:tcp' + oci: + user: root + environment: + - PUID: !ENV '${PUID}' + - PGID: !ENV '${PGID}' + - TZ: !ENV '${TZ}' + - PAPERLESS_ADMIN_USER: !ENV '${PAPERLESS_ADMIN_USER}' + - PAPERLESS_ADMIN_PASSWORD: !ENV '${PAPERLESS_ADMIN_PASSWORD}' + volumes: + - paperless-ngx: /config +volumes: + paperless-ngx: + device: '/path/to/containers/paperless-ngx' ``` +**Makejail**: + +``` +# Makejail + +ARG tag=latest + +OPTION overwrite=force +OPTION from=ghcr.io/daemonless/paperless-ngx:${tag} +``` + +Save the files above, then run `appjail-director up`. + +**Note**: Exposing ports in AppJail means that your service can be reached from remote hosts. If that is not your intention, do not expose the ports and communicate with the service using the IPv4 address assigned by the virtual network. + ### Podman CLI ```bash @@ -63,17 +122,73 @@ podman run -d --name paperless-ngx \ -e PAPERLESS_ADMIN_USER=admin \ -e PAPERLESS_ADMIN_PASSWORD= \ -v /path/to/containers/paperless-ngx:/config \ - -v /path/to/containers/paperless-ngx/config/media/documents:/config/media/documents \ ghcr.io/daemonless/paperless-ngx:latest ``` +Save as `run.sh`, then run `sh run.sh`. + +### AppJail + +```bash +appjail oci run -Pd \ + -o overwrite=force \ + -o container="args:--pull" \ + -o virtualnet=": default" \ + -o nat \ + -o expose="8000:8000 proto:tcp" \ + -o expose="5555:5555 proto:tcp" \ + -e PUID=1000 \ + -e PGID=1000 \ + -e TZ=UTC \ + -e PAPERLESS_ADMIN_USER=admin \ + -e PAPERLESS_ADMIN_PASSWORD= \ + -o fstab="/path/to/containers/paperless-ngx /config " \ + ghcr.io/daemonless/paperless-ngx:latest paperless-ngx +``` + +Save as `run.sh`, then run `sh run.sh`. + +**Note**: Exposing ports in AppJail means that your service can be reached from remote hosts. If that is not your intention, do not expose the ports and communicate with the service using the IPv4 address assigned by the virtual network. + +### Bastille + +> [!WARNING] +> Bastille's OCI support is **experimental**. It requires `buildah`, shares the host network stack (`inherit`), and persists image-declared volumes under `--data-path`. + +```yaml +services: + paperless-ngx: + image: "ghcr.io/daemonless/paperless-ngx:latest" + container_name: paperless-ngx + network_mode: host # jail shares host networking + environment: + - PUID=1000 + - PGID=1000 + - TZ=UTC + - PAPERLESS_ADMIN_USER=admin + - PAPERLESS_ADMIN_PASSWORD= +``` + +Save as `podman-compose.yml`, then run `bastille up`. Or via CLI: + +```bash +bastille create -O \ + --env PUID=1000 \ + --env PGID=1000 \ + --env TZ=UTC \ + --env PAPERLESS_ADMIN_USER=admin \ + --env PAPERLESS_ADMIN_PASSWORD= \ + --data-path /path/to/containers/paperless-ngx \ + paperless-ngx ghcr.io/daemonless/paperless-ngx:latest inherit +``` + ### Ansible ```yaml - name: Deploy paperless-ngx containers.podman.podman_container: name: paperless-ngx - image: ghcr.io/daemonless/paperless-ngx:latest + image: "ghcr.io/daemonless/paperless-ngx:latest" state: started restart_policy: always env: @@ -87,9 +202,12 @@ podman run -d --name paperless-ngx \ - "5555:5555" volumes: - "/path/to/containers/paperless-ngx:/config" - - "/path/to/containers/paperless-ngx/config/media/documents:/config/media/documents" ``` +Save as `paperless-ngx-deploy.yaml`, then run `ansible-playbook paperless-ngx-deploy.yaml`. + +Access at: `http://localhost:8000` + ## Parameters ### Environment Variables @@ -107,7 +225,6 @@ podman run -d --name paperless-ngx \ | Path | Description | |------|-------------| | `/config` | Configuration directory | -| `/config/media/documents` | Optional - the actual document store (originals, archive and thumbnails) | ### Ports @@ -116,6 +233,14 @@ podman run -d --name paperless-ngx \ | `8000` | TCP | Web UI | | `5555` | TCP | Flower monitoring UI available at '/flower', optional | +## Upgrade to v3 +### Pre-Requisites +Upgrading to Paperless-ngx v3 can only be performed from version 2.20.15. If you are running an older version, please upgrade to v2.20.15 before proceeding with the v3 upgrade. +### Breaking Changes +Paperless-ngx v3 introduced some changes to the configuration. +Read the official [v3 Migration Guide](https://docs.paperless-ngx.com/migration-v3/) to make sure you adapt your configuration so that it works with v3. +### Database migration +The DB migration will run automatically when the container starts. ## First time setup To configure the admin user with a password during the first startup, you can define some additional environment variables in your container file: @@ -145,7 +270,7 @@ This directoru should contain these 3 directories: **Architectures:** amd64 **User:** `bsd` (UID/GID via PUID/PGID, defaults to 1000:1000) -**Base:** FreeBSD 15.0 +**Base:** FreeBSD 15 --- diff --git a/compose.yaml b/compose.yaml index 1645e26..19ae577 100644 --- a/compose.yaml +++ b/compose.yaml @@ -27,6 +27,14 @@ x-daemonless: 8000: "Web UI" 5555: "Flower monitoring UI available at '/flower', optional" notes: | + ## Upgrade to v3 + ### Pre-Requisites + Upgrading to Paperless-ngx v3 can only be performed from version 2.20.15. If you are running an older version, please upgrade to v2.20.15 before proceeding with the v3 upgrade. + ### Breaking Changes + Paperless-ngx v3 introduced some changes to the configuration. + Read the official [v3 Migration Guide](https://docs.paperless-ngx.com/migration-v3/) to make sure you adapt your configuration so that it works with v3. + ### Database migration + The DB migration will run automatically when the container starts. ## First time setup To configure the admin user with a password during the first startup, you can define some additional environment variables in your container file: @@ -73,7 +81,7 @@ services: volumes: - /path/to/containers/paperless-ngx:/config - - /path/to/documents:/config/media/documents +# - /path/to/documents:/config/media/documents ports: - 8000:8000 diff --git a/root/etc/cont-init.d/20-paperlessngx b/root/etc/cont-init.d/20-paperlessngx old mode 100755 new mode 100644 index 354d1e8..f985a36 --- a/root/etc/cont-init.d/20-paperlessngx +++ b/root/etc/cont-init.d/20-paperlessngx @@ -8,11 +8,11 @@ chown -R bsd:bsd /config # Run a DB migration to make sure the database is initialized # and updated to the latest schema. echo [paperlessngx-migration] Running DB migration -s6-setuidgid bsd cd /app/src /usr/local/bin/python3.12 manage.py migrate +s6-setuidgid bsd cd /app/src/src uv run python manage.py migrate # Create superuser if env variable is defined if [ "x${PAPERLESS_ADMIN_USER}" != "x" ]; then - s6-setuidgid bsd cd /app/src /usr/local/bin/python3.12 manage.py manage_superuser + s6-setuidgid bsd cd /app/src/src uv run python manage.py manage_superuser echo "[paperlessngx-superuser] Superuser creation done" diff --git a/root/etc/services.d/consumer/run b/root/etc/services.d/consumer/run old mode 100755 new mode 100644 index 34ca64c..31ebe39 --- a/root/etc/services.d/consumer/run +++ b/root/etc/services.d/consumer/run @@ -5,8 +5,9 @@ readonly log_prefix="[cosumer]" echo "${log_prefix} Starting consumer..." +HOME=/config; export HOME LANG=en_US.UTF-8; export LANG # Standard start command for consumer -exec /usr/local/bin/s6-setuidgid bsd cd /app/src \ - /usr/local/bin/python3.12 manage.py document_consumer +exec /usr/local/bin/s6-setuidgid bsd cd /app/src/src \ + /app/venv/bin/python manage.py document_consumer diff --git a/root/etc/services.d/flower/run b/root/etc/services.d/flower/run old mode 100755 new mode 100644 index 79518bd..e0fe22b --- a/root/etc/services.d/flower/run +++ b/root/etc/services.d/flower/run @@ -5,6 +5,7 @@ readonly log_prefix="[flower]" echo "${log_prefix} Starting flower..." +HOME=/config; export HOME LANG=en_US.UTF-8; export LANG # Standard start command for paperless-ngx @@ -13,8 +14,8 @@ if [ "x${PAPERLESS_ENABLE_FLOWER}" != "x" ]; then echo "${log_prefix} Starting flower in 5s" sleep 5 - exec /usr/local/bin/s6-setuidgid bsd cd /app/src \ - /app/.local/bin/celery --app paperless flower --url-prefix=flower --loglevel INFO + exec /usr/local/bin/s6-setuidgid bsd cd /app/src/src \ + /app/venv/bin/python -m celery --app paperless flower --url-prefix=flower --loglevel INFO else echo "${log_prefix} Not starting flower" # https://skarnet.org/software/s6/s6-svc.html diff --git a/root/etc/services.d/paperlessngx/run b/root/etc/services.d/paperlessngx/run old mode 100755 new mode 100644 index 0b2c56d..068cf82 --- a/root/etc/services.d/paperlessngx/run +++ b/root/etc/services.d/paperlessngx/run @@ -5,10 +5,10 @@ readonly log_prefix="[paperlessngx]" echo "${log_prefix} Starting paperless-ngx..." +HOME=/config; export HOME LANG=en_US.UTF-8; export LANG # Standard start command for paperless-ngx -#/usr/local/bin/s6-notifyoncheck -c /healthz /usr/local/bin/s6-setuidgid bsd exec /usr/local/bin/s6-notifyoncheck -c /healthz /usr/local/bin/s6-setuidgid bsd \ - cd /app/src /app/.local/bin/granian --interface asginl \ - --ws --loop uvloop paperless.asgi:application + cd /app/src/src /app/venv/bin/python -m granian \ + --interface asginl --ws --loop uvloop paperless.asgi:application \ No newline at end of file diff --git a/root/etc/services.d/scheduler/run b/root/etc/services.d/scheduler/run old mode 100755 new mode 100644 index 9ef60ab..e7f851e --- a/root/etc/services.d/scheduler/run +++ b/root/etc/services.d/scheduler/run @@ -5,9 +5,10 @@ readonly log_prefix="[scheduler]" echo "${log_prefix} Starting schduler..." +HOME=/config; export HOME LANG=en_US.UTF-8; export LANG # Standard start command for scheduler #/usr/local/bin/s6-notifyoncheck -c /healthz /usr/local/bin/s6-setuidgid bsd -exec /usr/local/bin/s6-setuidgid bsd cd /app/src \ - /app/.local/bin/celery --app paperless beat --loglevel DEBUG -s /config/data/celerybeat-schedule +exec /usr/local/bin/s6-setuidgid bsd cd /app/src/src \ + /app/venv/bin/python -m celery --app paperless beat --loglevel DEBUG -s /config/data/celerybeat-schedule diff --git a/root/etc/services.d/worker/run b/root/etc/services.d/worker/run old mode 100755 new mode 100644 index c52f4c2..7ac2fa3 --- a/root/etc/services.d/worker/run +++ b/root/etc/services.d/worker/run @@ -5,9 +5,10 @@ readonly log_prefix="[worker]" echo "${log_prefix} Starting worker..." +HOME=/config; export HOME LANG=en_US.UTF-8; export LANG # Standard start command for worker #/usr/local/bin/s6-notifyoncheck -c /healthz /usr/local/bin/s6-setuidgid bsd -exec /usr/local/bin/s6-setuidgid bsd cd /app/src \ - /app/.local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip +exec /usr/local/bin/s6-setuidgid bsd cd /app/src/src \ + /app/venv/bin/python -m celery --app paperless worker --loglevel INFO --without-mingle --without-gossip From bb52e27706df8a92de0bc8dcc60d2617cee2a960 Mon Sep 17 00:00:00 2001 From: varpon Date: Sun, 30 Aug 2026 18:29:44 -0400 Subject: [PATCH 2/2] fix behavior if PKG_CACHE_URL is unset --- Containerfile | 53 ++++++++---------------------------------------- Containerfile.j2 | 53 ++++++++---------------------------------------- 2 files changed, 16 insertions(+), 90 deletions(-) diff --git a/Containerfile b/Containerfile index 3afb1e7..f474c03 100644 --- a/Containerfile +++ b/Containerfile @@ -15,7 +15,10 @@ FROM ghcr.io/daemonless/base:${BASE_VERSION} AS builder # Use local package cache ARG PKG_CACHE_URL RUN </usr/local/etc/pkg/repos/FreeBSD.conf FreeBSD-Ports: { @@ -149,52 +152,9 @@ maturin build --release mv target/wheels/tantivy-*.whl /wheels/tantivy-0.26.0-py3-none-any.whl -# Build ocrmypdf wheel rm -fr /.cache /.cargo CMD_EOF -# # Install build dependencies -# RUN < -# ' src/binding/JbgDecoder.hpp -# mydir=$(pwd) -# cd ../.. - -# pip wheel --wheel-dir wheels/ \ -# -Ccmake.define.CMAKE_TOOLCHAIN_FILE=/tmp/freebsd-toolchain.cmake \ -# pngquant-py ${mydir} || exit 1 - -# version=$(ls -1 /wheels/pngquant_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') -# mv /wheels/pngquant_py*.whl /wheels/pngquant_py-${version}-py3-none-any.whl || exit 1 -# version=$(ls -1 /wheels/jbigkit_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') -# mv /wheels/jbigkit_py*.whl /wheels/jbigkit_py-${version}-py3-none-any.whl || exit 1 -# CMD_EOF - FROM ghcr.io/daemonless/base:${BASE_VERSION} @@ -202,7 +162,10 @@ FROM ghcr.io/daemonless/base:${BASE_VERSION} # Use local package cache ARG PKG_CACHE_URL RUN </usr/local/etc/pkg/repos/FreeBSD.conf FreeBSD-Ports: { diff --git a/Containerfile.j2 b/Containerfile.j2 index ceee745..3c0fe72 100644 --- a/Containerfile.j2 +++ b/Containerfile.j2 @@ -9,7 +9,10 @@ FROM ghcr.io/daemonless/base:${BASE_VERSION} AS builder # Use local package cache ARG PKG_CACHE_URL RUN </usr/local/etc/pkg/repos/FreeBSD.conf FreeBSD-Ports: { @@ -143,52 +146,9 @@ maturin build --release mv target/wheels/tantivy-*.whl /wheels/tantivy-0.26.0-py3-none-any.whl -# Build ocrmypdf wheel rm -fr /.cache /.cargo CMD_EOF -# # Install build dependencies -# RUN < -# ' src/binding/JbgDecoder.hpp -# mydir=$(pwd) -# cd ../.. - -# pip wheel --wheel-dir wheels/ \ -# -Ccmake.define.CMAKE_TOOLCHAIN_FILE=/tmp/freebsd-toolchain.cmake \ -# pngquant-py ${mydir} || exit 1 - -# version=$(ls -1 /wheels/pngquant_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') -# mv /wheels/pngquant_py*.whl /wheels/pngquant_py-${version}-py3-none-any.whl || exit 1 -# version=$(ls -1 /wheels/jbigkit_py*.whl | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') -# mv /wheels/jbigkit_py*.whl /wheels/jbigkit_py-${version}-py3-none-any.whl || exit 1 -# CMD_EOF - FROM ghcr.io/daemonless/base:${BASE_VERSION} @@ -196,7 +156,10 @@ FROM ghcr.io/daemonless/base:${BASE_VERSION} # Use local package cache ARG PKG_CACHE_URL RUN </usr/local/etc/pkg/repos/FreeBSD.conf FreeBSD-Ports: {