Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Immich — dossier de mise en production

Stack Immich (serveur + machine learning + PostgreSQL + Valkey) derrière un Traefik existant, avec sauvegarde borgwarehouse vérifiée et script de mise à jour.

Prérequis sur le serveur

  • Docker et le plugin compose, et make.
  • Un Traefik déjà en place, avec un réseau Docker externe nommé frontend et un certresolver nommé myresolver — c'est la stack TraefikV3. (Si tes noms diffèrent, adapte les labels et le bloc networks du docker-compose.yml.)
  • Un enregistrement DNS A pointant le domaine vers le serveur.
  • borg installé sur l'hôte (apt install borgbackup), et un serveur borgwarehouse joignable.
  • De la place : Immich stocke les originaux plus les miniatures et les vidéos transcodées. Compter large.

Un make sans argument liste tout ce qu'on peut faire sur cette stack.

Un réglage à faire côté Traefik, pas ici

Traefik coupe toute requête dont la lecture entière, corps compris, dépasse 60 secondes — c'est le défaut de readTimeout, vérifiable sur la version installée :

docker exec traefik traefik --help | grep -A3 respondingtimeouts

Sur une connexion mobile, une vidéo de quelques centaines de Mo dépasse déjà ce délai : l'envoi échoue sans explication côté client. Le réglage n'est donc pas cosmétique. C'est une modification de la configuration statique de Traefik, donc dans l'autre dépôt (TraefikV3/traefik.yml), sur l'entryPoint websecure :

entryPoints:
  websecure:
    address: ":443"
    http:
      middlewares:
        - crowdsec@file
    transport:
      respondingTimeouts:
        readTimeout: 600s   # défaut 60 s : c'est celui-là qui coupe les envois
        idleTimeout: 600s   # défaut 180 s

Redémarrer Traefik ensuite (docker compose restart traefik dans son dossier) : la configuration statique n'est pas rechargée à chaud. Le redémarrage coupe brièvement toutes les stacks derrière ce Traefik — quelques secondes. Une config statique invalide empêche Traefik de démarrer du tout : vérifier docker ps et tester un domaine existant juste après.

Installation

git clone git@github.com:CoopCodeCommun/Immich.git /opt/immich
cd /opt/immich

# Copier l'exemple et remplir les variables
cp env_example .env
openssl rand -hex 32        # pour DB_PASSWORD
nano .env

# Lancer
make up && make logs

Le premier démarrage est long : PostgreSQL initialise son cluster, puis Immich joue toutes ses migrations de schéma. Plusieurs minutes sur un disque lent.

make ps        # les quatre conteneurs doivent être "healthy"

Puis se rendre sur https://<DOMAIN> pour créer le compte administrateur. Faire cette étape tout de suite : tant que le compte n'existe pas, n'importe qui arrivant sur cette URL peut le créer.

Enfin, configurer la sauvegarde : make init (voir plus bas). Une stack sans sauvegarde n'est pas en production.

Configuration

Toute la configuration d'infrastructure passe par le .env. Après modification, recréer le conteneur concerné :

docker compose up -d

Base de données

DB_PASSWORD, DB_USERNAME et DB_DATABASE_NAME ne doivent plus jamais changer une fois la base initialisée : ça modifierait la configuration de connexion sans modifier ce que la base attend réellement, et Immich ne pourrait plus s'y connecter.

DB_PASSWORD ne doit contenir que des lettres et des chiffres (A-Za-z0-9) : Immich construit une URL de connexion, et un caractère spécial non échappé la casse.

L'image PostgreSQL est celle d'Immich (ghcr.io/immich-app/postgres), qui embarque les extensions vectorielles nécessaires à la recherche. Une image PostgreSQL standard ne convient pas.

SMTP — dans l'interface, pas dans le .env

Immich n'expose pas son SMTP en variables d'environnement. Il n'y a donc rien à mettre dans le .env : les emails se règlent une fois la stack debout, dans l'interface d'administration.

