{ config, pkgs, lib, ... }: with lib; let # Version tagging nextcloudVersion = "32.0.6"; redisVersion = "8.6-alpine"; databaseVersion = "11.8"; onlyofficeVersion = "9.2"; whiteboardVersion = "v1.5.6"; # Helper helper = import ./lib.nix { inherit config pkgs lib; }; cfg = config.numbus.services.nextcloud; # Container config name = "nextcloud"; in helper.mkPodmanService { inherit name; description = "Nextcloud, your own online office suite"; defaultPort = "1100"; generatedSecrets = { DB_NAME = "xkcdpass -n 2 -d -"; DB_USERNAME = "xkcdpass -n 2 -d -"; DB_PASSWORD = "xkcdpass -n 10 -d -"; REDIS_PASSWORD = "xkcdpass -n 10 -d -"; ONLYOFFICE_PASSWORD = "xkcdpass -n 10 -d -"; WHITEBOARD_PASSWORD = "xkcdpass -n 10 -d -"; SMTP_PASSWORD = "cat ${config.numbus.mail.smtpPasswordPath}"; }; dirPermissions = [ "100032:100 ${cfg.configDir}" "100032:100 ${cfg.configDir}/web" "100999:100 ${cfg.configDir}/redis" "100999:100 ${cfg.configDir}/database" "100999:100 ${cfg.configDir}/onlyoffice" "100999:100 ${cfg.configDir}/onlyoffice/log" "100999:100 ${cfg.configDir}/onlyoffice/cache" "100999:100 ${cfg.configDir}/onlyoffice/database" "100032:100 ${cfg.dataDir}" ]; middlewares = [ "secureHeaders" "nextcloud-dav" ]; # Compose file good composeText = '' services: nextcloud-server: image: docker.io/library/nextcloud:${nextcloudVersion} container_name: nextcloud-server hostname: nextcloud-server networks: nextcloud: ports: - "${cfg.port}:80/tcp" volumes: - ${cfg.configDir}/web:/var/www/html - ${cfg.dataDir}:/mnt/ncdata environment: MYSQL_HOST: nextcloud-database:3306 MYSQL_DATABASE: $DB_NAME MYSQL_USER: $DB_USERNAME MYSQL_PASSWORD: $DB_PASSWORD REDIS_HOST: nextcloud-redis REDIS_HOST_PASSWORD: $REDIS_PASSWORD NEXTCLOUD_TRUSTED_DOMAINS: ${cfg.subdomain}.${config.numbus.services.domain} NEXTCLOUD_DATA_DIR: /mnt/ncdata SMTP_SECURE: tls SMTP_HOST: ${config.numbus.mail.smtpServer} SMTP_PORT: ${toString config.numbus.mail.smtpPort} SMTP_NAME: ${config.numbus.mail.smtpUsername} SMTP_PASSWORD: $SMTP_PASSWORD MAIL_FROM_ADDRESS: nextcloud-noreply MAIL_DOMAIN: ${config.numbus.services.domain} APACHE_DISABLE_REWRITE_IP: 1 OVERWRITEPROTOCOL: https TRUSTED_PROXIES: ${config.numbus.networking.ipAddress} NC_default_phone_region: "${config.numbus.language}" NC_default_language: "${config.numbus.language}" NC_default_locale: "${config.numbus.locale}" NC_default_timezone: "${config.time.timeZone}" NC_maintenance_window_start: "1" depends_on: - nextcloud-database security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped nextcloud-redis: image: docker.io/library/redis:${redisVersion} container_name: nextcloud-redis hostname: nextcloud-redis user: '1000:1000' networks: nextcloud: volumes: - ${cfg.configDir}/redis:/data command: redis-server --requirepass $REDIS_PASSWORD --save 60 1 --loglevel warning security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped nextcloud-database: image: docker.io/library/mariadb:${databaseVersion} container_name: nextcloud-database hostname: nextcloud-database user: '1000:1000' networks: nextcloud: volumes: - ${cfg.configDir}/database:/var/lib/mysql environment: MARIADB_DATABASE: $DB_NAME MARIADB_USER: $DB_USERNAME MARIADB_PASSWORD: $DB_PASSWORD MARIADB_RANDOM_ROOT_PASSWORD: true security_opt: - no-new-privileges:true cap_drop: - NET_RAW command: - "--transaction-isolation=READ-COMMITTED" - "--binlog-format=ROW" restart: unless-stopped nextcloud-onlyoffice: container_name: nextcloud-onlyoffice hostname: nextcloud-onlyoffice image: docker.io/onlyoffice/documentserver:${onlyofficeVersion} environment: - JWT_SECRET=$ONLYOFFICE_PASSWORD ports: - "9980:80/tcp" volumes: - ${cfg.configDir}/onlyoffice/log:/var/log/onlyoffice - ${cfg.configDir}/onlyoffice/cache:/var/lib/onlyoffice - ${cfg.configDir}/onlyoffice/database:/var/lib/postgresql security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped nextcloud-whiteboard: image: ghcr.io/nextcloud-releases/whiteboard:${whiteboardVersion} container_name: nextcloud-whiteboard hostname: nextcloud-whiteboard user: '1000:1000' ports: - "3002:3002/tcp" environment: NEXTCLOUD_URL: https://${cfg.subdomain}.${config.numbus.services.domain} JWT_SECRET_KEY: $WHITEBOARD_PASSWORD security_opt: - no-new-privileges:true cap_drop: - NET_RAW restart: unless-stopped networks: nextcloud: name: nextcloud driver: bridge ''; extraConfig = { environment.etc."traefik/rules/nextcloud-onlyoffice.yaml".text = '' http: routers: nextcloud-onlyoffice: rule: "Host(`onlyoffice.${config.numbus.services.domain}`)" entrypoints: - "websecure" service: nextcloud-onlyoffice middlewares: - "secureHeaders" tls: certresolver: "cloudflare" options: "secureTLS" services: nextcloud-onlyoffice: loadBalancer: servers: - url: "http://host.containers.internal:9980" ''; environment.etc."traefik/rules/nextcloud-whiteboard.yaml".text = '' http: routers: nextcloud-whiteboard: rule: "Host(`whiteboard.${config.numbus.services.domain}`)" entrypoints: - "websecure" service: nextcloud-whiteboard middlewares: - "secureHeaders" tls: certresolver: "cloudflare" options: "secureTLS" services: nextcloud-whiteboard: loadBalancer: servers: - url: "http://host.containers.internal:3002" ''; environment.etc."traefik/rules/nextcloud-dav.yaml".text = '' http: middlewares: nextcloud-dav: replacePathRegex: regex: "^/.well-known/ca(l|rd)dav" replacement: "/remote.php/dav/" ''; systemd.services."${name}-quirk" = { description = "Podman container quirk : ${name}"; wantedBy = [ "multi-user.target" ]; after = [ "${name}.service" "${name}-secrets.service" ]; onFailure = [ "service-failure-notify@%n.service" ]; startLimitBurst = 5; startLimitIntervalSec = 600; path = [ pkgs.coreutils pkgs.sudo pkgs.podman pkgs.systemd ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' OCC="sudo -u numbus-admin podman exec --user www-data nextcloud-server php occ" [[ ! -e /var/lib/numbus-server/${name}/.env ]] && systemctl start ${name}-secrets.service until [[ -e /var/lib/numbus-server/${name}/.env ]]; do echo "Waiting for secrets generation..." sleep 5 done source /var/lib/numbus-server/${name}/.env until $OCC status >/dev/null 2>&1; do echo "Waiting for Nextcloud to be up and running..." sleep 10 done $OCC db:add-missing-indices $OCC maintenance:repair --include-expensive INSTALL_APPS_LIST=( "calendar" "contacts" "mail" "notes" "onlyoffice" "cookbook" "whiteboard" ) REMOVE_APPS_LIST=( "activity" "federation" "webhook_listeners" "photos" "recommendations" "sharebymail" "teams" "support" "richdocumentscode" ) for app in ''${INSTALL_APPS_LIST[@]}; do if ! $OCC --no-warnings app:list | grep -iq "$app:"; then $OCC --no-warnings app:install "$app" if $OCC --no-warnings app:list --disabled | grep -iq "$app:"; then $OCC --no-warnings app:enable "$app" fi done for app in ''${REMOVE_APPS_LIST[@]}; do if $OCC --no-warnings app:list --enabled | grep -iq "$app:"; then $OCC --no-warnings app:disable "$app" if $OCC --no-warnings app:list | grep -iq "$app:"; then $OCC --no-warnings app:remove "$app" fi done $OCC --no-warnings config:system:set onlyoffice DocumentServerInternalUrl --value="https://onlyoffice.${config.numbus.services.domain}/" $OCC --no-warnings config:system:set onlyoffice DocumentServerUrl --value="https://onlyoffice.${config.numbus.services.domain}/" $OCC --no-warnings config:system:set onlyoffice jwt_secret --value="$ONLYOFFICE_PASSWORD" $OCC --no-warnings config:app:set whiteboard collabBackendUrl --value="https://whiteboard.${config.numbus.services.domain}" $OCC --no-warnings config:app:set whiteboard jwt_secret_key --value="$WHITEBOARD_PASSWORD" if [[ ! -f /var/lib/numbus-server/${name}/croned.true ]]; then $OCC background:cron sudo -u numbus-admin podman exec --user www-data nextcloud-server php -f /var/www/html/cron.php touch /var/lib/numbus-server/${name}/croned.true fi if [[ ! -f /var/lib/numbus-server/${name}/scanned.true ]]; then $OCC files:scan --all $OCC files:repair-tree touch /var/lib/numbus-server/${name}/scanned.true fi ''; }; systemd.services."${name}-cron" = { description = "Podman container crontab : ${name}"; after = [ "${name}.service" "${name}-quirk.service" ]; onFailure = [ "service-failure-notify@%n.service" ]; path = [ pkgs.sudo pkgs.podman ]; serviceConfig = { Type = "oneshot"; ExecCondition = ''${pkgs.sudo}/bin/sudo -u numbus-admin podman exec --user www-data nextcloud-server php occ status''; ExecStart = "${pkgs.sudo}/bin/sudo -u numbus-admin podman exec --user www-data nextcloud-server php -f /var/www/html/cron.php"; }; }; systemd.timers."${name}-cron" = { description = "Timer for Nextcloud cron"; wantedBy = [ "timers.target" ]; timerConfig = { OnBootSec = "5m"; OnUnitActiveSec = "5m"; Unit = "${name}-cron.service"; }; }; }; }