Services are ready.

This commit is contained in:
Raphaël Numbus
2026-02-22 12:04:19 +01:00
parent 40265e8c81
commit 2e16ac3711
10 changed files with 335 additions and 267 deletions
+9 -4
View File
@@ -3,15 +3,16 @@
with lib; with lib;
let let
frigateVersion = "0.16.4";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.frigate; cfg = config.numbus.services.frigate;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "frigate";
description = "Frigate, your fully-local NVR (Network Video Recorder)"; description = "Frigate, your fully-local NVR (Network Video Recorder)";
defaultPort = "8971"; name = "frigate";
pod = "home-assistant"; pod = "home-assistant";
defaultPort = "8971";
scheme = "https"; scheme = "https";
dependencies = [ "traefik.service" "${config.numbus.services.dns}.service" "home-assistant.service" ]; dependencies = [ "traefik.service" "${config.numbus.services.dns}.service" "home-assistant.service" ];
@@ -27,7 +28,7 @@ helper.mkPodmanService {
composeText = '' composeText = ''
services: services:
frigate: frigate:
image: ghcr.io/blakeblackshear/frigate:stable image: ghcr.io/blakeblackshear/frigate:${frigateVersion}
container_name: frigate container_name: frigate
hostname: frigate hostname: frigate
shm_size: "256mb" shm_size: "256mb"
@@ -50,8 +51,12 @@ ${lib.optionalString (cfg.devices != []) ''
devices: devices:
${lib.concatStringsSep "\n" (map (d: " - \"${d}\"") cfg.devices)} ${lib.concatStringsSep "\n" (map (d: " - \"${d}\"") cfg.devices)}
''} ''}
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
stop_grace_period: 30s
restart: unless-stopped restart: unless-stopped
networks: networks:
home-assistant: home-assistant:
external: true external: true
+20 -7
View File
@@ -3,29 +3,32 @@
with lib; with lib;
let let
giteaVersion = "1.25.4-rootless";
databaseVersion = "18-alpine";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.gitea; cfg = config.numbus.services.gitea;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "gitea";
description = "Gitea, your own self-hosted git platform"; description = "Gitea, your own self-hosted git platform";
defaultPort = "3000"; name = "gitea";
pod = "gitea"; pod = "gitea";
defaultPort = "3000";
composeText = '' composeText = ''
services: services:
gitea-server: gitea-server:
image: docker.gitea.com/gitea:latest-rootless image: docker.gitea.com/gitea:${giteaVersion}
container_name: gitea-server container_name: gitea-server
hostname: gitea-server hostname: gitea-server
user: '1000:1000'
networks: networks:
gitea: gitea:
ports: ports:
- "${cfg.port}:3000/tcp" - "${cfg.port}:3000/tcp"
volumes: volumes:
- ${cfg.dataDir}:/var/lib/gitea - ${cfg.configDir}/data:/var/lib/gitea
- ${cfg.configDir}:/etc/gitea - ${cfg.configDir}/config:/etc/gitea
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
environment: environment:
- GITEA__database__DB_TYPE=postgres - GITEA__database__DB_TYPE=postgres
@@ -37,22 +40,32 @@ helper.mkPodmanService {
- GITEA__server__ROOT_URL=${cfg.subdomain}.${config.numbus.services.domain} - GITEA__server__ROOT_URL=${cfg.subdomain}.${config.numbus.services.domain}
depends_on: depends_on:
- gitea-database - gitea-database
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
restart: unless-stopped restart: unless-stopped
gitea-database: gitea-database:
image: docker.io/library/postgres:14 image: docker.io/library/postgres:${databaseVersion}
container_name: gitea-database container_name: gitea-database
hostname: gitea-database hostname: gitea-database
user: '999:999'
networks: networks:
gitea: gitea:
volumes: volumes:
- gitea_database:/var/lib/postgresql/data - ${cfg.configDir}/database:/var/lib/postgresql
environment: environment:
- POSTGRES_USER=$DB_USERNAME - POSTGRES_USER=$DB_USERNAME
- POSTGRES_PASSWORD=$DB_PASSWORD - POSTGRES_PASSWORD=$DB_PASSWORD
- POSTGRES_DB=$DB_NAME - POSTGRES_DB=$DB_NAME
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
restart: unless-stopped restart: unless-stopped
volumes: volumes:
gitea_database: gitea_database:
name: gitea_database
networks: networks:
gitea: gitea:
name: gitea name: gitea
+18 -10
View File
@@ -3,15 +3,17 @@
with lib; with lib;
let let
homeAssistantVersion = "2026.2.3";
mqttVersion = "2.1-alpine";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.home-assistant; cfg = config.numbus.services.home-assistant;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "home-assistant";
description = "Home Assistant, libre house control and much more"; description = "Home Assistant, libre house control and much more";
defaultPort = "8123"; name = "home-assistant";
pod = "home-assistant"; pod = "home-assistant";
defaultPort = "8123";
dataDir = false; dataDir = false;
extraOptions = { extraOptions = {
@@ -23,14 +25,11 @@ helper.mkPodmanService {
}; };
}; };
extraConfig = { # Compose file good
};
composeText = '' composeText = ''
services: services:
home-assistant: home-assistant:
image: ghcr.io/home-assistant/home-assistant:latest image: ghcr.io/home-assistant/home-assistant:${homeAssistantVersion}
container_name: home-assistant container_name: home-assistant
hostname: home-assistant hostname: home-assistant
networks: networks:
@@ -38,22 +37,31 @@ helper.mkPodmanService {
ports: ports:
- "${cfg.port}:8123/tcp" - "${cfg.port}:8123/tcp"
volumes: volumes:
- ${cfg.configDir}:/config - ${cfg.configDir}/config:/config
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- /run/dbus:/run/dbus:ro - /run/dbus:/run/dbus:ro
${lib.optionalString (cfg.devices != []) '' ${lib.optionalString (cfg.devices != []) ''
devices: devices:
${lib.concatStringsSep "\n" (map (d: " - \"${d}\"") cfg.devices)} ${lib.concatStringsSep "\n" (map (d: " - \"${d}\"") cfg.devices)}
''} ''}
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
restart: unless-stopped restart: unless-stopped
home-assistant-mqtt: home-assistant-mqtt:
image: docker.io/library/eclipse-mosquitto:latest image: docker.io/library/eclipse-mosquitto:${mqttVersion}
container_name: home-assistant-mqtt container_name: home-assistant-mqtt
hostname: home-assistant-mqtt hostname: home-assistant-mqtt
user: '1000:1000'
networks: networks:
home-assistant: home-assistant:
volumes: volumes:
- /mnt/config/mosquitto:/mosquitto - ${cfg.configDir}/mqtt:/mosquitto
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
restart: unless-stopped restart: unless-stopped
networks: networks:
home-assistant: home-assistant:
+18 -16
View File
@@ -3,29 +3,33 @@
with lib; with lib;
let let
immichVersion = "v2.5.6";
redisVersion = "9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63";
databaseVersion = "14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.immich; cfg = config.numbus.services.immich;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "immich";
description = "Immich, Google Photos but better"; description = "Immich, Google Photos but better";
defaultPort = "2283"; name = "immich";
pod = "immich"; pod = "immich";
defaultPort = "2283";
# Compose file good
composeText = '' composeText = ''
services: services:
immich-server: immich-server:
image: ghcr.io/immich-app/immich-server:latest
container_name: immich-server container_name: immich-server
hostname: immich-server hostname: immich-server
image: ghcr.io/immich-app/immich-server:${immichVersion}
user: '1000:1000' user: '1000:1000'
networks: networks:
immich: immich:
ports: ports:
- "${cfg.port}:2283/tcp" - "${cfg.port}:2283/tcp"
volumes: volumes:
- ${cfg.dataDir}:/data - $UPLOAD_LOCATION:/data
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
@@ -40,14 +44,16 @@ helper.mkPodmanService {
- NET_RAW - NET_RAW
restart: unless-stopped restart: unless-stopped
immich-machine-learning: immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:latest
container_name: immich-machine-learning container_name: immich-machine-learning
hostname: immich-machine-learning hostname: immich-machine-learning
image: ghcr.io/immich-app/immich-machine-learning:${immichVersion}
user: '1000:1000' user: '1000:1000'
networks: networks:
immich: immich:
volumes: volumes:
- ${cfg.configDir}/machine-learning:/cache - ${cfg.configDir}/model-cache:/cache
- ${cfg.configDir}/machine-learning-config:/usr/src/.config
- ${cfg.configDir}/machine-learning-cache:/usr/src/.cache/
env_file: env_file:
- .env - .env
healthcheck: healthcheck:
@@ -58,34 +64,30 @@ helper.mkPodmanService {
- NET_RAW - NET_RAW
restart: unless-stopped restart: unless-stopped
immich-redis: immich-redis:
image: docker.io/valkey/valkey:8-bookworm
container_name: immich-redis container_name: immich-redis
hostname: immich-redis hostname: immich-redis
image: docker.io/valkey/valkey:${redisVersion}
user: '1000:1000' user: '1000:1000'
networks: networks:
immich: immich:
healthcheck: healthcheck:
test: redis-cli ping || exit 1 test: redis-cli ping || exit 1
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
restart: unless-stopped restart: unless-stopped
immich-database: immich-database:
image: ghcr.io/immich-app/postgres:14
container_name: immich-database container_name: immich-database
hostname: immich-database hostname: immich-database
user: '1000:1000' image: ghcr.io/immich-app/postgres:${databaseVersion}
user: '999:999'
networks: networks:
immich: immich:
shm_size: 128mb
volumes:
- ${cfg.configDir}/database:/var/lib/postgresql/data
environment: environment:
POSTGRES_PASSWORD: $DB_PASSWORD POSTGRES_PASSWORD: $DB_PASSWORD
POSTGRES_USER: $DB_USERNAME POSTGRES_USER: $DB_USERNAME
POSTGRES_DB: $DB_DATABASE_NAME POSTGRES_DB: $DB_DATABASE_NAME
POSTGRES_INITDB_ARGS: '--data-checksums' POSTGRES_INITDB_ARGS: '--data-checksums'
volumes:
- $DB_DATA_LOCATION:/var/lib/postgresql/data
shm_size: 128mb
healthcheck: healthcheck:
disable: false disable: false
security_opt: security_opt:
+5 -4
View File
@@ -3,22 +3,24 @@
with lib; with lib;
let let
it-toolsVersion = "2024.10.22-7ca5933";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.it-tools; cfg = config.numbus.services.it-tools;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "it-tools";
description = "IT-tools, useful tools when doing IT"; description = "IT-tools, useful tools when doing IT";
defaultPort = "8880"; name = "it-tools";
pod = "false"; pod = "false";
defaultPort = "8880";
configDir = false; configDir = false;
dataDir = false; dataDir = false;
# Compose file good
composeText = '' composeText = ''
services: services:
it-tools: it-tools:
image: docker.io/corentinth/it-tools:latest image: docker.io/corentinth/it-tools:${it-toolsVersion}
container_name: it-tools container_name: it-tools
hostname: it-tools hostname: it-tools
networks: networks:
@@ -26,7 +28,6 @@ helper.mkPodmanService {
ports: ports:
- "${cfg.port}:80/tcp" - "${cfg.port}:80/tcp"
restart: unless-stopped restart: unless-stopped
networks: networks:
it-tools: it-tools:
name: it-tools name: it-tools
+107 -90
View File
@@ -4,129 +4,146 @@ with lib;
{ {
mkPodmanService = { mkPodmanService = {
name,
description, description,
defaultPort ? "0", name,
secondName ? null,
thirdName ? null,
defaultSubdomain ? name, defaultSubdomain ? name,
pod ? name, secondDefaultSubdomain ? secondName,
reverseProxied ? true, thirdDefaultSubdomain ? thirdName,
composeFile ? "podman/${name}/compose.yaml", defaultPort ? "",
composeText, secondDefaultPort ? "",
thirdDefaultPort ? "",
scheme ? "http", scheme ? "http",
middlewares ? [ "secureHeaders" ], secondScheme ? "http",
dependencies ? [ "traefik.service" "${config.numbus.services.dns}.service" ], thirdScheme ? "http",
reverseProxied ? true,
secondReverseProxied ? false,
thirdReverseProxied ? false,
configDirEnabled ? true,
secondConfigDirEnabled ? false,
thirdConfigDirEnabled ? false,
dataDirEnabled ? true,
secondDataDirEnabled ? false,
thirdDataDirEnabled ? false,
pod ? "false",
composeText,
extraOptions ? {}, extraOptions ? {},
extraConfig ? {}, extraConfig ? {},
configDir ? true, delaySec ? 180,
dataDir ? true, middlewares ? [ "secureHeaders" ],
delaySec ? 180 dependencies ? [ "traefik.service" "${config.numbus.services.dns}.service" ],
}: }:
let let
cfg = config.numbus.services.${name}; mkServiceOpts = svcName: svcDesc: svcPort: svcSubdomain: svcReverseProxied: svcConfigDir: svcDataDir:
Deps = dependencies;
in
{
options.numbus.services.${name} = recursiveUpdate ({
enable = mkEnableOption description;
subdomain = mkOption {
type = types.str;
default = defaultSubdomain;
example = defaultSubdomain;
description = "The subdomain that ${name} will use";
};
port = mkOption {
type = types.str;
default = defaultPort;
example = defaultPort;
description = "The port that ${name} will use.";
};
reverseProxied = mkOption {
type = types.bool;
default = reverseProxied;
description = "Whether to create a Traefik reverse proxy configuration for this service.";
};
} // (optionalAttrs configDir {
configDir = mkOption {
type = types.str;
default = "/mnt/config/${name}";
example = "/mnt/config/${name}";
description = "The directory where ${name}'s configuration files will be stored";
};
}) // (optionalAttrs dataDir {
dataDir = mkOption {
type = types.str;
default = "/mnt/data/${name}";
example = "/mnt/data/${name}";
description = "The directory where ${name}'s data will be stored";
};
})) extraOptions;
config = mkIf cfg.enable (mkMerge [
{ {
environment.etc."${composeFile}".text = composeText; numbus.services.${svcName} = {
enable = mkEnableOption svcDesc;
environment.etc."${config.numbus.traefikDynamicConfigDir}/${name}.yaml" = mkIf cfg.reverseProxied { subdomain = mkOption {
text = '' type = types.str;
default = svcSubdomain;
example = svcSubdomain;
description = "The subdomain that ${svcName} will use";
};
port = mkOption {
type = types.str;
default = svcPort;
example = svcPort;
description = "The port that ${svcName} will use.";
};
reverseProxied = mkOption {
type = types.bool;
default = svcReverseProxied;
description = "Whether to create a Traefik reverse proxy configuration for this service.";
};
} // (optionalAttrs svcConfigDir {
configDir = mkOption {
type = types.str;
default = "/mnt/config/${svcName}";
example = "/mnt/config/${svcName}";
description = "The directory where ${svcName}'s configuration files will be stored";
};
}) // (optionalAttrs svcDataDir {
dataDir = mkOption {
type = types.str;
default = "/mnt/data/${svcName}";
example = "/mnt/data/${svcName}";
description = "The directory where ${svcName}'s data will be stored";
};
});
};
cfg = config.numbus.services.${name};
cfg2 = if secondName != null then config.numbus.services.${secondName} else {};
cfg3 = if thirdName != null then config.numbus.services.${thirdName} else {};
mkTraefikConfig = svcName: svcCfg: svcScheme:
mkIf (cfg.enable && svcCfg.reverseProxied) {
text = ''
http: http:
routers: routers:
${name}: ${svcName}:
rule: "Host(`${cfg.subdomain}.${config.numbus.services.domain}`)" rule: "Host(`${svcCfg.subdomain}.${config.numbus.services.domain}`)"
entrypoints: entrypoints:
- "websecure" - "websecure"
service: ${name} service: ${svcName}
middlewares: middlewares:
${concatStringsSep "\n" (map (m: " - ${m}") middlewares)} ${concatStringsSep "\n" (map (m: " - ${m}") middlewares)}
tls: tls:
certresolver: "cloudflare" certresolver: "cloudflare"
options: "secureTLS" options: "secureTLS"
services: services:
${name}: ${svcName}:
loadBalancer: loadBalancer:
servers: servers:
- url: "${scheme}://host.containers.internal:${cfg.port}" - url: "${svcScheme}://host.containers.internal:${svcCfg.port}"
''; '';
}; };
in
{
options = mkMerge [
(mkServiceOpts name description defaultPort defaultSubdomain reverseProxied configDirEnabled dataDirEnabled)
(optionalAttrs (secondName != null) (mkServiceOpts secondName "Secondary service for ${name}" secondDefaultPort secondDefaultSubdomain secondReverseProxied secondConfigDirEnabled secondDataDirEnabled))
(optionalAttrs (thirdName != null) (mkServiceOpts thirdName "Tertiary service for ${name}" thirdDefaultPort thirdDefaultSubdomain thirdReverseProxied thirdConfigDirEnabled thirdDataDirEnabled))
{ numbus.services.${name} = extraOptions; }
];
config = mkIf cfg.enable (mkMerge [
{
environment.etc."podman/${name}/compose.yaml".text = composeText;
environment.etc = mkMerge [
{ "${config.numbus.traefikDynamicConfigDir}/${name}.yaml" = mkTraefikConfig name cfg scheme; }
(mkIf (secondName != null) {
"${config.numbus.traefikDynamicConfigDir}/${secondName}.yaml" = mkTraefikConfig secondName cfg2 secondScheme;
})
(mkIf (thirdName != null) {
"${config.numbus.traefikDynamicConfigDir}/${thirdName}.yaml" = mkTraefikConfig thirdName cfg3 thirdScheme;
})
];
systemd.services."${name}" = { systemd.services."${name}" = {
description = "Podman container : ${name}"; description = "Podman container : ${name}";
requires = Deps; requires = dependencies;
after = Deps; after = dependencies;
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
path = [ pkgs.podman pkgs.podman-compose pkgs.coreutils pkgs.sudo ]; path = [ pkgs.podman pkgs.podman-compose pkgs.coreutils pkgs.sudo ];
serviceConfig = { serviceConfig = {
Type = "exec"; Type = "exec";
ExecStartPre = "bash -c 'sleep $((RANDOM % ${delaySec}))'"; ExecStartPre = [
ExecStart = "sudo -u numbus-admin podman-compose --in-pod ${pod} -f /etc/${composeFile} up --remove-orphans"; "bash -c 'sleep $((RANDOM % ${toString delaySec}))'"
ExecStop = "sudo -u numbus-admin podman-compose --in-pod ${pod} -f /etc/${composeFile} down"; "-sudo -u numbus-admin podman-compose -f /etc/podman/${name}/compose.yaml pull"
];
ExecStart = "sudo -u numbus-admin podman-compose --in-pod ${toString pod} -f /etc/podman/${name}/compose.yaml up --remove-orphans";
ExecStop = "sudo -u numbus-admin podman-compose --in-pod ${toString pod} -f /etc/podman/${name}/compose.yaml down";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "1m"; RestartSec = "1m";
StartLimitBurst = "5"; StartLimitBurst = "5";
}; };
}; };
systemd.services."update-${name}" = {
description = "Update ${name} container";
path = [ pkgs.podman pkgs.podman-compose pkgs.sudo pkgs.systemd ];
serviceConfig = {
Type = "oneshot";
ExecStart = [
"sudo -u numbus-admin podman-compose --in-pod ${pod} -f /etc/${composeFile} pull"
"${pkgs.systemd}/bin/systemctl restart ${name}.service"
];
};
};
systemd.timers."update-${name}" = {
timerConfig = {
OnCalendar = "02:00";
RandomizedDelaySec = "60m";
Unit = "update-${name}.service";
};
wantedBy = [ "timers.target" ];
};
} }
extraConfig extraConfig
]); ]);
+136 -122
View File
@@ -1,131 +1,145 @@
{ config, pkgs, ... }: { config, pkgs, lib, ... }:
with lib;
let let
container_name = "nextcloud"; nextcloudVersion = "32.0.6";
compose_file = "podman/nextcloud/compose.yaml"; redisVersion = "8.6-alpine";
data_dir = "/mnt/data/nextcloud"; databaseVersion = "11.4";
onlyofficeVersion = "9.2";
whiteboardVersion = "v1.5.6";
helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.nextcloud;
cfg2 = config.numbus.services.onlyoffice;
cfg3 = config.numbus.services.whiteboard;
in in
{ helper.mkPodmanService {
config = { description = "Nextcloud, your own online office suite";
environment.etc."${compose_file}".text = name = "nextcloud";
/* pod = "nextcloud";
yaml secondName = "onlyoffice";
*/ thirdName = "whiteboard";
'' defaultPort = "11000";
services: secondDefaultPort = "9980";
nextcloud-server: thirdDefaultPort = "3002";
image: docker.io/library/nextcloud:latest secondReverseProxied = true;
container_name: nextcloud-server thirdReverseProxied = true;
restart: unless-stopped secondConfigDirEnabled = true;
networks: thirdConfigDirEnabled = false;
nextcloud_frontend: secondDataDirEnabled = false;
nextcloud_backend: thirdDataDirEnabled = false;
volumes:
- nextcloud_data:/var/www/html
- ${data_dir}:/var/www/html/data
environment:
MYSQL_HOST: nextcloud-database
MYSQL_DATABASE: $MYSQL_DATABASE
MYSQL_USER: $MYSQL_USER
MYSQL_PASSWORD: $MYSQL_PASSWORD
REDIS_HOST: nextcloud-redis
REDIS_HOST_PASSWORD: $REDIS_HOST_PASSWORD
NEXTCLOUD_TRUSTED_DOMAINS: $DOMAIN_NAME
SMTP_HOST: $SMTP_HOST
SMTP_SECURE: tls
SMTP_PORT: $SMTP_PORT
SMTP_NAME: $SMTP_NAME
SMTP_PASSWORD: $SMTP_PASSWORD
MAIL_FROM_ADDRESS: $MAIL_FROM_ADDRESS
MAIL_DOMAIN: $DOMAIN_NAME
APACHE_DISABLE_REWRITE_IP: 1
TRUSTED_PROXIES: traefik
OVERWRITEPROTOCOL: https
labels:
- traefik.enable=true
- traefik.docker.network=nextcloud_frontend
- traefik.http.services.nextcloud.loadbalancer.server.port=80
- traefik.http.services.nextcloud.loadbalancer.server.scheme=http
- traefik.http.routers.nextcloud-https.entrypoints=websecure
- traefik.http.routers.nextcloud-https.rule=Host(`nextcloud.$DOMAIN_NAME`)
- traefik.http.routers.nextcloud-https.tls=true
- traefik.http.routers.nextcloud-https.tls.certresolver=cloudflare
depends_on:
- nextcloud-database
nextcloud-redis:
image: docker.io/library/redis:alpine
name: nextcloud-redis
restart: unless-stopped
networks:
nextcloud_backend:
command: redis-server --requirepass $REDIS_HOST_PASSWORD
nextcloud-database:
image: docker.io/library/mariadb:latest
container_name: nextcloud-database
restart: unless-stopped
networks:
nextcloud_backend:
volumes:
- nextcloud_database:/var/lib/mysql
environment:
MARIADB_DATABASE: $MYSQL_DATABASE
MARIADB_USER: $MYSQL_USER
MARIADB_PASSWORD: $MYSQL_PASSWORD
MARIADB_RANDOM_ROOT_PASSWORD: true
# Compose file good
composeText = ''
services:
nextcloud-server:
image: docker.io/library/nextcloud:${nextcloudVersion}
container_name: nextcloud-server
hostname: nextcloud-server
networks: networks:
nextcloud_frontend: nextcloud:
external: true ports:
nextcloud_backend: - "${cfg.port}:80/tcp"
external: true
volumes: volumes:
nextcloud_data: - ${cfg.configDir}/web:/var/www/html
nextcloud_database: - ${cfg.dataDir}:/mnt/ncdata
''; environment:
MYSQL_HOST: nextcloud-database
MYSQL_DATABASE: $MYSQL_DATABASE
MYSQL_USER: $MYSQL_USER
MYSQL_PASSWORD: $MYSQL_PASSWORD
REDIS_HOST: nextcloud-redis
REDIS_HOST_PASSWORD: $REDIS_HOST_PASSWORD
NEXTCLOUD_TRUSTED_DOMAINS: ${cfg.subdomain}.${config.numbus.services.domain}
NEXTCLOUD_DATA_DIR: /mnt/ncdata
SMTP_HOST: $SMTP_HOST
SMTP_SECURE: tls
SMTP_PORT: $SMTP_PORT
SMTP_NAME: $SMTP_NAME
SMTP_PASSWORD: $SMTP_PASSWORD
MAIL_FROM_ADDRESS: nextcloud-noreply
MAIL_DOMAIN: ${config.numbus.services.domain}
APACHE_DISABLE_REWRITE_IP: 1
TRUSTED_PROXIES: 192.168.11.5
OVERWRITEPROTOCOL: https
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_HOST_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: $MYSQL_DATABASE
MARIADB_USER: $MYSQL_USER
MARIADB_PASSWORD: $MYSQL_PASSWORD
MARIADB_RANDOM_ROOT_PASSWORD: true
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
restart: unless-stopped
nextcloud-onlyoffice:
container_name: nextcloud-onlyoffice
hostname: nextcloud-onlyoffice
image: docker.io/onlyoffice/documentserver:${onlyofficeVersion}
environment:
- JWT_SECRET=$JWT_SECRET
ports:
- "${cfg2.port}:80/tcp"
volumes:
- ${cfg2.configDir}/log:/var/log/onlyoffice
- ${cfg2.configDir}/cache:/var/lib/onlyoffice
- ${cfg2.configDir}/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:
- "${cfg3.port}:3002/tcp"
environment:
NEXTCLOUD_URL: https://${cfg.subdomain}.${config.numbus.services.domain}
JWT_SECRET_KEY: $JWT_SECRET
security_opt:
- no-new-privileges:true
cap_drop:
- NET_RAW
restart: unless-stopped
networks:
nextcloud:
name: nextcloud
driver: bridge
'';
systemd.services."${container_name}" = {
description = "Podman container : ${container_name}";
after = [ "network.target" "traefik.service" "pi-hole.service" ];
requires = [ "traefik.service" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.podman pkgs.coreutils ];
serviceConfig = {
User = "numbus-admin";
Environment = [ "XDG_RUNTIME_DIR=/run/user/1000" ];
Type = "exec";
TimeoutStartSec = "600";
ExecStartPre = [
"${pkgs.bash}/bin/bash -c 'sleep $((RANDOM % 180))'"
"-${pkgs.podman-compose}/bin/podman-compose -f /etc/${compose_file} pull"
];
ExecStart = "${pkgs.podman-compose}/bin/podman-compose -f /etc/${compose_file} up --remove-orphans";
ExecStop = "${pkgs.podman-compose}/bin/podman-compose -f /etc/${compose_file} down";
Restart = "on-failure";
RestartSec = "5m";
StartLimitBurst = "3";
};
};
systemd.services."update-${container_name}" = {
description = "Update ${container_name} container";
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.systemd}/bin/systemctl restart ${container_name}.service";
};
};
systemd.timers."update-${container_name}" = {
timerConfig = {
OnCalendar = "02:00";
RandomizedDelaySec = "60m";
Unit = "update-${container_name}.service";
};
wantedBy = [ "timers.target" ];
};
};
} }
+11 -5
View File
@@ -3,23 +3,26 @@
with lib; with lib;
let let
passboltVersion = "5.9.0-1-ce-non-root";
databaseVersion = "12.2";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.passbolt; cfg = config.numbus.services.passbolt;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "passbolt";
description = "Passbolt, your password manager"; description = "Passbolt, your password manager";
defaultPort = "4433"; name = "passbolt";
pod = "passbolt"; pod = "passbolt";
defaultPort = "4433";
scheme = "https"; scheme = "https";
configDir = false; configDir = false;
dataDir = false; dataDir = false;
# Compose file good
composeText = '' composeText = ''
services: services:
passbolt-server: passbolt-server:
image: docker.io/passbolt/passbolt:latest-ce-non-root image: docker.io/passbolt/passbolt:${passboltVersion}
container_name: passbolt-server container_name: passbolt-server
hostname: passbolt-server hostname: passbolt-server
networks: networks:
@@ -42,7 +45,7 @@ helper.mkPodmanService {
EMAIL_TRANSPORT_DEFAULT_USERNAME: $EMAIL_TRANSPORT_DEFAULT_USERNAME EMAIL_TRANSPORT_DEFAULT_USERNAME: $EMAIL_TRANSPORT_DEFAULT_USERNAME
EMAIL_TRANSPORT_DEFAULT_PASSWORD: $EMAIL_TRANSPORT_DEFAULT_PASSWORD EMAIL_TRANSPORT_DEFAULT_PASSWORD: $EMAIL_TRANSPORT_DEFAULT_PASSWORD
EMAIL_TRANSPORT_DEFAULT_TLS: true EMAIL_TRANSPORT_DEFAULT_TLS: true
EMAIL_DEFAULT_FROM: $EMAIL_ADDRESS EMAIL_DEFAULT_FROM: passbolt-noreply@${config.numbus.services.domain}
PASSBOLT_SSL_FORCE: true PASSBOLT_SSL_FORCE: true
command: command:
[ [
@@ -61,7 +64,7 @@ helper.mkPodmanService {
- NET_RAW - NET_RAW
restart: unless-stopped restart: unless-stopped
passbolt-database: passbolt-database:
image: docker.io/library/mariadb:12.2 image: docker.io/library/mariadb:${databaseVersion}
container_name: passbolt-database container_name: passbolt-database
hostname: passbolt-database hostname: passbolt-database
networks: networks:
@@ -80,8 +83,11 @@ helper.mkPodmanService {
restart: unless-stopped restart: unless-stopped
volumes: volumes:
passbolt-database: passbolt-database:
name: passbolt-database
passbolt-gpg: passbolt-gpg:
name: passbolt-gpg
passbolt-jwt: passbolt-jwt:
name: passbolt-jwt
networks: networks:
passbolt: passbolt:
name: passbolt name: passbolt
+4 -3
View File
@@ -3,24 +3,25 @@
with lib; with lib;
let let
piholeVersion = "2026.02.0";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.pi-hole; cfg = config.numbus.services.pi-hole;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "pi-hole";
description = "Pi-Hole, the ads black hole"; description = "Pi-Hole, the ads black hole";
name = "pi-hole";
defaultPort = "4443"; defaultPort = "4443";
pod = "false";
scheme = "https"; scheme = "https";
dependencies = [ "network.target" "multi-user.target" ]; dependencies = [ "network.target" "multi-user.target" ];
dataDir = false; dataDir = false;
delaySec = 10; delaySec = 10;
# Compose file good
composeText = '' composeText = ''
services: services:
pi-hole: pi-hole:
image: docker.io/pihole/pihole:latest image: docker.io/pihole/pihole:${piholeVersion}
container_name: pi-hole container_name: pi-hole
hostname: pi-hole hostname: pi-hole
network_mode: pasta network_mode: pasta
+5 -4
View File
@@ -3,14 +3,14 @@
with lib; with lib;
let let
traefikVersion = "v3.6.8";
helper = import ./lib.nix { inherit config pkgs lib; }; helper = import ./lib.nix { inherit config pkgs lib; };
cfg = config.numbus.services.traefik; cfg = config.numbus.services.traefik;
in in
helper.mkPodmanService { helper.mkPodmanService {
name = "traefik";
description = "Traefik reverse proxy, one to rule them all"; description = "Traefik reverse proxy, one to rule them all";
pod = "false"; name = "traefik";
reverseProxied = false; reverseProxied = false;
dependencies = [ "network.target" "multi-user.target" ]; dependencies = [ "network.target" "multi-user.target" ];
configDir = false; configDir = false;
@@ -28,13 +28,14 @@ helper.mkPodmanService {
default = "ERROR"; default = "ERROR";
description = "The level of detail Traefik should print in the logs."; description = "The level of detail Traefik should print in the logs.";
}; };
# traefikDynamicConfigDir referenced at global.nix # traefikDynamicConfigDir defined at global.nix
}; };
# Compose file good
composeText = '' composeText = ''
services: services:
traefik: traefik:
image: docker.io/library/traefik:latest image: docker.io/library/traefik:${traefikVersion}
container_name: traefik container_name: traefik
hostname: traefik hostname: traefik
network_mode: pasta network_mode: pasta