Ils servent aux invitations d'utilisateurs, aux réinitialisations de mot de passe et aux notifications d'albums partagés.

  1. Se connecter avec le compte administrateur
  2. Administration → Settings → Notification settings → Email
  3. Renseigner l'hôte, le port, l'utilisateur, le mot de passe, l'adresse d'expéditeur
  4. Send test email avant de sauvegarder — c'est le seul moyen de savoir que ça marche

Ces réglages vivent dans la base de données : ils sont donc couverts par la sauvegarde, et reviennent tels quels après une restauration.

Ports usuels : 465 en TLS implicite, 587 en STARTTLS. Si l'envoi échoue en silence, l'hébergeur bloque peut-être le port 25 ou 465 en sortie.

Où vivent les données

Deux dossiers, créés par Docker au premier démarrage :

Variable Défaut Contenu Sauvegardé ?
UPLOAD_LOCATION ./library Les photos et vidéos, les miniatures, les profils Oui, sauf thumbs/ et encoded-video/
DB_DATA_LOCATION ./postgres Les fichiers PostgreSQL Non — remplacé par le dump SQL

UPLOAD_LOCATION accepte un chemin absolu : les photos peuvent vivre sur un autre disque que la stack, le script de sauvegarde suit. DB_DATA_LOCATION, en revanche, ne doit pas être sur un partage réseau (NFS, SMB) : PostgreSQL ne le supporte pas.

Sous UPLOAD_LOCATION, Immich range :

Dossier Quoi Perte acceptable ?
library/ upload/ profile/ Les originaux Non. Irremplaçables
thumbs/ encoded-video/ Miniatures et transcodages Oui : un job les refabrique
backups/ Les dumps automatiques d'Immich Oui : on fait le nôtre, et on le vérifie

Sauvegarde

make backup dépose un pg_dump dans le dossier de la stack, puis pousse tout le nécessaire dans une archive borg unique : le dump, les originaux, le .env et le docker-compose.yml. Une archive suffit à remonter Immich de zéro. Détails et exclusions dans scripts/README.md.

Mise en place : make init

