Rattrapage pb de gitea

Signed-off-by: Gato <cedric@goutailler-olivier.fr>
This commit is contained in:
2026-06-06 21:01:24 +02:00
parent 86fa4f8519
commit 5ba69d1e16
23 changed files with 832 additions and 730 deletions
-125
View File
@@ -1,125 +0,0 @@
# CI/CD — Pipelines Gitea Actions
Bonsai API et Bonsai Webapp disposent chacune de trois workflows Gitea Actions : `ci.yml`, `release.yml` et `rollback.yml`. **Luz** dispose d'un workflow `release.yml` à déclencheur automatique sur `main`. Le runner Gitea s'exécute sur le même serveur que les applications et a accès direct au socket Docker (`/var/run/docker.sock`), ce qui lui permet de piloter les conteneurs sans passer par SSH.
---
## Workflow CI (`ci.yml`)
**Déclencheur** : tout `push` sur n'importe quelle branche, et toute pull request vers `main`.
### Bonsai API
| Étape | Ce qu'elle fait |
|---|---|
| **Checkout** | Clone le dépôt |
| **Cache Java 25** | Vérifie si le JDK est déjà présent dans le cache du runner. Si oui, le téléchargement est sauté (~11 min économisées) |
| **Install Java 25** | *(seulement si cache absent)* Télécharge et extrait le JDK 25 (Temurin, build Alpine) dans `/opt/` |
| **Set up Java 25** | Exporte `JAVA_HOME` et ajoute le JDK au `PATH` pour les étapes suivantes |
| **Run tests** | Lance `./gradlew test` — la suite de tests échoue le workflow si un test est rouge |
| **Upload test report** | *(seulement en cas d'échec)* Publie le rapport HTML de Gradle comme artifact téléchargeable dans l'interface Gitea |
### Bonsai Webapp
| Étape | Ce qu'elle fait |
|---|---|
| **Checkout** | Clone le dépôt |
| **Setup Node.js** | Installe Node.js 22 et active le cache npm automatique |
| **Install dependencies** | Lance `npm ci` pour une installation reproductible (utilise le cache npm si disponible) |
| **Run tests with coverage** | Lance `npm test -- --watch=false` — exécute Vitest avec rapport de couverture |
---
## Workflow Release (`release.yml`)
**Déclencheur** : publication d'une release Gitea (bouton "Publish release" sur un tag `vX.Y.Z`).
### Bonsai API
| Étape | Ce qu'elle fait |
|---|---|
| **Checkout** | Clone le dépôt avec l'historique complet (`fetch-depth: 0`) — nécessaire pour le merge vers `main` |
| **Cache Java 25** | Même logique que le CI |
| **Install Java 25** | *(si cache absent)* Installe le JDK |
| **Set up Java 25** | Configure `JAVA_HOME` et `PATH` |
| **Bump version in build.gradle** | Remplace la version dans `build.gradle` par le tag de la release (ex. `v1.2.3``1.2.3`). Modification locale uniquement, non commitée |
| **Build JAR** | Lance `./gradlew build -x test` pour produire le JAR avec la bonne version. Les tests sont skippés (déjà passés en CI) |
| **Set up Docker Buildx** | Active Buildx pour le build multi-plateforme avec support du cache registry |
| **Login to Gitea registry** | Authentifie le runner sur `git.goutailler-olivier.com` avec le token `RELEASE_TOKEN` |
| **Set lowercase repo name** | Normalise le nom du repo en minuscules (requis par Docker) |
| **Build and push** | Build l'image Docker et la pousse avec deux tags : `vX.Y.Z` (archive) et `latest` (production). Utilise le cache registry pour accélérer les builds suivants |
| **Backup database before deployment** | Via le socket Docker, exécute `pg_dump` dans le conteneur `bonsai-api-db` et sauvegarde le dump compressé dans `/opt/backups/bonsai-api/`. **Bloque le déploiement si le backup échoue.** Conserve les 10 derniers backups |
| **Trigger production deployment** | Appelle le webhook Watchtower — Watchtower détecte la nouvelle image `:latest` et redémarre le conteneur API |
| **Merge release to main** | Restore `build.gradle` (pour annuler la modification locale), puis merge la branche de release dans `main` avec un commit de merge `--no-ff` |
| **Bump version to next SNAPSHOT on develop** | Incrémente le patch sur `develop` (ex. `1.2.3``1.2.4-SNAPSHOT`) et pousse le commit |
### Bonsai Webapp
| Étape | Ce qu'elle fait |
|---|---|
| **Checkout** | Clone le dépôt |
| **Set up Docker Buildx** | Active Buildx avec cache registry |
| **Login to Gitea registry** | Authentifie le runner |
| **Set lowercase repo name** | Normalise le nom du repo |
| **Bump version in package.json** | Met à jour le champ `version` dans `package.json` avec le tag de release |
| **Build and push** | Build l'image Docker (inclut le `npm run build` via le Dockerfile) et pousse `vX.Y.Z` + `latest` |
| **Trigger production deployment** | Appelle le webhook Watchtower |
| **Merge release to main** | Merge la branche de release dans `main` |
| **Bump version to next SNAPSHOT on develop** | Incrémente le patch sur `develop` dans `package.json` |
---
## Workflow Rollback (`rollback.yml`)
**Déclencheur** : manuel via **Actions → Rollback → Run workflow** dans l'interface Gitea.
**Paramètres** :
- `version` *(obligatoire)* — version cible, ex. `v1.2.3`
- `restore_db` *(API seulement, défaut : `no`)*`yes` pour restaurer aussi la base de données
| Étape | Ce qu'elle fait |
|---|---|
| **Login to Gitea registry** | Authentifie le runner sur le registry |
| **Set lowercase repo name** | Normalise le nom du repo |
| **Retag version as latest** | Pull l'image `vX.Y.Z`, la retague en `latest` et la repousse — Watchtower déploiera cette version |
| **Restore database backup** | *(si `restore_db=yes`)* Arrête le conteneur API, supprime et recrée la base de données, restaure le dump compressé correspondant à la version cible depuis `/opt/backups/bonsai-api/` |
| **Trigger production deployment** | Appelle le webhook Watchtower pour redéployer avec l'image rétrogradée |
> **Voir aussi** : [Mise à jour des applications](Mise-a-jour-Applications) pour la procédure de rollback complète et la gestion des backups.
---
## Workflow Release Luz (`release.yml`)
**Déclencheur** : tout `push` sur `main`.
**Versionnage automatique** : le workflow lit le dernier tag `vX.Y.Z`, incrémente le patch (`Z+1`) et crée un nouveau tag. Si aucun tag n'existe, démarre à `v0.1.0`.
| Étape | Ce qu'elle fait |
|---|---|
| **Tests & couverture** | *(job `test`)* Lance `npm ci` puis `npm test -- --watch=false` — bloque la release si les tests échouent |
| **Checkout** | Clone avec `fetch-depth: 0` pour récupérer tous les tags |
| **Calculate next version** | Calcule le prochain tag en incrémentant le patch du dernier tag |
| **Bump version in package.json** | Met à jour le champ `version`, commite et pousse sur `main` |
| **Set up Docker Buildx** | Active Buildx avec cache registry |
| **Login to Gitea registry** | Authentifie le runner sur `git.goutailler-olivier.com` avec `RELEASE_TOKEN` |
| **Build and push** | Build l'image Docker et pousse `vX.Y.Z` + `latest` avec cache registry |
| **Create Gitea release** | Crée la release sur Gitea via l'API avec le tag calculé |
---
## Secrets requis
| Secret | Utilisé par | Rôle |
|---|---|---|
| `RELEASE_TOKEN` | `release.yml`, `rollback.yml` | Token Gitea avec accès en écriture au registry et au dépôt (push sur `main` et `develop`) |
| `WATCHTOWER_TOKEN` | `release.yml`, `rollback.yml` | Token Bearer pour l'API HTTP de Watchtower |
---
## Infrastructure du runner
Le runner (`gitea-runner`) est configuré dans `Infra/gitea/gitea-compose.yml` avec :
- **Label** `ubuntu-latest:host` — les jobs s'exécutent directement dans le conteneur runner
- **Socket Docker monté** (`/var/run/docker.sock`) — accès direct aux conteneurs de l'hôte sans SSH
- **Dossier backups monté** (`/opt/backups`) — les backups écrits par le runner sont persistés sur l'hôte
-15
View File
@@ -1,15 +0,0 @@
# Changelog
## 2026-06-05 — Workflow release automatique pour Luz
- `CI-CD.md` — documentation du workflow `release.yml` de Luz : déclencheur push main, versionnage automatique, jobs test + release
## 2026-06-05 — Ajout du projet Luz
- `Home.md` — ajout de Luz dans l'architecture et le tableau des services
- `Installation-Production.md` — section 7 Luz avec commandes de démarrage et secrets requis
- `Mise-a-jour-Applications.md` — procédure de rollback Luz, Luz ajouté dans la boucle de mise à jour globale
## 2026-06-02 — Fix CI release Bonsai-webapp : bump version commité avant merge main
- `Mise-a-jour-Applications.md` — documentation du bug (package.json modifié non commité bloquait git checkout main) et de la correction (commit du bump avant le merge, sauvegarde du RELEASE_HEAD)
-41
View File
@@ -1,41 +0,0 @@
# Infrastructure — Vue d'ensemble
Ce dépôt contient toutes les configurations Docker Compose de l'infrastructure auto-hébergée.
## Architecture
```
Internet
Traefik (reverse proxy, TLS Let's Encrypt)
├── git.goutailler-olivier.com → Gitea (forge + CI/CD)
├── auth.goutailler-olivier.com → Keycloak (SSO / OAuth2)
├── bonsai.goutailler-olivier.com
│ ├── /api → Bonsai API (Spring Boot)
│ └── / → Bonsai Webapp (front-end)
├── luz.goutailler-olivier.com → Luz (front-end Angular)
├── cloud.goutailler-olivier.com → Nextcloud
└── notes.goutailler-olivier.com → Trilium
```
## Services
| Service | Dossier | URL |
|---|---|---|
| Traefik | `traefik/` | `traefik.goutailler-olivier.com` |
| Gitea | `gitea/` | `git.goutailler-olivier.com` |
| Keycloak | `keycloak/` | `auth.goutailler-olivier.com` |
| Bonsai API | `bonsai-api/` | `bonsai.goutailler-olivier.com/api` |
| Bonsai Webapp | `bonsai-webapp/` | `bonsai.goutailler-olivier.com` |
| Luz | `luz/` | `luz.goutailler-olivier.com` |
| Nextcloud | `nextcloud/` | `cloud.goutailler-olivier.com` |
| Trilium | `trilium/` | `notes.goutailler-olivier.com` |
## Pages
- [Installation Production](Installation-Production)
- [Installation Développement](Installation-Developpement)
- [Mise à jour des applications](Mise-a-jour-Applications)
- [CI/CD — Pipelines Gitea Actions](CI-CD)
-150
View File
@@ -1,150 +0,0 @@
# Installation Développement
Cette page décrit comment démarrer l'environnement de développement local pour le projet **Bonsai API**.
## Prérequis
| Outil | Version minimale |
|---|---|
| Java JDK | 25 |
| Docker + Docker Compose | 24+ |
| Gradle | 8+ (wrapper inclus dans le dépôt) |
---
## Cloner le dépôt
```bash
git clone https://git.goutailler-olivier.com/bonsai/bonsai-api.git
cd bonsai-api
```
---
## Option A — Hot-reload avec Docker Compose (recommandé)
Cette option monte les sources depuis l'hôte dans le conteneur. Gradle détecte les changements et relance automatiquement l'application.
```bash
docker compose -f docker-compose.dev.yml up --build
```
- L'API est disponible sur `http://localhost:8080`
- PostgreSQL est disponible sur `localhost:5432`
- Le cache Gradle est conservé dans un volume dédié (`gradle_home`) : le premier build télécharge les dépendances, les suivants sont rapides
Pour arrêter et tout supprimer (y compris les volumes) :
```bash
docker compose -f docker-compose.dev.yml down -v
```
---
## Option B — Gradle en local + PostgreSQL Docker
Démarrer uniquement la base de données :
```bash
docker compose up db -d
```
Lancer l'API avec le wrapper Gradle :
```bash
./gradlew bootRun
```
Flyway applique automatiquement la migration `V1__init.sql` au premier démarrage.
---
## Variables d'environnement
En développement, les valeurs par défaut sont utilisées automatiquement (définies dans `src/main/resources/application.yml`). Aucun `.env` n'est nécessaire.
| Variable | Valeur locale | Description |
|---|---|---|
| `DATASOURCE_URL` | `jdbc:postgresql://localhost:5432/bonsai` | URL JDBC |
| `DATASOURCE_USERNAME` | `bonsai` | Utilisateur PostgreSQL |
| `DATASOURCE_PASSWORD` | `bonsai` | Mot de passe PostgreSQL |
| `KEYCLOAK_JWKS_URI` | `https://auth.goutailler-olivier.com/realms/bonsai/protocol/openid-connect/certs` | Endpoint JWKS |
| `CORS_ALLOWED_ORIGIN_PROD` | `https://bonsai.goutailler-olivier.com` | Origine CORS de prod |
L'origine `http://localhost:4200` est toujours autorisée en CORS (front Angular en dev).
---
## Documentation de l'API
Swagger UI est disponible à l'adresse suivante une fois l'API démarrée :
```
http://localhost:8080/swagger-ui.html
```
La spécification OpenAPI (JSON) est accessible sur :
```
http://localhost:8080/v3/api-docs
```
---
## Lancer les tests
```bash
./gradlew test
```
Le rapport HTML est généré dans `build/reports/tests/test/index.html`.
---
## Structure du projet
```
src/main/java/fr/bonsai/api/
├── domain/model/ # Entités métier (sans dépendance Spring)
├── application/
│ ├── port/in/ # Interfaces des use cases
│ ├── port/out/ # Interface du repository
│ └── usecase/ # Logique métier (IssueService)
├── adapter/
│ ├── in/web/ # Controllers REST et DTOs
│ └── out/persistence/ # Entités JPA et adaptateur repository
└── config/ # Configuration Spring (Security, CORS, Beans)
```
---
## Sécurité
Toutes les routes nécessitent un token JWT Bearer valide émis par Keycloak :
- **Realm** : `bonsai`
- **Client** : `bonsai-webapp`
- **Issuer** : `https://auth.goutailler-olivier.com/realms/bonsai`
Pour tester sans Keycloak local, utiliser l'instance de production comme fournisseur JWKS (valeur par défaut).
```http
Authorization: Bearer <token>
```
---
## Endpoints disponibles
| Méthode | Route | Description |
|---|---|---|
| `GET` | `/issues` | Liste toutes les issues |
| `POST` | `/issues` | Crée une issue |
| `PUT` | `/issues/{id}` | Remplace une issue |
| `DELETE` | `/issues/{id}` | Supprime une issue (204) |
---
## CI/CD
Le pipeline Gitea Actions (`.gitea/workflows/`) construit l'image Docker et la pousse sur le registre interne à chaque push sur `main`. Le runner `ubuntu-latest` est fourni par le conteneur `act_runner` de la stack Gitea.
-212
View File
@@ -1,212 +0,0 @@
# Installation Production
## Prérequis
- Serveur Linux avec Docker 24+ et Docker Compose V2
- Domaine DNS pointant vers le serveur (`goutailler-olivier.com`)
- Ports **80**, **443** et **2222** ouverts en entrée
---
## 1. Réseau Docker partagé
Tous les services communiquent via un réseau externe `proxy`. À créer une seule fois :
```bash
docker network create proxy
```
---
## 2. Traefik
Traefik est le point d'entrée unique : il gère le TLS (Let's Encrypt) et route les requêtes vers chaque service.
```bash
cd traefik/
docker compose up -d
```
Le dashboard est exposé sur `https://traefik.goutailler-olivier.com` (accès restreint par défaut).
---
## 3. Keycloak
Keycloak gère l'authentification SSO pour toute l'infrastructure.
```bash
cd keycloak/
cp .env.example .env
# Éditer .env avec des mots de passe sécurisés
docker compose up -d
```
Variables à définir dans `.env` :
| Variable | Description |
|---|---|
| `POSTGRES_PASSWORD` | Mot de passe de la base PostgreSQL |
| `KEYCLOAK_ADMIN` | Login administrateur (défaut : `admin`) |
| `KEYCLOAK_ADMIN_PASSWORD` | Mot de passe administrateur |
**Configuration post-démarrage :**
1. Se connecter à `https://auth.goutailler-olivier.com/admin`
2. Créer le realm `bonsai`
3. Créer le client `bonsai-webapp` (type *OpenID Connect*, flux *Authorization Code*)
4. Configurer les *Valid redirect URIs* : `https://bonsai.goutailler-olivier.com/*`
---
## 4. Gitea
Gitea est la forge Git avec le runner CI/CD intégré.
```bash
cd gitea/
docker compose -f gitea-compose.yml up -d
```
**Configuration post-démarrage :**
1. Accéder à `https://git.goutailler-olivier.com` et terminer l'installation via l'interface web
2. Créer l'organisation `bonsai`
3. Récupérer un token d'enregistrement pour le runner dans *Administration → Actions → Runners*
4. Mettre à jour `GITEA_RUNNER_REGISTRATION_TOKEN` dans `gitea-compose.yml`, puis redémarrer le service `act_runner`
Le runner est configuré avec le label `ubuntu-latest` mappé sur `ubuntu:22.04`.
---
## 5. Bonsai API
L'image est construite par la CI Gitea et poussée sur le registre `git.goutailler-olivier.com/bonsai/bonsai-api:latest`.
Le registre Gitea requiert une authentification. Se connecter une première fois avec son compte Gitea :
```bash
docker login git.goutailler-olivier.com -u <utilisateur>
```
> **Note :** par défaut, Docker stocke le mot de passe en clair dans `~/.docker/config.json`. Pour éviter cela, configurer un [credential helper](https://docs.docker.com/engine/reference/commandline/login/#credentials-store).
```bash
cd bonsai-api/
# Créer un fichier .env avec le mot de passe PostgreSQL
echo "POSTGRES_PASSWORD=<mot_de_passe_sécurisé>" > .env
docker compose up -d
```
Variables d'environnement :
| Variable | Description |
|---|---|
| `POSTGRES_PASSWORD` | Mot de passe PostgreSQL (injecté via `.env`) |
| `DATASOURCE_URL` | `jdbc:postgresql://db:5432/bonsai` (défaut réseau interne) |
| `KEYCLOAK_JWKS_URI` | `https://auth.goutailler-olivier.com/realms/bonsai/protocol/openid-connect/certs` |
| `CORS_ALLOWED_ORIGIN_PROD` | `https://bonsai.goutailler-olivier.com` |
Flyway applique automatiquement les migrations SQL au démarrage.
**Documentation API (Swagger UI) :**
| Environnement | URL |
|---|---|
| Production | `https://bonsai.goutailler-olivier.com/api/swagger-ui.html` |
| Développement local | `http://localhost:8080/swagger-ui.html` |
La définition OpenAPI brute est disponible sur `/v3/api-docs`.
---
## 6. Bonsai Webapp
```bash
cd bonsai-webapp/
docker compose up -d
```
L'image `git.goutailler-olivier.com/bonsai/bonsai-webapp:latest` est également construite par la CI. Aucune variable d'environnement spécifique n'est requise.
---
## 7. Luz
```bash
cd luz/
docker compose up -d
```
L'image `git.goutailler-olivier.com/gato/luz:latest` est construite par la CI Gitea lors d'une release. Aucune variable d'environnement spécifique n'est requise.
**Secrets Gitea à configurer dans le dépôt Luz :**
| Secret | Description |
|---|---|
| `RELEASE_TOKEN` | Token Gitea avec droits `write:packages` et `write:repository` |
| `WATCHTOWER_TOKEN` | Token HTTP de l'API Watchtower pour déclencher le redéploiement |
---
## 9. Nextcloud
```bash
cd nextcloud/
# Adapter les mots de passe dans docker-compose.yml avant le premier démarrage
docker compose up -d
```
Variables à personnaliser dans `docker-compose.yml` avant le premier lancement :
| Variable | Description |
|---|---|
| `POSTGRES_PASSWORD` | Mot de passe PostgreSQL |
| `NEXTCLOUD_ADMIN_USER` | Compte administrateur Nextcloud |
| `NEXTCLOUD_ADMIN_PASSWORD` | Mot de passe administrateur |
---
## 10. Trilium
```bash
cd trilium/
docker compose up -d
```
Les données sont persistées dans `/home/gato/Applications/Trilium/data` sur l'hôte. S'assurer que ce chemin existe avant le démarrage :
```bash
mkdir -p /home/gato/Applications/Trilium/data
```
---
## Ordre de démarrage recommandé
```
1. Réseau proxy (une seule fois)
2. Traefik
3. Keycloak
4. Gitea
5. Bonsai API
6. Bonsai Webapp
7. Luz
8. Nextcloud
9. Trilium
```
---
## Vérifications
```bash
# État de tous les conteneurs
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Logs d'un service
docker logs <nom_conteneur> --tail 50 -f
# Certificats TLS Traefik
docker logs traefik 2>&1 | grep -i "certificate\|acme"
```
-185
View File
@@ -1,185 +0,0 @@
# Mise à jour des applications
Chaque application est packagée sous forme d'image Docker publiée sur le registre Gitea (`git.goutailler-olivier.com`) par la CI lors d'une release. La mise à jour en production consiste à récupérer la nouvelle image et à recréer le conteneur.
---
## Principe général
```bash
cd <dossier-du-service>/
docker compose pull # télécharge la nouvelle image
docker compose up -d # recrée le conteneur si l'image a changé
```
`docker compose up -d` détecte automatiquement que l'image locale est différente de celle utilisée par le conteneur en cours et le recrée. Le temps d'indisponibilité est limité à la durée du redémarrage du conteneur (quelques secondes).
---
## Exemple : mettre à jour Bonsai Webapp
Une nouvelle release a été publiée sur Gitea et l'image `bonsai-webapp:latest` a été mise à jour par la CI.
```bash
cd bonsai-webapp/
docker compose pull
docker compose up -d
```
Vérifier que le conteneur tourne bien avec la nouvelle image :
```bash
docker inspect bonsai-webapp --format '{{.Image}}'
# ou
docker compose ps
```
---
## Mettre à jour une version précise (tag de release)
Par défaut les `docker-compose.yml` pointent sur `:latest`. Pour épingler une version spécifique, éditer le fichier et remplacer le tag :
```yaml
image: git.goutailler-olivier.com/bonsai/bonsai-webapp:v1.2.3
```
Puis appliquer :
```bash
docker compose pull
docker compose up -d
```
---
## Notes sur le workflow de release (CI)
### `fetch-depth: 0` requis sur le checkout
Le job `release.yml` de Bonsai-webapp (et Bonsai-api) effectue un `git merge` du tag de release dans `main` à la fin du pipeline. `actions/checkout@v4` fait par défaut un clone superficiel (`--depth 1`), ce qui empêche git de trouver l'ancêtre commun entre le tag et `main` → erreur `refusing to merge unrelated histories`.
**Solution appliquée** : ajouter `fetch-depth: 0` sur le step Checkout :
```yaml
- name: Checkout
uses: https://github.com/actions/checkout@v4
with:
fetch-depth: 0
```
Sans ce paramètre, tout nouveau runner qui clone le dépôt avec historique limité fera échouer l'étape « Merge release to main ».
### Bump de version commité avant le merge
Le step « Bump version in package.json » modifie `package.json` pour y inscrire la version de la release. Ce changement est **commité sur le HEAD détaché** (tag checkout) avant que le build Docker ne commence. Le step « Merge release to main » sauvegarde ensuite le hash de ce commit (`RELEASE_HEAD`) et merge ce commit (et non le tag) sur `main` :
```yaml
- name: Bump version in package.json
run: |
# ... sed + git config ...
git add package.json
git commit -m "chore: set version to $VERSION"
- name: Merge release to main
run: |
RELEASE_HEAD=$(git rev-parse HEAD)
git checkout main
git merge --no-ff "$RELEASE_HEAD" -m "chore: release ..."
git push origin main
```
**Pourquoi** : sans ce commit préalable, `git checkout main` échoue avec *"Your local changes would be overwritten by checkout"* car `package.json` est modifié mais non commité.
---
## Revenir à une version précédente (rollback)
Un workflow Gitea Actions dédié permet de déclencher un rollback sans toucher au serveur manuellement.
### Rollback Bonsai Webapp
1. Aller dans **Gitea → Bonsai-webapp → Actions → Rollback → Run workflow**
2. Saisir la version cible (ex. `v1.2.3`)
3. Lancer — Watchtower redéploie automatiquement
### Rollback Bonsai API (avec ou sans restauration BDD)
1. Aller dans **Gitea → Bonsai-api → Actions → Rollback → Run workflow**
2. Saisir la version cible (ex. `v1.2.3`)
3. Choisir `restore_db` :
- `no` (défaut) — rollback du code uniquement (migrations Flyway compatibles)
- `yes` — rollback du code **et** restauration de la base de données depuis le backup créé avant ce déploiement
> **Attention** : `restore_db=yes` écrase toutes les données créées depuis le déploiement à annuler. À réserver aux cas où la migration de schéma est incompatible avec l'ancienne version.
### Ce que fait le workflow en coulisse
1. Retague l'image `image:vX.Y.Z` en `image:latest` dans le registry Gitea
2. (Si `restore_db=yes`) Via le socket Docker du runner, arrête l'API et restaure le dump PostgreSQL depuis `/opt/backups/bonsai-api/`
3. Déclenche Watchtower → redéploie le conteneur avec la version rétrogradée
---
## Backups de la base de données
Un backup compressé (`pg_dump | gzip`) est créé automatiquement **avant chaque déploiement** de Bonsai API.
- **Emplacement sur le serveur** : `/opt/backups/bonsai-api/`
- **Format du nom** : `bonsai_v1.2.3_20260531_143000.sql.gz`
- **Rétention** : les 10 derniers backups sont conservés, les plus anciens sont supprimés automatiquement
Pour lister les backups disponibles sur le serveur :
```bash
ls -lh /opt/backups/bonsai-api/
```
Pour restaurer manuellement un backup :
```bash
VERSION="v1.2.3"
BACKUP_FILE=$(ls -t /opt/backups/bonsai-api/bonsai_${VERSION}_*.sql.gz | head -1)
docker stop bonsai-api
docker exec bonsai-api-db psql -U bonsai postgres \
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='bonsai' AND pid != pg_backend_pid();"
docker exec bonsai-api-db dropdb -U bonsai bonsai
docker exec bonsai-api-db createdb -U bonsai bonsai
gunzip -c "$BACKUP_FILE" | docker exec -i bonsai-api-db psql -U bonsai bonsai
docker start bonsai-api
```
---
## Rollback Luz
1. Aller dans **Gitea → Luz → Actions → Rollback → Run workflow**
2. Saisir la version cible (ex. `v1.2.3`)
3. Lancer — Watchtower redéploie automatiquement
---
## Mettre à jour tous les services d'un coup
```bash
for dir in traefik keycloak gitea bonsai-api bonsai-webapp luz nextcloud trilium; do
echo "=== $dir ==="
(cd "$dir" && docker compose pull && docker compose up -d)
done
```
---
## Vérifications après mise à jour
```bash
# État de tous les conteneurs
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
# Logs du service mis à jour
docker logs bonsai-webapp --tail 50 -f
# Supprimer les anciennes images devenues inutiles
docker image prune -f
```
+54
View File
@@ -0,0 +1,54 @@
name: bonsai-api-stack
services:
db:
image: postgres:16-alpine
container_name: bonsai-api-db
restart: unless-stopped
environment:
POSTGRES_DB: bonsai
POSTGRES_USER: bonsai
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
TZ: Europe/Paris
healthcheck:
test: ["CMD-SHELL", "pg_isready -U bonsai -d bonsai"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- ./db_data:/var/lib/postgresql/data
networks:
- bonsai-api-net
api:
image: git.goutailler-olivier.com/bonsai/bonsai-api:latest
container_name: bonsai-api
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
DATASOURCE_URL: jdbc:postgresql://db:5432/bonsai
DATASOURCE_USERNAME: bonsai
DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}
KEYCLOAK_JWKS_URI: https://auth.goutailler-olivier.com/realms/bonsai/protocol/openid-connect/certs
CORS_ALLOWED_ORIGIN_PROD: https://bonsai.goutailler-olivier.com
TZ: Europe/Paris
networks:
- bonsai-api-net
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.bonsai-api.rule=Host(`bonsai.goutailler-olivier.com`) && PathPrefix(`/api`)
- traefik.http.routers.bonsai-api.entrypoints=websecure
- traefik.http.routers.bonsai-api.tls.certresolver=le
- traefik.http.services.bonsai-api.loadbalancer.server.port=8080
- traefik.docker.network=proxy
- com.centurylinklabs.watchtower.enable=true
networks:
bonsai-api-net:
driver: bridge
proxy:
external: true
name: proxy
+27
View File
@@ -0,0 +1,27 @@
name: bonsai-webapp
services:
bonsai-webapp:
image: git.goutailler-olivier.com/bonsai/bonsai-webapp:latest
container_name: bonsai-webapp
restart: unless-stopped
environment:
TZ: Europe/Paris
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.bonsai-webapp.rule=Host(`bonsai.goutailler-olivier.com`)
- traefik.http.routers.bonsai-webapp.entrypoints=websecure
- traefik.http.routers.bonsai-webapp.tls.certresolver=le
- traefik.http.services.bonsai-webapp.loadbalancer.server.port=80
- traefik.docker.network=proxy
- com.centurylinklabs.watchtower.enable=true
networks:
proxy:
external: true
name: proxy
+9
View File
@@ -0,0 +1,9 @@
FROM gitea/act_runner:latest
# Installer Node.js et npm
RUN apk add --no-cache nodejs npm
# Docker CLI déjà installé via le wget précédent
RUN wget -O /tmp/docker.tgz https://download.docker.com/linux/static/stable/x86_64/docker-26.1.4.tgz \
&& tar -xzf /tmp/docker.tgz --strip-components=1 -C /usr/local/bin docker/docker \
&& rm /tmp/docker.tgz
+13
View File
@@ -0,0 +1,13 @@
## Mise a jours
```bash
docker compose -f gitea-compose.yml down
tar -czvf gitea_backup_$(date +%Y%m%d).tar.gz ./gitea ./db_data
docker compose -f gitea-compose.yml pull gitea
docker compose -f gitea-compose.yml up -d
docker exec gitea gitea --version
```
Relancer le runner
```bash
docker compose -f gitea-compose.yml restart act_runner
```
+1 -2
View File
@@ -88,7 +88,7 @@ services:
- gitea
environment:
GITEA_INSTANCE_URL: http://gitea:3000
GITEA_RUNNER_REGISTRATION_TOKEN: IZM8wKkzR4XZogOxsb5or3JKiugXyguFtA0zjNWZ
GITEA_RUNNER_REGISTRATION_TOKEN: Rvi31evVGlyH8o1h2lw200uMjOJyCrBQJXLKQqJk
GITEA_RUNNER_NAME: docker-runner
GITEA_RUNNER_LABELS: ubuntu-latest:host
CONFIG_FILE: /config.yaml
@@ -98,7 +98,6 @@ services:
- ./runner_data:/data
- ./runner_data/config.yaml:/config.yaml
- /var/run/docker.sock:/var/run/docker.sock
- /opt/backups:/opt/backups
networks:
- gitea-net
+3
View File
@@ -0,0 +1,3 @@
POSTGRES_PASSWORD=changeme
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=changeme
+67
View File
@@ -0,0 +1,67 @@
name: keycloak-stack
services:
db:
image: postgres:16-alpine
container_name: keycloak-db
restart: unless-stopped
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
TZ: Europe/Paris
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak -d keycloak"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- ./db_data:/var/lib/postgresql/data
networks:
- keycloak-net
keycloak:
image: quay.io/keycloak/keycloak:26.2
container_name: keycloak
restart: unless-stopped
command: start
depends_on:
db:
condition: service_healthy
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://db:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KC_HOSTNAME: auth.goutailler-olivier.com
KC_HOSTNAME_STRICT: "true"
KC_HTTP_ENABLED: "true"
KC_PROXY_HEADERS: xforwarded
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN:-admin}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
TZ: Europe/Paris
KC_SPI_THEME_STATIC_MAX_AGE: "-1"
KC_SPI_THEME_CACHE_THEMES: "false"
KC_SPI_THEME_CACHE_TEMPLATES: "false"
volumes:
- ./themes:/opt/keycloak/themes
networks:
- keycloak-net
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.keycloak.rule=Host(`auth.goutailler-olivier.com`)
- traefik.http.routers.keycloak.entrypoints=websecure
- traefik.http.routers.keycloak.tls.certresolver=le
- traefik.http.services.keycloak.loadbalancer.server.port=8080
- traefik.docker.network=proxy
networks:
keycloak-net:
driver: bridge
proxy:
external: true
name: proxy
@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="${(locale.currentLanguageTag)!'fr'}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bonsai — Réinitialisation du mot de passe</title>
<link rel="stylesheet" href="${url.resourcesPath}/css/login.css">
</head>
<body>
<div class="page">
<div class="card">
<div class="logo">
<svg viewBox="0 0 48 56" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M14 56 L16 49 L32 49 L34 56 Z" fill="#C05621"/>
<rect x="12" y="45" width="24" height="5" rx="2" fill="#9C4221"/>
<path d="M24 45 C24 39 22 33 20 27 C18 22 19 17 22 13" stroke="#744210" stroke-width="4" stroke-linecap="round"/>
<path d="M21 28 C26 25 31 22 33 18" stroke="#744210" stroke-width="2.5" stroke-linecap="round"/>
<path d="M20 35 C15 32 11 28 10 24" stroke="#744210" stroke-width="2" stroke-linecap="round"/>
<circle cx="10" cy="21" r="9" fill="#276749"/>
<circle cx="34" cy="16" r="10" fill="#276749"/>
<circle cx="22" cy="11" r="11" fill="#2F855A"/>
<circle cx="26" cy="17" r="8" fill="#38A169"/>
<circle cx="18" cy="16" r="6" fill="#48BB78"/>
</svg>
<span class="logo-name">Bonsai</span>
</div>
<h1 class="title">Mot de passe oublié ?</h1>
<p class="subtitle">Saisissez votre email pour recevoir un lien de réinitialisation.</p>
<#if message?has_content>
<div class="alert alert--${message.type}">
${message.summary}
</div>
</#if>
<form action="${url.loginAction}" method="post">
<div class="field">
<label for="username">Email ou nom d'utilisateur</label>
<input
type="text"
id="username"
name="username"
value="${(auth.attemptedUsername!'')}"
autocomplete="username"
autofocus
/>
</div>
<button type="submit" class="btn-primary">Envoyer le lien</button>
</form>
<p class="register-link">
<a href="${url.loginUrl}">← Retour à la connexion</a>
</p>
</div>
</div>
</body>
</html>
+105
View File
@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="${(locale.currentLanguageTag)!'fr'}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bonsai — Connexion</title>
<link rel="stylesheet" href="${url.resourcesPath}/css/login.css">
</head>
<body>
<div class="page">
<div class="card">
<div class="logo">
<svg viewBox="0 0 48 56" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M14 56 L16 49 L32 49 L34 56 Z" fill="#C05621"/>
<rect x="12" y="45" width="24" height="5" rx="2" fill="#9C4221"/>
<path d="M24 45 C24 39 22 33 20 27 C18 22 19 17 22 13" stroke="#744210" stroke-width="4" stroke-linecap="round"/>
<path d="M21 28 C26 25 31 22 33 18" stroke="#744210" stroke-width="2.5" stroke-linecap="round"/>
<path d="M20 35 C15 32 11 28 10 24" stroke="#744210" stroke-width="2" stroke-linecap="round"/>
<circle cx="10" cy="21" r="9" fill="#276749"/>
<circle cx="34" cy="16" r="10" fill="#276749"/>
<circle cx="22" cy="11" r="11" fill="#2F855A"/>
<circle cx="26" cy="17" r="8" fill="#38A169"/>
<circle cx="18" cy="16" r="6" fill="#48BB78"/>
</svg>
<span class="logo-name">Bonsai</span>
</div>
<h1 class="title">Connexion</h1>
<#if message?has_content>
<div class="alert alert--${message.type}">
${message.summary}
</div>
</#if>
<#if realm.password>
<form action="${url.loginAction}" method="post" novalidate>
<input type="hidden" id="id-hidden-input" name="credentialId"
<#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>/>
<div class="field">
<label for="username">Email ou nom d'utilisateur</label>
<input
type="text"
id="username"
name="username"
value="${(login.username!'')}"
autocomplete="username"
<#if usernameEditDisabled??>disabled</#if>
autofocus
/>
</div>
<div class="field">
<div class="field-label-row">
<label for="password">Mot de passe</label>
<#if realm.resetPasswordAllowed>
<a href="${url.loginResetCredentialsUrl}" class="forgot-link" tabindex="5">
Mot de passe oublié ?
</a>
</#if>
</div>
<input
type="password"
id="password"
name="password"
autocomplete="current-password"
/>
</div>
<#if realm.rememberMe && !usernameEditDisabled??>
<div class="remember">
<input type="checkbox" id="rememberMe" name="rememberMe"
<#if login.rememberMe??>checked</#if>>
<label for="rememberMe">Se souvenir de moi</label>
</div>
</#if>
<button type="submit" class="btn-primary">Se connecter</button>
</form>
</#if>
<#if social.providers?has_content>
<div class="divider"><span>ou</span></div>
<div class="socials">
<#list social.providers as p>
<a href="${p.loginUrl}" class="btn-social">
Continuer avec ${p.displayName!''}
</a>
</#list>
</div>
</#if>
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
<p class="register-link">
Pas encore de compte ?
<a href="${url.registrationUrl}">Créer un compte</a>
</p>
</#if>
</div>
</div>
</body>
</html>
@@ -0,0 +1,262 @@
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
font-size: 0.9rem;
color: #111827;
background: #f0fdf4;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
/* ── Page ── */
.page {
width: 100%;
padding: 1.5rem;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* ── Card ── */
.card {
background: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
padding: 2.5rem 2rem;
width: 100%;
max-width: 380px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
display: flex;
flex-direction: column;
gap: 1.25rem;
}
/* ── Logo ── */
.logo {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
}
.logo svg {
width: 52px;
height: 60px;
}
.logo-name {
font-size: 1.3rem;
font-weight: 800;
color: #111827;
letter-spacing: -0.02em;
}
/* ── Titles ── */
.title {
font-size: 1.1rem;
font-weight: 700;
color: #111827;
text-align: center;
}
.subtitle {
font-size: 0.85rem;
color: #6b7280;
text-align: center;
line-height: 1.5;
}
/* ── Alert ── */
.alert {
padding: 0.65rem 0.875rem;
border-radius: 0.5rem;
font-size: 0.85rem;
line-height: 1.4;
}
.alert--error {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fecaca;
}
.alert--warning {
background: #fffbeb;
color: #d97706;
border: 1px solid #fde68a;
}
.alert--success {
background: #f0fdf4;
color: #16a34a;
border: 1px solid #bbf7d0;
}
.alert--info {
background: #eff6ff;
color: #2563eb;
border: 1px solid #bfdbfe;
}
/* ── Form fields ── */
.field {
display: flex;
flex-direction: column;
gap: 0.375rem;
}
.field-label-row {
display: flex;
justify-content: space-between;
align-items: baseline;
}
.field label {
font-size: 0.85rem;
font-weight: 500;
color: #374151;
}
.field input {
width: 100%;
padding: 0.55rem 0.75rem;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
font-size: 0.9rem;
color: #111827;
background: #ffffff;
outline: none;
transition: border-color 0.15s, box-shadow 0.15s;
}
.field input:focus {
border-color: #2F855A;
box-shadow: 0 0 0 3px rgba(47, 133, 90, 0.15);
}
.field input:disabled {
background: #f3f4f6;
color: #9ca3af;
cursor: not-allowed;
}
.forgot-link {
font-size: 0.78rem;
color: #2F855A;
text-decoration: none;
}
.forgot-link:hover {
text-decoration: underline;
}
/* ── Remember me ── */
.remember {
display: flex;
align-items: center;
gap: 0.5rem;
}
.remember input[type="checkbox"] {
width: 1rem;
height: 1rem;
accent-color: #2F855A;
cursor: pointer;
flex-shrink: 0;
}
.remember label {
font-size: 0.85rem;
color: #374151;
cursor: pointer;
}
/* ── Primary button ── */
.btn-primary {
width: 100%;
padding: 0.6rem;
background: #2F855A;
color: #ffffff;
font-size: 0.9rem;
font-weight: 600;
border: none;
border-radius: 0.5rem;
cursor: pointer;
transition: background 0.15s;
}
.btn-primary:hover {
background: #276749;
}
.btn-primary:active {
background: #1e5236;
}
/* ── Divider ── */
.divider {
display: flex;
align-items: center;
gap: 0.75rem;
color: #9ca3af;
font-size: 0.8rem;
}
.divider::before,
.divider::after {
content: '';
flex: 1;
border-top: 1px solid #e5e7eb;
}
/* ── Social buttons ── */
.socials {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.btn-social {
display: block;
width: 100%;
padding: 0.55rem;
text-align: center;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
background: #ffffff;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
text-decoration: none;
transition: background 0.1s, border-color 0.1s;
}
.btn-social:hover {
background: #f3f4f6;
border-color: #9ca3af;
}
/* ── Register link ── */
.register-link {
text-align: center;
font-size: 0.83rem;
color: #6b7280;
}
.register-link a {
color: #2F855A;
text-decoration: none;
font-weight: 500;
}
.register-link a:hover {
text-decoration: underline;
}
@@ -0,0 +1,2 @@
parent=keycloak
styles=css/login.css
+27
View File
@@ -0,0 +1,27 @@
name: luz
services:
luz:
image: git.goutailler-olivier.com/gato/luz:latest
container_name: luz
restart: unless-stopped
environment:
TZ: Europe/Paris
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.luz.rule=Host(`luz.goutailler-olivier.com`)
- traefik.http.routers.luz.entrypoints=websecure
- traefik.http.routers.luz.tls.certresolver=le
- traefik.http.services.luz.loadbalancer.server.port=80
- traefik.docker.network=proxy
- com.centurylinklabs.watchtower.enable=true
networks:
proxy:
external: true
name: proxy
+95
View File
@@ -0,0 +1,95 @@
# Nextcloud on port 8088 with Postgres and pgAdmin
# ------------------------------------------------
# Quick start:
# docker compose up -d # (Compose V2 syntax; no `version:` key)
name: nextcloud-stack
services:
db:
image: postgres:16-alpine
container_name: nextcloud-db
restart: unless-stopped
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: changeme
TZ: Europe/Paris
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- ./db_data:/var/lib/postgresql/data
networks:
- nextcloud-net
nextcloud:
image: nextcloud:latest
container_name: nextcloud-app
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
POSTGRES_HOST: db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: changeme
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: adminpass
NEXTCLOUD_TRUSTED_DOMAINS: cloud.goutailler-olivier.com
NEXTCLOUD_OVERWRITEHOST: cloud.goutailler-olivier.com
NEXTCLOUD_OVERWRITEPROTOCOL: https
NEXTCLOUD_TRUSTED_PROXIES: 172.23.0.0/16
APACHE_DISABLE_REWRITE_IP: "1"
PHP_MEMORY_LIMIT: 1G
PHP_UPLOAD_LIMIT: 2G
TZ: Europe/Paris
volumes:
- ./nextcloud_app:/var/www/html
- ./nextcloud_data:/var/www/html/data
networks:
- nextcloud-net
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.nextcloud.rule=Host(`cloud.goutailler-olivier.com`)
- traefik.http.routers.nextcloud.entrypoints=websecure
- traefik.http.routers.nextcloud.tls.certresolver=le
- traefik.http.services.nextcloud.loadbalancer.server.port=80
- traefik.docker.network=proxy
# (optionnel) quelques en-têtes de sécurité
- traefik.http.routers.nextcloud.middlewares=nc-sec
- traefik.http.middlewares.nc-sec.headers.stsSeconds=31536000
- traefik.http.middlewares.nc-sec.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.nc-sec.headers.stsPreload=true
- traefik.http.middlewares.nc-sec.headers.contentTypeNosniff=true
- traefik.http.middlewares.nc-sec.headers.browserXssFilter=true
pgadmin:
image: dpage/pgadmin4:latest
container_name: nextcloud-pgadmin
restart: unless-stopped
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: adminpass
PGADMIN_CONFIG_SERVER_MODE: 'False'
TZ: Europe/Paris
volumes:
- ./pgadmin_data:/var/lib/pgadmin
- ./pgadmin/servers.json:/pgadmin4/servers.json:ro
networks:
- nextcloud-net
networks:
nextcloud-net:
driver: bridge
proxy:
external: true
name: proxy
+45
View File
@@ -0,0 +1,45 @@
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=proxy
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Redirection HTTP -> HTTPS
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
# Let's Encrypt (HTTP-01)
- --certificatesresolvers.le.acme.email=cedric@goutailler-olivier.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
- --entrypoints.ssh.address=:2222
# (Optionnel) Dashboard interne
- --api.dashboard=true
ports:
- "80:80"
- "443:443"
- "2222:2222"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik-letsencrypt:/letsencrypt
networks:
- proxy
environment:
- TZ=Europe/Paris
labels:
- traefik.enable=true
- traefik.http.routers.traefik.rule=Host(`traefik.goutailler-olivier.com`)
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.tls.certresolver=le
- traefik.http.routers.traefik.service=api@internal
networks:
# nextcloud-net:
# driver: bridge
proxy:
external: true
name: proxy
+30
View File
@@ -0,0 +1,30 @@
services:
trilium:
image: triliumnext/trilium:latest
container_name: trilium
restart: unless-stopped
environment:
TRILIUM_DATA_DIR: /home/node/trilium-data
TZ: Europe/Paris
volumes:
- /home/gato/Applications/Trilium/data:/home/node/trilium-data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.trilium.rule=Host(`notes.goutailler-olivier.com`)
- traefik.http.routers.trilium.entrypoints=websecure
- traefik.http.routers.trilium.tls.certresolver=le
- traefik.http.services.trilium.loadbalancer.server.port=8080
- traefik.docker.network=proxy
networks:
proxy:
external: true
name: proxy
+1
View File
@@ -0,0 +1 @@
WATCHTOWER_TOKEN=votre_token_secret_ici
+30
View File
@@ -0,0 +1,30 @@
name: watchtower
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /root/.docker/config.json:/root/.docker/config.json:ro
environment:
WATCHTOWER_HTTP_API_UPDATE: "true"
WATCHTOWER_HTTP_API_TOKEN: ${WATCHTOWER_TOKEN}
WATCHTOWER_LABEL_ENABLE: "true"
WATCHTOWER_CLEANUP: "true"
TZ: Europe/Paris
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.watchtower.rule=Host(`watchtower.goutailler-olivier.com`)
- traefik.http.routers.watchtower.entrypoints=websecure
- traefik.http.routers.watchtower.tls.certresolver=le
- traefik.http.services.watchtower.loadbalancer.server.port=8080
- traefik.docker.network=proxy
networks:
proxy:
external: true
name: proxy