Aller au contenu

SaaS & back-office

Les deux exemples déploient des produits open source existants, avec leurs images ou leurs dépôts officiels : ils s’importent sans rien écrire. Le premier est une stack à compose en ligne (le compose est dans le blueprint), le second une stack Git (le compose est lu dans le dépôt du produit).

Cal.com est une application de planification : pages de réservation, agendas connectés, rappels. L’image officielle calcom/cal.com applique elle-même ses migrations au démarrage. Elle a besoin de DATABASE_URL et de DATABASE_DIRECT_URL ; comme un binding n’injecte qu’une seule variable par add-on, la base PostgreSQL est un conteneur de la stack, avec son volume.

apiVersion: cassiope.eu/v1alpha2
kind: Blueprint
project:
slug: planification
name: Planification
environments:
- slug: production
name: Production
stacks:
- slug: calcom
name: Cal.com
compose: |
services:
calcom:
image: calcom/cal.com:v6.2.0
environment:
NEXT_PUBLIC_WEBAPP_URL: ${NEXT_PUBLIC_WEBAPP_URL}
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
CALENDSO_ENCRYPTION_KEY: ${CALENDSO_ENCRYPTION_KEY}
DATABASE_HOST: database:5432
DATABASE_URL: postgresql://calcom:${POSTGRES_PASSWORD}@database:5432/calendso
DATABASE_DIRECT_URL: postgresql://calcom:${POSTGRES_PASSWORD}@database:5432/calendso
CALCOM_TELEMETRY_DISABLED: "1"
depends_on:
database:
condition: service_healthy
restart: always
database:
image: postgres:16-alpine
environment:
POSTGRES_DB: calendso
POSTGRES_USER: calcom
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- database-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U calcom -d calendso"]
interval: 5s
timeout: 5s
retries: 10
restart: always
volumes:
database-data:
services:
- { name: calcom, cpuMillicores: 1000, ramMb: 2048 }
- { name: database, cpuMillicores: 500, ramMb: 1024 }
volumes:
- { name: database-data, sizeGb: 10 }
exposed:
- { serviceName: calcom, port: 3000 }
env:
- key: NEXT_PUBLIC_WEBAPP_URL
value: ${CASSIOPE_PUBLIC_URL}
- key: NEXTAUTH_SECRET
fromSecret: true
- key: CALENDSO_ENCRYPTION_KEY
fromSecret: true
- key: POSTGRES_PASSWORD
fromSecret: true

Valeurs à fournir à l’import, jamais dans le fichier. Générez-les avec openssl rand -base64 32 (NEXTAUTH_SECRET, CALENDSO_ENCRYPTION_KEY) et openssl rand -hex 24 (POSTGRES_PASSWORD : sans caractère spécial, il entre dans une URL) :

{
"production/calcom": {
"NEXTAUTH_SECRET": "…",
"CALENDSO_ENCRYPTION_KEY": "…",
"POSTGRES_PASSWORD": "…"
}
}

Ce qu’il faut savoir :

  • L’adresse publique est injectée. NEXT_PUBLIC_WEBAPP_URL vaut ${CASSIOPE_PUBLIC_URL} : la plateforme y met l’adresse du conteneur exposé (calcom), ou votre domaine personnalisé une fois vérifié, à chaque déploiement — rien à copier.
  • Le premier démarrage applique les migrations : comptez quelques minutes avant que la page d’inscription réponde. Le premier compte créé devient l’administrateur.
  • CALENDSO_ENCRYPTION_KEY chiffre les identifiants d’agenda en base : la perdre rend ces identifiants illisibles. Conservez-la avec vos sauvegardes.

Twenty est un CRM open source (contacts, entreprises, opportunités). Son dépôt publie un docker-compose.yml officiel — serveur, worker de tâches de fond, PostgreSQL et Redis — que la plateforme lit directement, sans copie, à la version épinglée. Le dépôt est public : aucune connexion GitHub n’est nécessaire.

apiVersion: cassiope.eu/v1alpha2
kind: Blueprint
project:
slug: crm-interne
name: CRM interne
environments:
- slug: production
name: Production
stacks:
- slug: twenty
name: Twenty
gitProvider: github
gitRepo: twentyhq/twenty
gitBranch: twenty/v2.51.0
composeLocation: /packages/twenty-docker/docker-compose.yml
services:
- { name: server, cpuMillicores: 1000, ramMb: 2048 }
- { name: worker, cpuMillicores: 500, ramMb: 1024 }
- { name: db, cpuMillicores: 500, ramMb: 1024 }
- { name: redis, cpuMillicores: 250, ramMb: 512 }
volumes:
- { name: db-data, sizeGb: 10 }
- { name: server-local-data, sizeGb: 5 }
exposed:
- { serviceName: server, port: 3000 }
composeOverride: |
services:
server:
ports: !reset []
deploy:
resources:
limits:
cpus: "1"
memory: "2048M"
healthcheck:
interval: 10s
retries: 20
start_period: 480s
worker:
deploy:
resources:
limits:
cpus: "0.5"
memory: "1024M"
db:
deploy:
resources:
limits:
cpus: "0.5"
memory: "1024M"
redis:
deploy:
resources:
limits:
cpus: "0.25"
memory: "512M"
env:
- key: TAG
value: v2.51.0
- key: SERVER_URL
value: ${CASSIOPE_PUBLIC_URL}
- key: STORAGE_TYPE
value: local
- key: DISABLE_DB_MIGRATIONS
value: "false"
- key: DISABLE_CRON_JOBS_REGISTRATION
value: "false"
- key: STORAGE_S3_REGION
value: ""
- key: STORAGE_S3_NAME
value: ""
- key: STORAGE_S3_ENDPOINT
value: ""
- key: STORAGE_S3_ACCESS_KEY_ID
value: ""
- key: STORAGE_S3_SECRET_ACCESS_KEY
value: ""
- key: FALLBACK_ENCRYPTION_KEY
value: ""
- key: ENCRYPTION_KEY
fromSecret: true
- key: PG_DATABASE_PASSWORD
fromSecret: true

Valeurs à fournir à l’import (openssl rand -base64 32 pour la clé, openssl rand -hex 32 pour le mot de passe de base) :

{
"production/twenty": {
"ENCRYPTION_KEY": "…",
"PG_DATABASE_PASSWORD": "…"
}
}

Ce qu’il faut savoir :

  • Deux versions à garder alignées. gitBranch fixe la version du compose lu dans le dépôt, TAG celle de l’image qu’il démarre : changez-les ensemble.
  • SERVER_URL vaut ${CASSIOPE_PUBLIC_URL}, l’adresse du conteneur server injectée par la plateforme. Les variables STORAGE_S3_* sont vides parce que le stockage est local ; le compose les référence sans valeur de repli, elles doivent donc exister.
  • Le compose du dépôt publie le port 3000 sur la machine : la plateforme retire cette publication et expose le conteneur server à la place.
  • Le premier démarrage dure plusieurs minutes. Le serveur crée le schéma puis applique les migrations avant d’écouter, et le compose du dépôt ne lui laisse qu’environ 100 s. Le bloc composeOverride porte la sonde à 8 minutes de grâce (start_period) ; sans lui, le déploiement échoue en « unhealthy » au milieu des migrations et laisse une base à moitié initialisée que les redéploiements ne réparent pas. Il reprend aussi la publication de port retirée et les plafonds CPU/RAM du blueprint, qu’un override saisi à la main ne génère plus.
  • ENCRYPTION_KEY protège les secrets stockés par Twenty (jetons d’agenda, de messagerie) : gardez-la.
  • Un CRM interne ne doit pas être ouvert à tous : restreignez les inscriptions dans les paramètres de Twenty une fois le premier espace de travail créé.