2023-06-23 06:22:34 +02:00
{ config , pkgs , lib , . . . }:
let
cfg = config . shb . nextcloud ;
2023-07-16 00:09:54 +02:00
fqdn = " ${ cfg . subdomain } . ${ cfg . domain } " ;
2023-06-23 06:22:34 +02:00
in
{
options . shb . nextcloud = {
enable = lib . mkEnableOption " s e l f h o s t b l o c k s . n e x t c l o u d - s e r v e r " ;
2023-07-16 00:09:54 +02:00
subdomain = lib . mkOption {
type = lib . types . str ;
description = " S u b d o m a i n u n d e r w h i c h h o m e - a s s i s t a n t w i l l b e s e r v e d . " ;
example = " n e x t c l o u d " ;
} ;
domain = lib . mkOption {
description = lib . mdDoc " D o m a i n t o s e r v e s i t e s u n d e r . " ;
2023-06-23 06:22:34 +02:00
type = lib . types . str ;
2023-07-16 00:09:54 +02:00
example = " d o m a i n . c o m " ;
2023-06-23 06:22:34 +02:00
} ;
sopsFile = lib . mkOption {
type = lib . types . path ;
description = " S o p s f i l e l o c a t i o n " ;
example = " s e c r e t s / n e x t c l o u d . y a m l " ;
} ;
2023-08-28 07:20:59 +02:00
localNetworkIPRange = lib . mkOption {
type = lib . types . str ;
description = " L o c a l n e t w o r k r a n g e , t o r e s t r i c t a c c e s s t o t h e U I t o o n l y t h o s e I P s . " ;
example = " 1 9 2 . 1 6 8 . 1 . 1 / 2 4 " ;
} ;
2023-09-13 05:52:24 +02:00
debug = lib . mkOption {
type = lib . types . bool ;
description = " E n a b l e d e b u g g i n g . " ;
default = false ;
example = true ;
} ;
2023-06-23 06:22:34 +02:00
} ;
config = lib . mkIf cfg . enable {
users . users = {
nextcloud = {
name = " n e x t c l o u d " ;
group = " n e x t c l o u d " ;
isSystemUser = true ;
} ;
} ;
users . groups = {
nextcloud = {
members = [ " b a c k u p " ] ;
} ;
} ;
2023-08-10 05:40:38 +02:00
# LDAP is manually configured through
# https://github.com/lldap/lldap/blob/main/example_configs/nextcloud.md, see also
# https://docs.nextcloud.com/server/latest/admin_manual/configuration_user/user_auth_ldap.html
2023-08-12 08:30:04 +02:00
#
# Verify setup with:
# - On admin page
# - https://scan.nextcloud.com/
# - https://www.ssllabs.com/ssltest/
# As of writing this, we got no warning on admin page and A+ on both tests.
#
# Content-Security-Policy is hard. I spent so much trying to fix lingering issues with .js files
# not loading to realize those scripts are inserted by extensions. Doh.
2023-06-23 06:22:34 +02:00
services . nextcloud = {
enable = true ;
2023-09-27 08:34:24 +02:00
package = pkgs . nextcloud27 ;
2023-06-23 06:22:34 +02:00
# Enable php-fpm and nginx which will be behind the shb haproxy instance.
2023-07-16 00:09:54 +02:00
hostName = fqdn ;
2023-08-12 06:35:51 +02:00
nginx . hstsMaxAge = 31536000 ; # Needs > 1 year for https://hstspreload.org to be happy
2023-06-23 06:22:34 +02:00
config = {
dbtype = " p g s q l " ;
adminuser = " r o o t " ;
adminpassFile = " / r u n / s e c r e t s / n e x t c l o u d / a d m i n p a s s " ;
# Not using dbpassFile as we're using socket authentication.
defaultPhoneRegion = " U S " ;
trustedProxies = [ " 1 2 7 . 0 . 0 . 1 " ] ;
} ;
database . createLocally = true ;
# Enable caching using redis https://nixos.wiki/wiki/Nextcloud#Caching.
configureRedis = true ;
caching . apcu = false ;
# https://docs.nextcloud.com/server/26/admin_manual/configuration_server/caching_configuration.html
caching . redis = true ;
# Adds appropriate nginx rewrite rules.
webfinger = true ;
2023-08-12 06:35:27 +02:00
# Very important for a bunch of scripts to load correctly. Otherwise you get Content-Security-Policy errors. See https://docs.nextcloud.com/server/13/admin_manual/configuration_server/harden_server.html#enable-http-strict-transport-security
https = true ;
2023-06-23 06:22:34 +02:00
extraOptions = {
2023-07-16 00:09:54 +02:00
" o v e r w r i t e . c l i . u r l " = " h t t p s : / / " + fqdn ;
" o v e r w r i t e h o s t " = fqdn ;
2023-08-12 01:38:13 +02:00
# 'trusted_domains' needed otherwise we get this issue https://help.nextcloud.com/t/the-polling-url-does-not-start-with-https-despite-the-login-url-started-with-https/137576/2
" t r u s t e d _ d o m a i n s " = [ fqdn ] ;
" o v e r w r i t e p r o t o c o l " = " h t t p s " ; # Needed if behind a reverse_proxy
" o v e r w r i t e c o n d a d d r " = " " ; # We need to set it to empty otherwise overwriteprotocol does not work.
2023-09-13 05:52:24 +02:00
} // lib . optionalAttrs cfg . debug {
" d e b u g " = true ;
" f i l e l o c k i n g . d e b u g " = true ;
2023-06-23 06:22:34 +02:00
} ;
phpOptions = {
# The OPcache interned strings buffer is nearly full with 8, bump to 16.
2023-07-30 07:13:09 +02:00
catch_workers_output = " y e s " ;
display_errors = " s t d e r r " ;
error_reporting = " E _ A L L & ~ E _ D E P R E C A T E D & ~ E _ S T R I C T " ;
expose_php = " O f f " ;
" o p c a c h e . e n a b l e _ c l i " = " 1 " ;
" o p c a c h e . f a s t _ s h u t d o w n " = " 1 " ;
2023-06-23 06:22:34 +02:00
" o p c a c h e . i n t e r n e d _ s t r i n g s _ b u f f e r " = " 1 6 " ;
2023-07-30 07:13:09 +02:00
" o p c a c h e . m a x _ a c c e l e r a t e d _ f i l e s " = " 1 0 0 0 0 " ;
" o p c a c h e . m e m o r y _ c o n s u m p t i o n " = " 1 2 8 " ;
" o p c a c h e . r e v a l i d a t e _ f r e q " = " 1 " ;
" o p e n s s l . c a f i l e " = " / e t c / s s l / c e r t s / c a - c e r t i f i c a t e s . c r t " ;
short_open_tag = " O f f " ;
# Needed to avoid corruption per https://docs.nextcloud.com/server/21/admin_manual/configuration_server/caching_configuration.html#id2
" r e d i s . s e s s i o n . l o c k i n g _ e n a b l e d " = " 1 " ;
" r e d i s . s e s s i o n . l o c k _ r e t r i e s " = " - 1 " ;
" r e d i s . s e s s i o n . l o c k _ w a i t _ t i m e " = " 1 0 0 0 0 " ;
2023-09-13 05:52:24 +02:00
} // lib . optionalAttrs cfg . debug {
# "xdebug.remote_enable" = "on";
# "xdebug.remote_host" = "127.0.0.1";
# "xdebug.remote_port" = "9000";
# "xdebug.remote_handler" = "dbgp";
" x d e b u g . t r i g g e r _ v a l u e " = " d e b u g _ m e " ;
" x d e b u g . m o d e " = " p r o f i l e " ;
" x d e b u g . o u t p u t _ d i r " = " / v a r / l o g / x d e b u g " ;
" x d e b u g . s t a r t _ w i t h _ r e q u e s t " = " t r i g g e r " ;
2023-06-23 06:22:34 +02:00
} ;
2023-09-13 05:52:24 +02:00
phpExtraExtensions = all : [ all . xdebug ] ;
2023-06-23 06:22:34 +02:00
} ;
2023-08-28 07:20:59 +02:00
services . onlyoffice = {
enable = true ;
hostname = " o o . ${ cfg . domain } " ;
port = 13444 ;
postgresHost = " / r u n / p o s t g r e s q l " ;
jwtSecretFile = " / r u n / s e c r e t s / n e x t c l o u d / o n l y o f f i c e / j w t _ s e c r e t " ;
} ;
services . nginx . virtualHosts . " o o . ${ cfg . domain } " = {
sslCertificate = " / v a r / l i b / a c m e / ${ cfg . domain } / c e r t . p e m " ;
sslCertificateKey = " / v a r / l i b / a c m e / ${ cfg . domain } / k e y . p e m " ;
forceSSL = true ;
locations . " / " = {
extraConfig = ''
allow $ { cfg . localNetworkIPRange } ;
'' ;
} ;
} ;
# Secret needed for services.onlyoffice.jwtSecretFile
sops . secrets . " n e x t c l o u d / o n l y o f f i c e / j w t _ s e c r e t " = {
inherit ( cfg ) sopsFile ;
mode = " 0 4 4 0 " ;
owner = " o n l y o f f i c e " ;
group = " o n l y o f f i c e " ;
restartUnits = [ " o n l y o f f i c e - d o c s e r v i c e . s e r v i c e " ] ;
} ;
2023-06-23 06:22:34 +02:00
# Secret needed for services.nextcloud.config.adminpassFile.
sops . secrets . " n e x t c l o u d / a d m i n p a s s " = {
inherit ( cfg ) sopsFile ;
mode = " 0 4 4 0 " ;
owner = " n e x t c l o u d " ;
group = " n e x t c l o u d " ;
2023-08-25 18:39:28 +02:00
restartUnits = [ " p h p f p m - n e x t c l o u d . s e r v i c e " ] ;
2023-06-23 06:22:34 +02:00
} ;
2023-07-16 00:09:54 +02:00
services . nginx . virtualHosts . ${ fqdn } = {
# listen = [ { addr = "0.0.0.0"; port = 443; } ];
sslCertificate = " / v a r / l i b / a c m e / ${ cfg . domain } / c e r t . p e m " ;
sslCertificateKey = " / v a r / l i b / a c m e / ${ cfg . domain } / k e y . p e m " ;
2023-08-12 00:53:05 +02:00
forceSSL = true ;
2023-06-23 06:22:34 +02:00
} ;
2023-10-01 00:49:55 +02:00
environment . systemPackages = [
# Needed for a few apps. Would be nice to avoid having to put that in the environment and instead override https://github.com/NixOS/nixpkgs/blob/261abe8a44a7e8392598d038d2e01f7b33cf26d0/nixos/modules/services/web-apps/nextcloud.nix#L1035
pkgs . ffmpeg
# Needed for the recognize app.
pkgs . nodejs
] ;
2023-09-27 22:28:10 +02:00
2023-09-27 22:36:29 +02:00
services . postgresql . settings = {
# From https://pgtune.leopard.in.ua/
# DB Version: 16
# OS Type: linux
# DB Type: web
# Total Memory (RAM): 5 GB
# CPUs num: 2
# Connections num: 100
# Data Storage: hdd
max_connections = " 1 0 0 " ;
shared_buffers = " 1 2 8 0 M B " ;
effective_cache_size = " 3 8 4 0 M B " ;
maintenance_work_mem = " 3 2 0 M B " ;
checkpoint_completion_target = " 0 . 9 " ;
wal_buffers = " 1 6 M B " ;
default_statistics_target = " 1 0 0 " ;
random_page_cost = " 4 " ;
effective_io_concurrency = " 2 " ;
work_mem = " 6 5 5 3 k B " ;
huge_pages = " o f f " ;
min_wal_size = " 1 G B " ;
max_wal_size = " 4 G B " ;
} ;
2023-06-23 06:22:34 +02:00
systemd . services . phpfpm-nextcloud . serviceConfig = {
# Setup permissions needed for backups, as the backup user is member of the jellyfin group.
UMask = lib . mkForce " 0 0 2 7 " ;
} ;
2023-09-13 05:52:24 +02:00
systemd . services . phpfpm-nextcloud . preStart = ''
mkdir - p /var/log/xdebug ; chown - R nextcloud : /var/log/xdebug
'' ;
2023-06-23 06:22:34 +02:00
2023-10-28 09:10:50 +02:00
systemd . services . nextcloud-cron . path = [
pkgs . perl
] ;
2023-06-23 06:22:34 +02:00
# Sets up backup for Nextcloud.
shb . backup . instances . nextcloud = {
sourceDirectories = [
config . services . nextcloud . datadir
] ;
2023-07-22 18:54:19 +02:00
excludePatterns = [ " . r n d " ] ;
2023-06-23 06:22:34 +02:00
} ;
} ;
}