Added Immich. Tweaked docker compose networks (frontend/backend).

This commit is contained in:
Raphaël Billet
2025-12-03 22:36:07 +01:00
parent 3f6d7cf43d
commit 3a5b786b36
9 changed files with 309 additions and 32 deletions
+12
View File
@@ -33,6 +33,18 @@ docker:
HOME_ROUTER_IP=$HOME_ROUTER_IP
HOME_SERVER_IP=$HOME_SERVER_IP
FTLCONF_webserver_api_password=$FTLCONF_WEBSERVER_PASSWORD
immich: |
DOMAIN_NAME=$DOMAIN_NAME
TZ=Europe/Paris
UPLOAD_LOCATION=/mnt/data-storage/docker-data/immich
IMMICH_VERSION=release
IMMICH_TRUSTED_PROXIES=172.16.50.253
REDIS_HOSTNAME=immich-redis
DB_HOSTNAME=immich-database
DB_DATABASE_NAME=$IMMICH_DB_DATABASE_NAME
DB_USERNAME=$IMMICH_DB_USERNAME
DB_PASSWORD=$IMMICH_DB_PASSWORD
DB_DATA_LOCATION=/mnt/config-storage/docker-data/immich/database
disks:
data_disk_1: $DATA_DISK_1
+2 -1
View File
@@ -43,6 +43,7 @@ in
sops.secrets."docker/passbolt" = { owner = "numbus-admin"; path = "/etc/docker-compose/passbolt/.env"; };
sops.secrets."docker/hass" = { owner = "numbus-admin"; path = "/etc/docker-compose/hass/.env"; };
sops.secrets."docker/pihole" = { owner = "numbus-admin"; path = "/etc/docker-compose/pihole/.env"; };
sops.secrets."docker/immich" = { owner = "numbus-admin"; path = "/etc/docker-compose/immich/.env"; };
sops.secrets."disks/data_disk_1" = { owner = "root"; };
sops.secrets."disks/data_disk_2" = { owner = "root"; };
sops.secrets."disks/data_disk_3" = { owner = "root"; };
@@ -125,7 +126,7 @@ in
# Enable docker
virtualisation.docker.enable = true;
virtualisation.docker.daemon.settings = {
data-root = "/mnt/config-storage/docker-data/docker-volumes/";
data-root = "/mnt/config-storage/docker-volumes/";
};
# Enable networking and firewall
+20 -5
View File
@@ -171,6 +171,9 @@ files_generation() {
export PASSBOLT_MYSQL_USER="$(openssl rand -hex 10)"
export PASSBOLT_MYSQL_PASSWORD="$(openssl rand -base64 32 | tr -d '\=+/')"
export FTLCONF_WEBSERVER_PASSWORD="$(openssl rand -base64 32 | tr -d '\=+/')"
export IMMICH_DB_DATABASE_NAME="$(openssl rand -hex 10)"
export IMMICH_DB_USERNAME="$(openssl rand -hex 10)"
export IMMICH_DB_PASSWORD="$(openssl rand -base64 32 | tr -d '\=+/')"
export DATA_DISK_1="$(openssl rand -base64 32 | tr -d '\=+/')"
export DATA_DISK_2="$(openssl rand -base64 32 | tr -d '\=+/')"
export DATA_DISK_3="$(openssl rand -base64 32 | tr -d '\=+/')"
@@ -217,21 +220,33 @@ files_generation() {
sed -i s+HOME_ROUTER_IP+$HOME_ROUTER_IP+g configuration.nix
echo -e "\n ✅ Adapting the docker configuration to your hardware..."
DEVICES_BLOCK=""
FRIGATE_DEVICES_BLOCK=""
if [[ "$TARGET_GRAPHICS_RENDERER" == "true" ]]; then
DEVICES_BLOCK+=" - /dev/dri/renderD128:/dev/dri/renderD128\n"
FRIGATE_DEVICES_BLOCK+=" - /dev/dri:/dev/dri\n"
fi
if [[ "$TARGET_USB_CORAL" == "true" ]]; then
DEVICES_BLOCK+=" - /dev/bus/usb:/dev/bus/usb\n"
FRIGATE_DEVICES_BLOCK+=" - /dev/bus/usb:/dev/bus/usb\n"
fi
if [[ -n "$DEVICES_BLOCK" ]]; then
REPLACEMENT="devices:\n${DEVICES_BLOCK%\\n}"
if [[ -n "$FRIGATE_DEVICES_BLOCK" ]]; then
REPLACEMENT="devices:\n${FRIGATE_DEVICES_BLOCK%\\n}"
sed -i.bak "s|# --- frigate devices --- #|$REPLACEMENT|" docker/frigate.original
else
sed -i.bak "/# --- frigate devices --- #/d" docker/frigate.original
fi
IMMICH_DEVICES_BLOCK=""
if [[ "$TARGET_GRAPHICS_RENDERER" == "true" ]]; then
IMMICH_DEVICES_BLOCK+=" - /dev/dri:/dev/dri\n"
fi
if [[ -n "$IMMICH_DEVICES_BLOCK" ]]; then
REPLACEMENT="devices:\n${IMMICH_DEVICES_BLOCK%\\n}"
sed -i.bak "s|# --- immich devices --- #|$REPLACEMENT|" docker/immich.original
else
sed -i.bak "/# --- immich devices --- #/d" docker/immich.original
fi
if [[ -n "$TARGET_ZIGBEE_DEVICE" ]]; then
REPLACEMENT="devices:\n - /dev/serial/by-id/${TARGET_ZIGBEE_DEVICE}:/dev/ttyUSB0"
sed -i.bak "s|# --- hass devices --- #|$REPLACEMENT|" docker/hass.original
+4
View File
@@ -38,6 +38,10 @@ in
# --- frigate devices --- #
labels:
- traefik.enable=true
- traefik.http.services.frigate.loadbalancer.server.port=8971
- traefik.http.services.frigate.loadbalancer.server.scheme=http
- traefik.http.routers.frigate-https.entrypoints=websecure
- traefik.http.routers.frigate-https.rule=Host(`cctv.$DOMAIN_NAME`)
- traefik.http.routers.frigate-https.tls=true
- traefik.http.routers.frigate-https.tls.certresolver=cloudflare
restart: unless-stopped
+1 -6
View File
@@ -47,12 +47,7 @@ in
networks:
hass_backend:
name: hass_backend
driver: bridge
ipam:
config:
- subnet: "172.16.4.0/24"
gateway: "172.16.4.254"
external: true
hass_frontend:
external: true
'';
+111
View File
@@ -0,0 +1,111 @@
{ config, pkgs, ... }:
let
container_name = "immich";
compose-dir = "docker-compose/immich";
config-dir = "/mnt/config-storage/docker-data/immich";
in
{
config = {
environment.etc."${compose-dir}/compose.yaml".text =
/*
yaml
*/
''
services:
immich-server:
image: ghcr.io/immich-app/immich-server:$IMMICH_VERSION
container_name: immich-server
networks:
immich_frontend:
immich_backend:
volumes:
- $UPLOAD_LOCATION:/data
- /etc/localtime:/etc/localtime:ro
# --- immich devices --- #
labels:
- traefik.enable=true
- traefik.http.services.immich.loadbalancer.server.port=2283
- traefik.http.services.immich.loadbalancer.server.scheme=http
- traefik.http.routers.immich-https.entrypoints=websecure
- traefik.http.routers.immich-https.rule=Host(`immich.$DOMAIN_NAME`)
- traefik.http.routers.immich-https.tls=true
- traefik.http.routers.immich-https.tls.certresolver=cloudflare
env_file:
- .env
depends_on:
- immich-redis
- immich-database
restart: always
healthcheck:
disable: false
immich-machine-learning:
container_name: immich-machine-learning
image: ghcr.io/immich-app/immich-machine-learning:$IMMICH_VERSION
networks:
immich_backend:
volumes:
- ${config-dir}/models:/cache
env_file:
- .env
restart: always
healthcheck:
disable: false
immich-redis:
container_name: immich-redis
image: docker.io/valkey/valkey:8-bookworm@sha256:a137a2b60aca1a75130022d6bb96af423fefae4eb55faf395732db3544803280
networks:
immich_backend:
healthcheck:
test: redis-cli ping || exit 1
restart: always
immich-database:
container_name: immich-database
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a
networks:
immich_backend:
shm_size: 128mb
volumes:
# Do not edit the next line. If you want to change the database storage location on your system, edit the value of DB_DATA_LOCATION in the .env file
- $DB_DATA_LOCATION:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: $DB_PASSWORD
POSTGRES_USER: $DB_USERNAME
POSTGRES_DB: $DB_DATABASE_NAME
POSTGRES_INITDB_ARGS: '--data-checksums'
restart: always
healthcheck:
disable: false
networks:
immich_backend:
external: true
immich_frontend:
external: true
'';
systemd.services.immich = {
description = "Docker container : ${container_name}";
after = [ "network.target" "docker.service" "docker.socket" "traefik.service" ];
requires = [ "docker.service" ];
wantedBy = ["multi-user.target"];
path = [ pkgs.docker ];
serviceConfig = {
Type = "exec";
# Pull the latest image before running
ExecStartPre = "${pkgs.docker}/bin/docker compose -f /etc/${compose-dir}/compose.yaml pull";
# Bring the service up
ExecStart = "${pkgs.docker}/bin/docker compose -f /etc/${compose-dir}/compose.yaml up --remove-orphans";
# Take it down gracefully
ExecStop = "${pkgs.docker}/bin/docker compose -f /etc/${compose-dir}/compose.yaml down";
Restart = "on-failure";
};
};
};
}
+15 -20
View File
@@ -13,20 +13,6 @@ in
*/
''
services:
passbolt-database:
image: mariadb:11.3
container_name: passbolt-database
networks:
passbolt_backend:
volumes:
- passbolt-database:/var/lib/mysql
environment:
MYSQL_RANDOM_ROOT_PASSWORD: "true"
MYSQL_DATABASE: $PASSBOLT_MYSQL_DATABASE
MYSQL_USER: $PASSBOLT_MYSQL_USER
MYSQL_PASSWORD: $PASSBOLT_MYSQL_PASSWORD
restart: unless-stopped
passbolt:
image: passbolt/passbolt:latest-ce-non-root
container_name: passbolt
@@ -72,14 +58,23 @@ in
- passbolt-database
restart: unless-stopped
passbolt-database:
image: mariadb:11.3
container_name: passbolt-database
networks:
passbolt_backend:
volumes:
- passbolt-database:/var/lib/mysql
environment:
MYSQL_RANDOM_ROOT_PASSWORD: "true"
MYSQL_DATABASE: $PASSBOLT_MYSQL_DATABASE
MYSQL_USER: $PASSBOLT_MYSQL_USER
MYSQL_PASSWORD: $PASSBOLT_MYSQL_PASSWORD
restart: unless-stopped
networks:
passbolt_backend:
name: passbolt_backend
driver: bridge
ipam:
config:
- subnet: "172.16.2.0/24"
gateway: "172.16.2.254"
external: true
passbolt_frontend:
external: true
+21
View File
@@ -55,6 +55,13 @@ in
config:
- subnet: "172.16.1.0/24"
gateway: "172.16.1.254"
passbolt_backend:
name: passbolt_backend
driver: bridge
ipam:
config:
- subnet: "172.16.2.0/24"
gateway: "172.16.2.254"
passbolt_frontend:
name: passbolt_frontend
driver: bridge
@@ -69,6 +76,13 @@ in
config:
- subnet: "172.16.3.0/24"
gateway: "172.16.3.254"
hass_backend:
name: hass_backend
driver: bridge
ipam:
config:
- subnet: "172.16.4.0/24"
gateway: "172.16.4.254"
hass_frontend:
name: hass_frontend
driver: bridge
@@ -76,6 +90,13 @@ in
config:
- subnet: "172.16.40.0/24"
gateway: "172.16.40.254"
immich_backend:
name: immich_backend
driver: bridge
ipam:
config:
- subnet: "172.16.5.0/24"
gateway: "172.16.5.254"
immich_frontend:
name: immich_frontend
driver: bridge
+123
View File
@@ -0,0 +1,123 @@
{ lib, utils, config, ... }:
let
inherit (lib)
head
optional
foldl'
nameValuePair
listToAttrs
optionals
concatStringsSep
sortOn
mkIf
mkEnableOption
mkOption
types
;
in
{
options = {
systemIdentity = {
enable = mkEnableOption "hashing of Luks values into PCR 15 and subsequent checks";
pcr15 = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The expected value of PCR 15 after all luks partitions have been unlocked
Should be a 64 character hex string as ouput by the sha256 field of
'systemd-analyze pcrs 15 --json=short'
If set to null (the default) it will not check the value.
If the check fails the boot will abort and you will be dropped into an emergency shell, if enabled.
In ermergency shell type:
'systemctl disable check-pcrs'
'systemctl default'
to continue booting
'';
example = "6214de8c3d861c4b451acc8c4e24294c95d55bcec516bbf15c077ca3bffb6547";
};
};
boot.initrd.luks.devices = lib.mkOption {
type =
with lib.types;
attrsOf (submodule {
config.crypttabExtraOpts = optionals config.systemIdentity.enable [
"tpm2-device=auto"
"tpm2-measure-pcr=yes"
];
});
};
};
config = mkIf config.systemIdentity.enable {
boot.kernelParams = [
"rd.luks=no"
];
boot.initrd.systemd.services =
{
check-pcrs = mkIf (config.systemIdentity.pcr15 != null) {
script = ''
echo "Checking PCR 15 value"
if [[ $(systemd-analyze pcrs 15 --json=short | jq -r ".[0].sha256") != "${config.systemIdentity.pcr15}" ]] ; then
echo "PCR 15 check failed"
exit 1
else
echo "PCR 15 check suceed"
fi
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
unitConfig.DefaultDependencies = "no";
after = [ "cryptsetup.target" ];
before = [ "sysroot.mount" ];
requiredBy = [ "sysroot.mount" ];
};
}
// (listToAttrs (
foldl' (
acc: attrs:
let
extraOpts = attrs.value.crypttabExtraOpts ++ (optional attrs.value.allowDiscards "discard");
cfg = config.boot.initrd.systemd;
in
[
(nameValuePair "cryptsetup-${attrs.name}" {
unitConfig = {
Description = "Cryptography setup for ${attrs.name}";
DefaultDependencies = "no";
IgnoreOnIsolate = true;
Conflicts = [ "umount.target" ];
BindsTo = "${utils.escapeSystemdPath attrs.value.device}.device";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
TimeoutSec = "infinity";
KeyringMode = "shared";
OOMScoreAdjust = 500;
ImportCredential = "cryptsetup.*";
ExecStart = ''${cfg.package}/bin/systemd-cryptsetup attach '${attrs.name}' '${attrs.value.device}' '-' '${concatStringsSep "," extraOpts}' '';
ExecStop = ''${cfg.package}/bin/systemd-cryptsetup detach '${attrs.name}' '';
};
after =
[
"cryptsetup-pre.target"
"systemd-udevd-kernel.socket"
"${utils.escapeSystemdPath attrs.value.device}.device"
]
++ (optional cfg.tpm2.enable "systemd-tpm2-setup-early.service")
++ optional (acc != [ ]) "${(head acc).name}.service";
before = [
"blockdev@dev-mapper-${attrs.name}.target"
"cryptsetup.target"
"umount.target"
];
wants = [ "blockdev@dev-mapper-${attrs.name}.target" ];
requiredBy = [ "sysroot.mount" ];
})
]
++ acc
) [ ] (sortOn (x: x.name) (lib.attrsets.attrsToList config.boot.initrd.luks.devices))
));
};
}