---
title: "Ce que votre appli doit respecter pour tourner sur while.sh"
url: https://while.sh/fr/docs/demarrer/contrat/
updated: 2026-11-09
---

# 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.

## É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_env` et `file: 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")`, puis `strings.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 liste `allow`, qui n'ouvre rien en direct.
- Pour appeler une API externe, passez par une connexion : avec `connections: [meteo]`, l'adresse arrive dans `METEO_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: internal` le 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-Name` et `X-While-Groups` (encodés en pourcentage), ou dans `X-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.max` compte 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 |
