Ce que votre appli doit respecter
Quelques règles suffisent pour qu’une appli tourne sur while. Votre agent les connaît et les applique : cette page les dit aussi pour vous.
Mis à jour le 9 novembre 2026.
Écouter sur le port PORT
Votre appli écoute en HTTP sur le port de la variable PORT : celui de while.yaml, 8080 par défaut. Elle écoute sur toutes les interfaces (0.0.0.0), pas seulement sur localhost.
Répondre sur le chemin de santé
Votre appli répond 2xx ou 3xx sur son chemin de santé (health.path, /health par défaut) dès qu’elle peut servir.
- while l’appelle chaque seconde.
- Une version qui ne répond pas ne reçoit jamais de trafic : while la retire d’elle-même, et la version d’avant reste en ligne.
- Une nouvelle version a 120 secondes pour répondre ; une appli qui migre sa base au démarrage (Metabase, Keycloak) en demande plus avec
health.startup_timeout_seconds, jusqu’à 900.
Passer ses tests à la construction
Les tests du projet tournent pendant la construction (npm test, pytest, go test).
- S’ils échouent, rien n’est mis en ligne.
- Un script de test vide ou absent est sauté.
- Quand plusieurs services partagent les mêmes sources, les tests tournent une fois, et
build: { skip_tests: true }les coupe pour un service. - Avec votre propre Dockerfile, while ne joue que son étage « test ».
Tourner sans privilèges
Votre appli tourne sans privilèges, jamais en root. Un USER numérique qui n’est pas root est respecté, par exemple USER 1001:1001. Un USER nommé (USER node), root ou absent devient 1000:1000, l’utilisateur que déclarent les Dockerfiles générés par while.
Écrire ses journaux sur la sortie standard
Votre appli écrit ses journaux sur la sortie standard et la sortie d’erreur. while les garde, et votre agent les lit avec l’outil get_logs.
Garder ses fichiers au bon endroit
Un worker et une tâche ont un /tmp de 1 Gio, qui ne prend rien sur leur mémoire, et resources.disk le porte jusqu’à 8 Gio. Le /tmp d’un service web, lui, prend sur sa mémoire : ses gros fichiers vont dans un worker, une base ou un bucket. Un /tmp se vide à chaque redémarrage, donc ce qui doit durer va dans une base ou un bucket.
Lire un secret posé en fichier
Un secret posé en fichier se lit dans /run/secrets/<NOM>, jamais dans une variable.
- Il se pose avec
set_envetfile: true, ou avec{ secret: …, file: true }dans while.yaml. - En Node :
(await readFile('/run/secrets/CLE_API', 'utf8')).trim(). - En Python :
Path('/run/secrets/CLE_API').read_text().strip(). - En Go :
os.ReadFile("/run/secrets/CLE_API"), puisstrings.TrimSpace. - Dans un projet lié à une connexion fédérée qui fait valider chaque mise en production, c’est la seule façon de recevoir un secret : les variables y sont montrées en clair à qui valide.
Appeler Internet par une connexion
Internet est fermé à l’appli par défaut : elle ne joint que ses autres services, ses bases, ses caches et while.
- Il l’est aussi avec
egress: none, ou avec une listeallow, qui n’ouvre rien en direct. - Pour appeler une API externe, passez par une connexion : avec
connections: [meteo], l’adresse arrive dansMETEO_URL, la clé reste dans while, et chaque appel est tracé. - Une API sans IPv6 passe alors par la sortie IPv4 de while.
network: { egress: all }ouvre Internet en direct, en IPv6 comme en IPv4, si les règles de l’organisation le permettent, sauf les réseaux internes de la plateforme et le port 25 (SMTP).
Choisir qui ouvre l’appli
Un service est public par défaut.
access: internalle met derrière la connexion de while, et l’appli n’a rien à coder pour l’authentification.- Elle lit l’identité dans
X-While-Email,X-While-NameetX-While-Groups(encodés en pourcentage), ou dansX-While-Identity, un JWT. - while retire tout en-tête
X-While-*venu du client.
Tenir dans ses ressources
Par défaut, un service web a 200 mCPU et 192 Mio de mémoire, et un worker 100 mCPU et 128 Mio ; resources les change.
- Un conteneur demande 8 Gio de mémoire au plus, et jusqu’à 16 Gio si le quota de mémoire de l’organisation le permet.
- Une organisation demande au plus 4 vCPU et 8 Gio de mémoire par défaut, tous ses projets et environnements ensemble ; l’outil
get_usage(vue quotas) donne les siens. - Chaque service y compte pour son nombre maximal d’instances :
scale.max(10 sans réglage), sans dépasser la limite « instances » (3 par défaut). - Un service web sans
scale.maxcompte donc 3 fois ses ressources, puisqu’il monte jusque-là avec le trafic. - Les tâches, les caches et les bases comptent aussi, une base pour 100 mCPU et 256 Mio par instance.
Sortir avec le code 0
Une tâche planifiée (crons) sort avec le code 0 quand tout va bien. Tout autre code est un échec : l’agent le reçoit avec l’erreur résumée et les dernières lignes du journal (outils list_tasks et run_task).
Essayer contre une vraie base
Pour essayer contre une vraie base, inutile d’en monter une sur votre poste.
- Mettez en ligne : la base naît avec l’environnement, et les versions suivantes se construisent en une minute environ.
- Lancez ensuite la tâche (
run_task, avec wait), puis lisez le résultat (query_database). - Un échec donne l’erreur et quoi faire.
- Une prévisualisation permet d’essayer sans toucher la production.
Les variables toujours présentes
Votre appli reçoit toujours ces variables, en plus de celles de ses briques :
| Variables | Ce qu’elles disent |
|---|---|
PORT, HOST | où écouter |
WHILE_PROJECT, WHILE_ENVIRONMENT, WHILE_SERVICE, WHILE_RELEASE | le projet, l’environnement, le service et la version |
WHILE_CRON, WHILE_TRIGGER | dans une tâche : laquelle, et comment ce passage a été lancé (scheduled, manual ou catch_up) |
WHILE_CATCH_UP_START, WHILE_CATCH_UP_END | la période d’un rattrapage |
NODE_ENV | production en production, development ailleurs |
OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME | la télémétrie OpenTelemetry |
SENTRY_DSN | les erreurs, avec n’importe quel SDK Sentry côté serveur (une adresse interne, joignable sans ouvrir les sorties réseau) |
SENTRY_DSN_PUBLIC | les erreurs, avec un SDK Sentry dans le navigateur |
DATABASE_URL, REDIS_URL… | celles des briques déclarées, et celles posées avec set_env |