Une seule commande configure toute la sauvegarde vers le borgwarehouse de Code Commun (https://borgwarehouse.codecommun.coop/, SSH sur le port 2226) :

command -v borg || sudo apt install borgbackup
make init

Elle enchaîne : génération de la clé SSH dédiée (sur borgwarehouse, une clé = un dépôt), création du dépôt via l'API, tirage d'une passphrase, borg init, export de la clé, pose du cron, première sauvegarde et vérification.

Deux choses te seront demandées :

Un token API borgwarehouse (Account → Integrations), qui permet de créer le dépôt automatiquement. Le générer avec la permission create uniquement : c'est le seul appel que fait make init (POST /api/v1/repositories), tout le reste — init, create, prune, list — passant par SSH avec la clé dédiée. Un token create-only qui fuiterait ne permettrait ni de lister ni de supprimer les dépôts, au pire d'en créer des parasites.

Le token n'est jamais stocké. Il peut être saisi au clavier, ou passé le temps d'une exécution :

BW_API_TOKEN=xxxxxxxx make init

De confirmer que tu as mis la passphrase au coffre. Le script affiche la passphrase, la clé exportée et l'adresse du dépôt, puis attend un OUI. Ce n'est pas une formalité : sans ces éléments, les archives sont un bloc chiffré définitivement illisible. C'est le seul maillon que la sauvegarde ne peut pas se sauvegarder elle-même. Coffre-fort numérique, tout de suite.

Le quota demandé est en Go. Mesurer d'abord ce qu'il y a à sauvegarder :

du -sh library/library library/upload library/profile

make init est rejouable. API injoignable, borg init raté, Ctrl-C en plein milieu : relance-le, il reprend ce qui existe. Le seul cas où il refuse, c'est quand le dépôt contient déjà des archives — régénérer une passphrase les rendrait illisibles.

Vérifier que la sauvegarde vaut quelque chose : make check

make check

Il répond à la seule question qui compte — est-ce restaurable ? — sans rien restaurer : fraîcheur de la dernière archive, présence du dump et des originaux, et surtout dump non tronqué (un dump coupé en plein vol a une taille crédible, se trouve bien dans l'archive, et ne se restaure pas).

Sortie en code non nul si quoi que ce soit cloche : utilisable tel quel dans un monitoring.

Ce que make check ne teste pas : ta copie de coffre-fort. Il ouvre le dépôt avec le .env de la machine, pas avec la passphrase que tu as archivée ailleurs — or c'est celle-là, et elle seule, qui servira le jour où le serveur aura brûlé. Vérifie une fois, depuis une autre machine, qu'un borg list passe avec les éléments du coffre.

La surveillance ne vient pas d'ici

C'est borgwarehouse qui envoie un mail si le dépôt ne reçoit plus rien (alerte réglée par make init sur 25 h pour une sauvegarde quotidienne). Un cron qui échoue en silence, c'est un backup qui n'existe pas : make check dit que la dernière sauvegarde est bonne, l'alerte BWH prévient qu'il n'y en a plus.

Rétention

7 jours glissants, 30 quotidiennes, 12 hebdomadaires, puis toutes les mensuelles et annuelles.

Mise à jour

make update

Il rejoue une sauvegarde avant le docker compose pull, puis attend qu'Immich repasse au vert (et sort en erreur en affichant les logs si ce n'est pas le cas).

L'ordre n'est pas décoratif : au premier démarrage d'une nouvelle version, Immich joue ses migrations de schéma, et elles ne sont pas réversibles. Si l'une d'elles se passe mal, la seule issue est de revenir à l'image précédente et de restaurer la base. D'où le dump vieux de trois minutes plutôt que de la veille.

IMMICH_VERSION est épinglé sur v3 : les correctifs et versions mineures arrivent au pull, jamais une majeure par surprise. Le passage à v4, le jour venu, sera une modification explicite du .env — après lecture des release notes, qu'Immich publie avec les manœuvres à faire.

Restauration

Une archive contient tout : le dump SQL, les originaux, le .env et le docker-compose.yml.

Repartir de zéro (le serveur a brûlé)

# Sur la nouvelle machine, avec borg installé.
# La clé SSH du dépôt n'est PAS dans l'archive (voir plus bas) : en générer une
# nouvelle et l'ajouter au dépôt depuis l'interface borgwarehouse.
ssh-keygen -t ed25519 -N '' -f ~/.ssh/immich_restore_ed25519
cat ~/.ssh/immich_restore_ed25519.pub     # → à ajouter au dépôt sur BWH

export BORG_REPO='ssh://borgwarehouse@…/./xxxxxxxx'
export BORG_PASSPHRASE=''                # depuis le coffre-fort
export BORG_RSH="ssh -i ~/.ssh/immich_restore_ed25519 -oIdentitiesOnly=yes"

borg list                                 # choisir l'archive
borg extract --list "$BORG_REPO::<archive>"
# L'extraction recrée l'arborescence absolue d'origine, sans le / initial :
#   opt/immich/            <- le dossier complet, .env compris

cd opt/immich/                            # ou déplacer le dossier où on veut

Puis démarrer la base seule, y injecter le dump, et seulement ensuite lancer Immich :

docker compose up -d --wait database    # --wait : rend la main quand la base est prete

sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
  scripts/pg-dump-<prefix>/immich.sql \
  | docker compose exec -T database sh -c 'PGPASSWORD="$POSTGRES_PASSWORD" exec psql \
      --dbname="$POSTGRES_DB" --username="$POSTGRES_USER" \
      --single-transaction --set ON_ERROR_STOP=on'

docker compose up -d
docker compose logs -f immich-server

Le sed n'est pas décoratif : pg_dump neutralise le search_path en tête de dump, et les extensions vectorielles d'Immich ne se recréent pas dans ces conditions. C'est la procédure officielle Immich.

--single-transaction --set ON_ERROR_STOP=on fait échouer la restauration en bloc à la première erreur, plutôt que de laisser une base à moitié remplie qui aurait l'air de marcher.

Enfin, dans Administration → Jobs, relancer Generate thumbnails et Transcode videos : ces dossiers ne sont pas sauvegardés, ils se refabriquent.

Restaurer une instance qui tourne encore

cd /opt/immich
docker compose stop immich-server immich-machine-learning   # on laisse la base debout

borg extract --list "$BORG_REPO::<archive>"                 # extrait dans le dossier courant
rsync -a --delete <chemin-extrait>/library/library/  ./library/library/
rsync -a --delete <chemin-extrait>/library/upload/   ./library/upload/
rsync -a --delete <chemin-extrait>/library/profile/  ./library/profile/

Puis le même bloc sed | psql que ci-dessus, et docker compose up -d.

Ce qui n'est pas dans l'archive

La clé SSH du dépôt (scripts/.ssh/) : on ne met pas la clé du coffre dans le coffre. En cas de perte totale, en générer une nouvelle et l'ajouter au dépôt depuis l'interface borgwarehouse.

Doivent donc vivre dans un coffre-fort numérique, sans quoi la sauvegarde est irrécupérable :

  • la passphrase du dépôt borg,
  • la clé du dépôt exportée (borg key export),
  • l'identifiant du dépôt (ex. c7a620ed).

Dépannage

Un conteneur reste unhealthy au premier up — c'est presque toujours le temps : PostgreSQL initialise son cluster, puis Immich joue ses migrations. docker compose logs -f immich-server et laisser tourner. Les données persistent : un simple docker compose up -d repart de là.

Erreur de connexion à la base — vérifier qu'aucune des variables DB_* n'a été modifiée depuis l'initialisation de la base, et que DB_PASSWORD ne contient que des lettres et des chiffres.

Traefik ne route pas — vérifier que le réseau frontend existe (docker network ls) et que le conteneur y est bien attaché (docker inspect immich_server).

Certificat non émis — le DNS doit pointer sur le serveur avant le premier démarrage, sinon le certresolver échoue et retente avec un délai qui grandit à chaque essai. Corriger le DNS puis docker compose restart côté Traefik.

Un gros upload échoue depuis un mobile — voir le réglage respondingTimeouts de Traefik, en haut de ce README.

La recherche intelligente ne trouve rien — le machine learning télécharge ses modèles au premier usage, puis il faut lancer Smart Search dans Administration → Jobs pour indexer la bibliothèque existante. C'est long sur une grosse bibliothèque, et ça mange du CPU.

make backup dit « une sauvegarde est déjà en cours » — le verrou flock fait son travail : le cron tourne au même moment. Attendre, ou regarder $HOME/.immich-backup-<prefix>.log.

Ce qu'on n'a pas activé

Ces options s'ajoutent le jour où le besoin se présente :

  • Accélération matérielle du transcodage (hwaccel.transcoding.yml) et de l'inférence (hwaccel.ml.yml) — dépendent du matériel du serveur. Les blocs extends sont déjà en place, commentés, dans le docker-compose.yml.
  • OAuth / OIDC — Immich sait déléguer l'authentification à un Keycloak. Ça se règle entièrement dans l'interface d'administration, rien à changer ici.
  • Bibliothèques externes — monter un dossier de photos existant en lecture seule, qu'Immich indexe sans le déplacer. Demande un volume supplémentaire sur immich-server.
  • Port local exposé — la stack n'écoute que derrière Traefik. Pour un accès LAN direct, rajouter ports: ['2283:2283'] sur immich-server.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages