while.yaml
while.yaml, à la racine de votre appli, dit ce dont elle a besoin. Votre agent l’écrit, et cette page en donne chaque champ.
Mis à jour le 9 novembre 2026.
apiVersion: while/v1
project: planning
services:
- name: web
port: 3000
health: { path: /health }
databases:
- name: main
Ce fichier décrit un projet : ses services, ses bases, ses caches, son stockage, ses files, ses tâches planifiées, ses domaines, ses environnements. while en fait l’infrastructure. Un projet sans service web est permis, s’il déclare au moins une base ou une tâche planifiée.
Le fichier
| Champ | Type | Ce qu’il fait |
|---|---|---|
apiVersion (obligatoire) | toujours while/v1 | La version du format : toujours while/v1. |
project (obligatoire) | texte (minuscules) | L’identifiant du projet, en minuscules. |
services | liste de services | Les services de l’appli. Ils sont facultatifs quand le projet déclare au moins une base ou une tâche planifiée : un projet de données n’a pas toujours de service web. |
databases | liste de bases, ou de bases d’autres projets | Les bases PostgreSQL du projet, ou les bases d’autres projets qu’il lit. Quatre au plus. |
caches | liste de caches | Les caches Valkey du projet, qui parlent le protocole Redis. Quatre au plus. |
buckets | liste de buckets | Les buckets S3 du projet, pour ses fichiers. Quatre au plus. |
crons | liste de tâches planifiées | Les tâches planifiées : une commande lancée à heure fixe, dans l’image d’un service, ou construite pour elle quand le projet n’a pas de service. Chaque passage est suivi (historique, échecs, passages manqués), et se relance ou se rattrape à la demande. |
environments | objet | Les réglages propres à un environnement. La clé est le nom de l’environnement : production, preview… |
telemetry | objet | Ce que while garde de l’appli : ses journaux, ses métriques, ses traces et ses erreurs. |
telemetry.logs | true ou false | true garde la sortie de l’appli dans ses journaux. Par défaut : true. |
telemetry.metrics | true ou false | true compte les requêtes, les erreurs et les temps de réponse de l’appli. Par défaut : true. |
telemetry.traces | true ou false | true garde les traces OpenTelemetry de l’appli. Par défaut : false. |
telemetry.errors | true ou false | true donne à l’appli un point d’entrée compatible Sentry, dont while fournit l’adresse (DSN). Par défaut : true. |
telemetry.uptime | objet | Une sonde de disponibilité : un chemin de l’appli, appelé à intervalle régulier. |
network | objet | Les sorties réseau de l’appli. Internet lui est fermé par défaut (egress: restricted) : une API externe se déclare en connexion (declare_connection), while ajoute sa clé et passe en IPv4 s’il le faut, et l’appli l’appelle par <NOM>_URL. egress: all ouvre Internet en direct, après la validation d’une personne. Un changement vaut à la mise en ligne suivante. |
network.egress | none, restricted, all | none ferme tout accès à Internet. restricted (défaut) ferme Internet, sauf les connexions déclarées et la liste allow, qui passent par la sortie de while. all ouvre Internet en direct, en IPv6 comme en IPv4, après validation. Par défaut : restricted. |
network.allow | liste de textes | Les noms d’hôtes permis quand egress vaut restricted. Ils sont appelés par la sortie de while, jamais en direct. Pour une API qui demande une clé, préférez une connexion. |
Les services
Chaque entrée de services décrit un service : un site, une API, un worker ou un site statique.
| Champ | Type | Ce qu’il fait |
|---|---|---|
name (obligatoire) | texte (minuscules) | Le nom du service, en minuscules. Il entre dans son adresse et dans SERVICE_<NOM>_URL. |
description | texte | Une ligne pour qui ouvre le service, sous son titre dans la page de ses applis (« Les ventes du jour, par magasin ») : 140 caractères au plus, sans rien de technique. Sans elle, la page montre l’adresse. |
type | web, worker, static | web : un service HTTP, avec son adresse. worker : un processus sans adresse ni sonde HTTP, comme une file de travaux ou un consommateur. static : un site statique, servi avec un cache HTTP. Par défaut : web. |
root | texte | Pour un site statique : le dossier à servir une fois le site construit (dist, build, public…). Par défaut : .. |
source | texte | Le chemin des sources dans l’archive envoyée, ou dans le dépôt git. Par défaut : .. |
image | texte | Une image déjà construite, dans un registre externe ou dans celui de while, de préférence avec son digest. |
dockerfile | texte | Le chemin du Dockerfile. Sans lui, while reconnaît le langage et écrit le Dockerfile. |
build | objet | La construction de l’image du service. |
build.context | texte | Le dossier de contexte de la construction. Par défaut : .. |
build.target | texte | L’étage du Dockerfile à construire (—target). |
build.args | objet | Les arguments de construction (—build-arg), non secrets : ils restent dans l’historique et la provenance de l’image. Un nom qui ressemble à un secret (TOKEN, KEY, SECRET, PASSWORD) est refusé : passez par secrets. |
build.secrets | liste de textes | Les noms des secrets de construction, comme NPM_TOKEN. Leur valeur se pose à part, avec set_env { build: true } ou la case « pour la construction » des réglages, jamais ici. Ils sont montés le temps d’un RUN dans /run/secrets/<NOM>, jamais dans l’image, et masqués au journal. Une construction ne lit jamais les secrets de l’appli, et l’appli ne reçoit pas ceux de la construction. |
build.test | texte | La commande des tests, jouée dans le même bac à sable que la construction. Par défaut, celle du langage reconnu. |
build.skip_tests | true ou false | true ne joue pas les tests à la construction de ce service. Par défaut : false. |
command | liste de textes | La commande qui démarre le service, à la place de celle de l’image, par exemple ["node", "server.js"]. |
port | nombre entier | Le port sur lequel le service écoute. while le donne à l’appli dans la variable PORT. Par défaut : 8080. De 1 à 65535. |
public | true ou false | true expose le service sur un domaine public. Par défaut : true. |
access | public, internal | public ouvre le service à tout Internet. interne le met derrière la connexion de while, ou celle de l’entreprise : l’appli reçoit l’identité dans X-While-Email, X-While-Name, X-While-Groups et X-While-Identity. Sans groupes, emails ni email_domains, les membres de l’organisation entrent. Rendre publique une appli interne, ou changer qui y entre, demande une validation. Par défaut : public. |
emails | liste de textes | Pour l’accès interne : les adresses qui entrent, membres de l’organisation ou non. |
domains | liste de textes | Les domaines personnalisés du service. while lui donne toujours aussi son adresse par défaut. |
health | objet | La sonde de santé : while appelle ce chemin chaque seconde, et une version ne reçoit du trafic qu’une fois qu’il répond 2xx ou 3xx. |
health.path | texte | Un chemin de l’appli. Il commence par un seul « / », sans adresse complète, sans « // », sans contre-oblique ni espace. Par défaut : /health. |
health.initial_delay_seconds | nombre entier | Le délai avant le premier appel de la sonde, en secondes. Par défaut : 0. |
health.timeout_seconds | nombre entier | Le temps que la sonde attend une réponse, en secondes. Par défaut : 3. |
health.startup_timeout_seconds | nombre entier | Le temps qu’une nouvelle version a pour répondre à sa sonde, en secondes, avant d’être refusée (la version en ligne garde le trafic). 120 par défaut, 900 au plus : pour une appli qui migre sa base au démarrage (Metabase, Keycloak). Une version vraiment cassée met alors ce temps à être dite en échec. Par défaut : 120. De 30 à 900. |
env | objet | Les variables d’environnement. Une valeur { secret: chemin } est lue dans le coffre de while. |
resources | objet | Le processeur et la mémoire réservés pour chaque instance du service. |
resources.cpu | processeur (500m, 1) | Une quantité de processeur : 500m pour un demi-vCPU, 1 pour un vCPU. Par défaut : 200m. |
resources.memory | mémoire (256Mi, 1Gi) | Une quantité de mémoire : 256Mi, 1Gi. Par défaut : 192Mi. |
resources.disk | disque (1Gi) | Pour un worker ou une tâche : le disque de /tmp, sur le disque du nœud et pas en mémoire, qui est aussi la limite de stockage du conteneur. 1 Gio par défaut, 8 Gio au plus. Sans effet sur un service web, dont le /tmp est pris sur sa mémoire. Par défaut : 1Gi. |
volume | volume | Un disque qui survit aux mises en ligne, aux redémarrages et à la perte d’un nœud, pour un worker ou une tâche, monté par une instance à la fois. Retiré du while.yaml, il est gardé 7 jours, puis détruit. |
scale | objet | Le nombre d’instances du service, entre min et max. |
scale.min | nombre entier | Le nombre d’instances gardées tant que le service ne dort pas (voir veille). 0 est l’ancienne façon de dire sleep: 30m : préférez veille. Par défaut : 1. De 0 à 5. |
scale.max | nombre entier | Le plafond d’instances quand le trafic monte : 10 au plus si scale.max n’est pas déclaré. Par défaut : 1. De 1 à 5. |
uses | liste de textes | Les bases, caches, stockages ou files dont se sert ce service. Par défaut, tout ce que déclare le projet. |
title | texte | Le titre que voient les personnes, écrit du point de vue de qui s’en sert (« Lecture des ventes », « Import des commandes, chaque heure ») : 80 caractères au plus, sur une ligne, sans rien de technique. Sans titre, les écrans montrent le nom. |
sleep | délai (30m, 2h) ou never | La mise en veille d’un service web ou d’un site statique. Un délai (30m, 2h) l’endort après ce temps sans requête : la requête suivante le réveille en quelques secondes, et attend sa réponse. non (ou false) lui garde toujours au moins une instance. Par défaut : non en production, 30m dans les autres environnements, comme les prévisualisations. Un service qui dort repart d’une instance au réveil : scale.min ne compte que s’il ne dort pas. Un worker ne dort jamais, une base de production non plus. |
groups | liste de textes | Pour l’accès interne : les groupes de l’organisation qui entrent, faits dans while ou venus de la connexion d’entreprise. |
members | true ou false | Pour l’accès interne avec groupes, emails ou email_domains : true laisse entrer aussi les membres de l’organisation. Sans ce champ, seules les listes entrent. Sans listes, les membres entrent déjà. |
email_domains | liste de textes | Pour l’accès interne : les domaines d’adresse qui entrent. Un domaine vaut exactement : nextmap.io n’ouvre pas fr.nextmap.io. |
connections | liste de noms de connexions | Les connexions de l’organisation, des API externes déclarées dans while, qu’appelle ce service ou cette tâche. Pour « meteo », l’appli reçoit METEO_URL, l’adresse du relais, jeton compris. La clé reste dans while, et chaque appel est tracé. |
Les bases
Chaque entrée de databases déclare une base PostgreSQL. Une entrée avec from lit la base d’un autre projet, sans rien créer.
| Champ | Type | Ce qu’il fait |
|---|---|---|
name (obligatoire) | texte (minuscules) | Le nom de la base, en minuscules. Il entre dans ses variables, comme DB_<NAME>_URL. |
engine | postgres | Le moteur de la base : postgres, le seul. Par défaut : postgres. |
version | texte | La version de PostgreSQL : 16, 17 ou 18. Par défaut : 17. |
size | mémoire (256Mi, 1Gi) | Le disque de la base, facturé au Gio par mois. Par défaut : 1Gi. |
compute | S, M, L, XL | La taille d’une instance : le processeur et la mémoire réservés, avec les réglages de PostgreSQL et du pooler qui suivent. S : 0,1 vCPU et 256 Mo (jusqu’à 1 Go). M : 0,5 vCPU et 1 Go. L : 1 vCPU et 4 Go. XL : 2 vCPU et 8 Go. Un changement de taille redémarre la base. Ne la confondez pas avec size, le disque. Par défaut : S. |
pool | transaction, session | Le mode du pooler (PgBouncer) devant la base. transaction, le défaut : chaque transaction peut prendre une autre connexion au serveur ; beaucoup de clients pour peu de connexions, mais ce qui tient à une session ne dure que sa transaction. session : une connexion au serveur par client connecté, gardée tant qu’il reste connecté ; le pooler n’accepte pas plus de clients que la taille de la base n’a de connexions (S 20, M 60, L 100, XL 140), le client de trop est refusé. Choisissez session pour une appli qui a besoin de sa session : les migrations Liquibase (Metabase, Keycloak) ou Flyway, qui prennent un verrou consultatif de session, un verrou consultatif de session posé par l’appli, LISTEN/NOTIFY, une table temporaire. Par défaut : transaction. |
instances | 1, 2 ou 3 | Le nombre d’instances PostgreSQL de la base. Avec 1, une seule instance tourne sur le disque d’un nœud, et une panne du nœud se répare par une restauration. Avec 2 ou 3, la base est en haute disponibilité : une ou deux instances de secours sont tenues à jour en continu, et prennent le relais d’elles-mêmes si la principale tombe. Chaque instance a son propre disque (size). Ne le confondez pas avec replica, une réplique en lecture pour query_database. |
replica | true ou false | Prévue : une réplique en lecture, tenue à jour en continu, que lira l’outil query_database des agents. Le champ est accepté dès maintenant. Tant que la réplique n’est pas servie, query_database lit la base principale, dans une transaction en lecture seule, et le dit dans sa réponse. Par défaut : false. |
backup | objet | Les sauvegardes de la base. |
backup.schedule | texte | L’heure de la sauvegarde complète du jour, au format cron, en UTC. Par défaut : 0 3 * * *. |
backup.retention_days | nombre entier | Lu mais pas suivi : while garde les sauvegardes 14 jours, pour toutes les bases. Par défaut : 7. |
backup.test_restore | true ou false | while rejoue la restauration de chaque sauvegarde, et affiche le résultat. Par défaut : true. |
Lire la base d’un autre projet
La base d’un autre projet de la même organisation, que lisent les applis de ce projet : rien n’est créé. La première mise en ligne qui la déclare demande la validation des approbateurs de cette base. Une fois validée, chaque appli lectrice reçoit DB_<NAME>_URL, DB_<NAME>_USER et DB_<NAME>_PASSWORD, un accès en lecture seule que les approbateurs peuvent restreindre à des schémas, des tables ou des colonnes. La base d’une autre organisation est refusée.
| Champ | Type | Ce qu’il fait |
|---|---|---|
name | texte (minuscules) | Son nom dans ce projet, qui fait la variable DB_<NAME>_URL. Par défaut, <projet>-<base> (DB_ENTREPOT_MAIN_URL). |
from (obligatoire) | texte | <projet>/<base> : un projet de la même organisation, et le nom de sa base. |
access | read | lecture : la seule valeur, la lecture seule. Par défaut : read. |
environment | texte | L’environnement de la base source. Par défaut : production. |
readers | liste de textes | Les applis qui la lisent : un service, ou task:<nom>. Sans ce champ, toutes celles du projet. |
Les caches
Chaque entrée de caches déclare un cache Valkey, qui parle le protocole Redis.
| Champ | Type | Ce qu’il fait |
|---|---|---|
name (obligatoire) | texte (minuscules) | Le nom du cache, en minuscules. Il entre dans ses variables, comme CACHE_<NOM>_URL. |
engine | valkey | Le moteur du cache : valkey, le seul. Par défaut : valkey. |
memory | mémoire (256Mi, 1Gi) | La mémoire réservée pour le cache. Par défaut : 64Mi. |
durable | true ou false | true garde les données sur disque (journal AOF), sans jamais les évincer : pour une file de tâches (BullMQ, Sidekiq, Celery). Sans lui, un redémarrage vide le cache. Par défaut : false. |
Les buckets
Chaque entrée de buckets déclare un bucket S3.
| Champ | Type | Ce qu’il fait |
|---|---|---|
name (obligatoire) | texte (minuscules) | Le nom du bucket, en minuscules. Il entre dans sa variable, comme S3_BUCKET_<NOM>. |
public | true ou false | true ouvre la lecture des objets à tous, sans clé, après une validation. Par défaut : false. |
cors | liste de textes | Les origines de plus d’où un navigateur ouvre une URL présignée de ce bucket (https://hôte[:port]). Celles de l’appli y sont déjà. |
Les tâches planifiées
Les tâches planifiées : une commande lancée à heure fixe, dans l’image d’un service, ou construite pour elle quand le projet n’a pas de service. Chaque passage est suivi (historique, échecs, passages manqués), et se relance ou se rattrape à la demande.
| Champ | Type | Ce qu’il fait |
|---|---|---|
name (obligatoire) | texte (minuscules) | Le nom de la tâche, en minuscules. |
schedule (obligatoire) | texte | Le rythme au format cron, en 5 champs, dans le fuseau de timezone (Europe/Paris par défaut) : « 0 3 * * * » lance la tâche chaque jour à 3 h. |
command (obligatoire) | liste de textes | La commande à lancer, par exemple ["node", "cron.js"]. |
service | texte (minuscules) | Le service dont la tâche reprend l’image et les variables : le premier service par défaut. Sans service déclaré, ou avec image, source, dockerfile ou build, la tâche est construite pour elle-même. |
image | texte | Une image déjà construite pour une tâche sans service, de préférence avec son digest. |
source | texte | Pour une tâche sans service : le chemin des sources dans l’archive envoyée, ou dans le dépôt git. Par défaut : .. |
dockerfile | texte | Pour une tâche sans service : le chemin du Dockerfile. Sans lui, while reconnaît le langage. |
build | objet | La construction de l’image du service. |
build.context | texte | Le dossier de contexte de la construction. Par défaut : .. |
build.target | texte | L’étage du Dockerfile à construire (—target). |
build.args | objet | Les arguments de construction (—build-arg), non secrets : ils restent dans l’historique et la provenance de l’image. Un nom qui ressemble à un secret (TOKEN, KEY, SECRET, PASSWORD) est refusé : passez par secrets. |
build.secrets | liste de textes | Les noms des secrets de construction, comme NPM_TOKEN. Leur valeur se pose à part, avec set_env { build: true } ou la case « pour la construction » des réglages, jamais ici. Ils sont montés le temps d’un RUN dans /run/secrets/<NOM>, jamais dans l’image, et masqués au journal. Une construction ne lit jamais les secrets de l’appli, et l’appli ne reçoit pas ceux de la construction. |
build.test | texte | La commande des tests, jouée dans le même bac à sable que la construction. Par défaut, celle du langage reconnu. |
build.skip_tests | true ou false | true ne joue pas les tests à la construction de ce service. Par défaut : false. |
env | objet | Les variables d’environnement. Une valeur { secret: chemin } est lue dans le coffre de while. |
uses | liste de textes | Pour une tâche sans service : les bases, caches, stockages ou files dont elle se sert. Par défaut, tout ce que déclare le projet. |
resources | objet | Par défaut, celles du service repris, et 100 mCPU et 128 Mio pour une tâche sans service. |
resources.cpu | processeur (500m, 1) | Une quantité de processeur : 500m pour un demi-vCPU, 1 pour un vCPU. |
resources.memory | mémoire (256Mi, 1Gi) | Une quantité de mémoire : 256Mi, 1Gi. |
resources.disk | disque (1Gi) | Pour un worker ou une tâche : le disque de /tmp, sur le disque du nœud et pas en mémoire, qui est aussi la limite de stockage du conteneur. 1 Gio par défaut, 8 Gio au plus. Sans effet sur un service web, dont le /tmp est pris sur sa mémoire. |
volume | volume | Un disque qui survit aux mises en ligne, aux redémarrages et à la perte d’un nœud, pour un worker ou une tâche, monté par une instance à la fois. Retiré du while.yaml, il est gardé 7 jours, puis détruit. |
timeout_seconds | nombre entier | La durée maximale d’un passage. Au-delà, il est arrêté et compté « délai dépassé ». Par défaut : 600. De 10 à 21600. |
retries | nombre entier | Le nombre de nouvelles tentatives après un échec, dans le même passage. Par défaut : 0. De 0 à 5. |
concurrency | forbid, allow, replace | forbid empêche deux passages en même temps, et refuse un passage manuel tant qu’un autre tourne. allow permet le chevauchement. replace fait arrêter le passage en cours par le nouveau. Par défaut : forbid. |
timezone | texte | Le fuseau horaire du rythme, en nom IANA : Europe/Paris, UTC… Par défaut : Europe/Paris. |
title | texte | Le titre que voient les personnes, écrit du point de vue de qui s’en sert (« Lecture des ventes », « Import des commandes, chaque heure ») : 80 caractères au plus, sur une ligne, sans rien de technique. Sans titre, les écrans montrent le nom. |
connections | liste de noms de connexions | Les connexions de l’organisation, des API externes déclarées dans while, qu’appelle ce service ou cette tâche. Pour « meteo », l’appli reçoit METEO_URL, l’adresse du relais, jeton compris. La clé reste dans while, et chaque appel est tracé. |
Les environnements
Les réglages propres à un environnement. La clé est le nom de l’environnement : production, preview…
| Champ | Type | Ce qu’il fait |
|---|---|---|
from | texte (minuscules) | L’environnement de référence dont celui-ci hérite. |
services | liste de objets | Les réglages des services dans cet environnement. |
databases | liste de objets | Les réglages des bases dans cet environnement. |
ttl_hours | nombre entier | La durée de vie avant la destruction automatique, pour un environnement de prévisualisation. De 1 à 720. |
Le schéma complet
Le schéma JSON de while.yaml est public, à https://app.while.sh/schema/while.v1.json. Votre agent le lit pour vérifier son fichier avant de mettre en ligne.