auto-hébergement d'une application libre de gestion et de stockage photos
Tue, 04 Feb 2025 15:15:15 +0200J’ai quittĂ© Google photos il y a un moment et depuis je cherchais une alternative lbre Ă©ventuellement en auto-hĂ©bergement (Ă savoir qu’aprĂšs un export pour tout supprimer chez eux il vaut mieux passer par l’application Android mais ca ne se fait pas en 1 clic đ€Ź)
Ce n’est pas les solutions qui manquent mais j’ai des contraintes de ressources CPU et mĂ©moire (NUC core I3 16Go de RAM). J’auto-hĂ©berge d’autres services sur ce NUC aussi il est essentiel que le service soit lĂ©ger et rapide, je privilĂ©gie donc le langage Go (ou Rust) sauf quand il n’y a pas d’autre choix comme avec Peertube.
J’ai dĂ©couvert il y a quelques temps le service de stockage photos Ente.io que j’ai testĂ© avec l’application Android et desktop pour Linux (un prĂ©requis) et c’est vraiment un excellent service. L’application utilisateur est trĂšs bien conçue sur mobile et sur le bureau. Vous pouvez partager des albums avec vos proches et synchroniser un rĂ©pertoire automatiquement, voir toutes les fonctionnalitĂ©s ici : Features.
L’autre gros avantage est que les photos sont chiffrĂ©es cĂŽtĂ© client avant d’arriver sur leur stockage cloud, voir ici l’architecture technique. De plus elles sont rĂ©pliquĂ©es dans 3 datacenters en Europe (dont un chez Scaleway) sur des serveurs S3 compatible.
Le dernier point est que c’est un logiciel libre (serveur et clients) depuis mars 2024 et dont la sĂ©curitĂ© a Ă©tĂ© auditĂ©. La touche finale qui m’a convaincu de le mettre en place @home est que le serveur est en Go et comme il utilise S3 pour le stockage je vais pouvoir utiliser mes serveurs Garage đ
Si vous souhaitez vous auto-hĂ©berger vous pouvez vous aider Ă choisir grĂące Ă ce comparatif des diffĂ©rentes solutions opensource en fonction de vos critĂšres : đž Free and OpenSource Photo Libraries.
Pour toutes ses qualitĂ©s je conseille fortement ce service SaaS si vous n’avez aucune connaissance technique si vous tenez Ă vos photos (mĂȘme question que pour vos mots de passe : quelle valeur vous leur donnez ? đ€). Les tarifs ne me paraissent pas dĂ©connant (Plans) 200Go Ă 5âŹ/mois devrait suffir pour la plupart des gens.
Avant de plonger dans la configuration regardons ce shĂ©ma de l’architecture du serveur
Lors de mes premiers tests je ne comprenais pas pourquoi l’application Android ne fonctionnait pas en 5G (pas de synchro) mais fonctionnait en WIFI. Or comme on le voit il y a un lien en pointillĂ© des applications vers l’object storage S3. Et oui le serveur S3 doit ĂȘtre exposĂ© sur Internet, les applications s’y connectant directement dessus en plus du serveur API Museum.
Ce n’est pas forcĂ©ment choquant, la plupart des fournisseurs S3 comme Scaleway ou Amazon exposent leur service sur Internet mais pour ma part je ne souhaite pas le faire pour mes serveurs Garage. Voici 3 solutions pour utiliser Ente auto-hĂ©bergĂ© :
Dans tous les cas vous devez utiliser soit 1 serveur S3 soit 3 si vous souhaitez une réplication, car elle ne fonctionne pas avec 2.
Je pensais utiliser la 3Ăšme solution mais je souhaite partager des albums Ă des proches et d’autres en public. Ăvidemment dans ce cas il faut un bucket public.
J’ai choisi la solution 2 mais en ajoutant les 2 serveurs Garage chez moi, le serveur primaire Ă©tant chez Scaleway ce qui me permettra de partager des albums tout en ayant une rĂ©plication locale.
Elle va ĂȘtre un peu plus compliquĂ©e que pour d’autres services car il a Ă©tĂ© dĂ©veloppĂ© pour ĂȘtre un SaaS, il n’y a donc pas encore d’interface web d’admin. Si vous ne souhaitez pas utiliser Nomad, Ente propose une documentation pour utiliser uniquement Docker avec docker compose : self-hostring.
Allons y et on commence par S3. La premiĂšre Ă©tape est de configurer un bucket dans Garage.
Cette étape est à sauter si vous utilisez un seul bucket S3 chez un opérateur.
Sur le NUC voici les quelques commandes Ă lancer
garage bucket create ente
garage key create ente-app-key
garage bucket allow --read --write --owner ente --key ente-app-key
garage bucket info ente
Il faut bien sûr bien sauvegarder les token key ID et secret key générées.
Dernier point il faut définir des rÚgles CORS sur ce bucket . Pour cela il faut installer la commande aws sur son PC (sudo pacman -S aws-cli
sur Manjaro) et exporter des variables d’environnement avec les tokens prĂ©cĂ©demment crĂ©es
AWS_ACCESS_KEY_ID=TOKEN_ID
AWS_SECRET_ACCESS_KEY=TOKEN_KEY
AWS_DEFAULT_REGION=garage
AWS_ENDPOINT_URL=http://IP_LOCAL_NUC:3900
Puis créer un fichier cors.json
{
"CORSRules": [
{
"AllowedOrigins": ["*"],
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD", "POST", "PUT", "DELETE"],
"MaxAgeSeconds": 3000,
"ExposeHeaders": ["Etag"]
}
]
}
On peut alors lancer la commande aws pour injecter ces rĂšgles
aws s3api put-bucket-cors --bucket ente --cors-configuration file://cors.json
Ces opĂ©rations sont Ă refaire sur le 2Ăšme serveur garage, chez moi sur un RaspberryPi 4 (voir l’article QNAP : un DAS pour S3).
voici le HCL du serveur Ente pour Nomad. Ce serveur qui s’appelle Museum, sert d’API pour les clients desktop et mobile Ente. Il communique avec le ou les serveurs S3 (locaux ou externes) et stocke ses donnĂ©es dans une base PostgreSQL.
ente.hcl
job "ente" {
datacenters = ["dc1"]
type = "service"
group "home" {
count = 1
network {
mode = "bridge"
port "http" {
to = 8080 # container port the app runs on
host_network = "tailscale"
}
port "postgresql" {
to = 5432 # container port the app runs on
}
}
task "ente" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
value = "nuc"
}
env {
ENTE_CREDENTIALS_FILE = "/credentials.yaml"
}
config {
image = "ghcr.io/ente-io/server"
command = "/bin/sh"
args = [
"-c",
"while ! nc -z 127.0.0.1 5432 </dev/null; do echo 'waiting for postgresql...'; sleep 5; done; /museum",
]
volumes = [
"/data/volumes/ente/logs:/var/logs",
"/data/volumes/ente/data:/data",
"/data/volumes/ente/config/museum.yaml:/museum.yaml",
"/data/volumes/ente/config/credentials.yaml:/credentials.yaml",
"/data/volumes/ente-replication:/replication"
]
ports = [
"http"
]
}
resources {
cpu = 300
memory = 1024
}
service {
name = "ente"
provider = "consul"
port = "http"
tags = ["tricot ente.domain.tld"]
check {
type = "http"
name = "app_health"
path = "/ping"
interval = "20s"
timeout = "10s"
}
}
}
task "postgresql" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
value = "nuc"
}
env {
POSTGRES_PASSWORD = "PASS"
POSTGRES_DB = "ente_db"
POSTGRES_USER = "ente"
}
config {
image = "postgres:16-alpine"
volumes = [
"/data/volumes/ente/postgresql:/var/lib/postgresql/data"
]
ports = [
"postgresql"
]
}
resources {
cpu = 500
memory = 1024
}
service {
name = "ente-postgresql"
provider = "consul"
port = "postgresql"
}
}
}
}
Ce hcl a besoin de 2 fichiers yaml, museum.yaml et credentials.yaml
/data/volumes/ente/config/museum.yaml
apps:
# Default is https://albums.ente.io
#
# If you're running a self hosted instance and wish to serve public links,
# set this to the URL where your albums web app is running.
public-albums: https://ente-albums.domain.tld
key:
encryption: ENCRYPTION
hash: HASH
jwt:
secret: SECRET
#internal:
# admins:
# - 1580559962386438
# Replication config
#
# If enabled, replicate each file to 2 other data centers after it gets
# successfully uploaded to the primary hot storage.
replication:
enabled: true
# The Cloudflare worker to use to download files from the primary hot
# bucket. If this isn't specified, files will be downloaded directly.
worker-url:
# Number of go routines to spawn for replication
# This is not related to the worker-url above.
# Optional, default value is indicated here.
worker-count: 6
# Where to store temporary objects during replication v3
# Optional, default value is indicated here.
tmp-storage: /replication/data
Dans le fichier museum.yaml on a besoin de générer des clés, pour cela il faut cloner le projet git et lancer un programme
git clone https://github.com/ente-io/ente.git
cd ente/server
go mod init gen
go mod tidy
go run tools/gen-random-keys/main.go
key.encryption: ENCRYPTION
key.hash: HASH
jwt.secret: SECRET
le fichier des credentials
/data/volumes/ente/config/credentials.yaml
db:
host: localhost
port: 5432
name: ente_db
user: ente
password: PASS
s3:
are_local_buckets: false
use_path_style_urls: true
b2-eu-cen:
key: TOKEN
secret: SECRET
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
bucket: bucket-name
wasabi-eu-central-2-v3:
key: TOKEN
secret: SECRET
endpoint: http://IP_LOCAL:3900
region: garage
bucket: ente
scw-eu-fr-v3:
key: TOKEN
secret: SECRET
endpoint: http://IP2_LOCAL:3900
region: garage
bucket: ente
A savoir que les noms des rĂ©gions ne doivent pas ĂȘtre changĂ© et la primaire est obligatoirement b2-eu-cen
Une fois le service lancĂ©, verifiez que l’API vous rĂ©ponde pong par cette URL : https://ente.domain.tld/ping
Le serveur Museum ne propose pas d’interface web mais uniquement une API pour les diffĂ©rents clients. Il suffira de lancer un des clients mobile ou desktop pour crĂ©er son compte, le premier Ă©tant admin.
A noter que mĂȘme si n’importe qui peut utiliser votre instance pour demander la crĂ©ation d’un compte il n’aura pas accĂšs au code de validation pour finaliser le compte. Par contre il vous sera nĂ©cessaire pour valider la crĂ©ation du votre puis celui d’Ă©ventuels proches.
Par dĂ©faut les clients Ente vont vouloir crĂ©er un compte sur le SaaS Ente.io. Pour pointer sur sa propre instance il faut cliquer ou tapoter 7 fois sur l’Ă©cran de l’app ce qui va afficher un champ de saisie de l’URL. Mettez celle de votre instance dĂ©clarĂ©e dans le tag tricot (dans l’exemple ente.domain.tld) (Connecting to a custom server.)
Ensuite lancer cette commande sur le NUC pour récupérer le code généré
docker logs -f ID_CONTAINER_ENTE 2>&1 |grep Verification
Indiquer son email et son pass dans le client Ente puis coller le code qui apparait dans le log lorsqu’il est demandĂ©. Une fois le compte crĂ©Ă© il n’aura par dĂ©faut que 5Go d’espace, aussi on lance 2 requĂȘtes SQL dans PostgreSQL pour passer Ă 1To
SELECT user_id from users;
puis celle-ci en remplacant USERID par l’ID renvoyĂ© par le SELECT
INSERT INTO storage_bonus (bonus_id, user_id, storage, type, valid_till) VALUES ('self-hosted-myself', USERID, 1099511627776, 'ADD_ON_SUPPORT', 0);
Il suffit de relancer les applications clientes et si tout va bien vous devriez voir 1To d’espace, sinon se dĂ©connecter puis se reconnecter.
Vient enfin la partie la plus longue, l’import des photos (et vidĂ©os). Je l’ai fait depuis mon PC et il a tournĂ© environ plus de 1 jour pour importer 157 Go (54496 fichiers). Je vous conseille si vous avez beaucoup de photos Ă importer de couper la mise en veille et de ne pas toucher Ă votre PC durant l’import.
J’ai eu quelques soucis sur la rĂ©plication des buckets S3 lorsque j’ai activĂ© l’option machine learning (Apprentissage automatique) dans le client. J’ai pu corriger en supprimant les donnĂ©es ML dans le S3 primaire et dans la base de donnĂ©es PostgreSQL.
# pour S3 bucket
aws s3 rm s3://bucket-ente/XXXXXX/file-data/ --recursive
# pour postgresql
delete from file_data where data_type='mldata';
Le client web permet de partager des albums public et privĂ© par un mot de passe. Dans le fichier museum.yaml l’URL des albums est https://ente-albums.domain.tld
. Les applications desktop et mobile utiliseront cette URL pour générer un lien.
Voici mon album public.
ente-albums.hcl
job "ente-albums" {
datacenters = ["dc1"]
type = "service"
group "home" {
count = 1
network {
port "http" {
to = 3002 # container port the app runs on
host_network = "tailscale"
}
}
task "ente-albums" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
value = "nuc"
}
env {
NEXT_PUBLIC_ENTE_ENDPOINT = "https://ente.domain.tld"
NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT = "https://ente-albums.domain.tld"
}
config {
image = "fredix/ente-albums"
ports = [
"http"
]
}
resources {
cpu = 200
memory = 500
}
service {
name = "ente-albums"
provider = "consul"
port = "http"
tags = ["tricot ente-albums.domain.tld"]
check {
type = "http"
name = "app_health"
path = "/"
interval = "20s"
timeout = "10s"
}
}
}
}
}
A ce jour il n’y a pas encore d’image docker officielle pour l’interface web. J’ai construit la mienne avec ce Dockerfile en Ă©tant positionnĂ© dans le rĂ©pertoire source ente/web
Dockerfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY . .
ARG NEXT_PUBLIC_ENTE_ENDPOINT=https://ente.domain.tld
ENV NEXT_PUBLIC_ENTE_ENDPOINT=${NEXT_PUBLIC_ENTE_ENDPOINT}
ARG NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=https://ente-albums.domain.tld
ENV NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=${NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT}
RUN yarn install && yarn build
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/apps/photos/out .
RUN npm install -g serve
ENV PORT_ALBUMS=3002
EXPOSE ${PORT_ALBUMS}
CMD serve -s . -l tcp://0.0.0.0:3002
Les URLs Ă©tant en dur si vous utilisez mon image fredix/ente-albums
ça ne marchera pas chez vous.
Ente est un excellent service de gestion et stockage chiffrĂ© de photos. Cependant la mise en place d’un auto-hĂ©bergement est peut ĂȘtre plus complexe que d’autres services plus simple. C’est le prix Ă payer pour avoir une rĂ©plication chiffrĂ©e possiblement rĂ©partie sur 3 sites.
A dĂ©faut n’hĂ©sitez pas Ă tester la version gratuite Ă 5Go sur Ente.io. Compte Ă crĂ©er par un des clients desktop/mobile ou par le web.