Méthode de déploiement de nao sur Scalingo via buildpacks (Scalingo ne déploie pas d'image Docker). Chaque produit a sa propre instance : une app, une base PostgreSQL, un repo de contexte et une clé LLM dédiés → données isolées.
nao est un monorepo polyglotte. Sur Scalingo on reproduit le build Docker amont avec un
multi-buildpack et on lance deux process dans le conteneur web :
- backend (Bun) :
bun run apps/backend/src/cli.ts serve --port $PORT— sert aussi le frontend (build Vite) en statique. - sidecar FastAPI (Python/uvicorn) sur
127.0.0.1:8005— le backend l'appelle enlocalhost(codé en dur amont).
Les deux sont co-localisés (pas de réseau privé) car le backend appelle FastAPI en localhost ; les
séparer imposerait de patcher le code amont (execute-sql.ts, live-story.ts).
| Fichier | Rôle |
|---|---|
.buildpacks |
apt → python → bun (le dernier fixe le start) |
Aptfile |
paquets système (git, chromium, libpq, openssh, deps build Python) |
.python-version |
Python 3.12 |
requirements.txt |
uv (déclenche la détection du python-buildpack) |
.slugignore |
allège le slug (<2 GiB) |
Procfile |
release (migrations) + web (sidecar + backend) |
bin/build.sh |
hook heroku-postbuild → build Python + JS |
bin/build-python.sh |
uv pip install '.[all]' (FastAPI, ibis, providers LLM dont mistral) |
bin/build-js.sh |
bun install + vite build du frontend |
bin/web.sh |
clone du contexte git + sidecar + backend sur $PORT |
bin/release.sh |
migrations Drizzle (gating déploiement) |
scalingo.json |
manifeste addons + env (review apps / one-click) |
nao-scalingo.sh |
gère une instance : create / deploy / logs / env / scale / destroy… |
instances/<produit>/ |
(optionnel) fichiers qui écrasent la base pour une instance (ex. Procfile) |
package.json |
ajout du script heroku-postbuild (seule modif d'un fichier amont) |
- CLI Scalingo installée et authentifiée :
scalingo login. - Accès région
osc-secnum-fr1(SecNumCloud) activé par le support Scalingo (entité EU requise). - Le repo de déploiement (ce fork) poussé sur GitHub (
incubateur-territoires/nao-scalingo).
Toutes les opérations passent par ./nao-scalingo.sh <commande> <produit> (voir ./nao-scalingo.sh help).
Créer une instance (provisionne app + PostgreSQL + env + 1er déploiement) :
NAO_CONTEXT_GIT_URL=https://github.com/incubateur-territoires/contexte-<produit>.git \
ANTHROPIC_API_KEY=sk-ant-... \
./nao-scalingo.sh create <produit>Variables reconnues : NAO_CONTEXT_GIT_URL (requis), ANTHROPIC_API_KEY/MISTRAL_API_KEY/OPENAI_API_KEY,
NAO_CONTEXT_GIT_BRANCH, NAO_CONTEXT_GIT_SUBPATH, NAO_CONTEXT_GIT_TOKEN (repo privé),
DB_URI (DB externe → implique --no-pg),
SCALINGO_REGION (déf. osc-secnum-fr1), PG_PLAN, WEB_SIZE.
create est idempotent : Scalingo est la source de vérité des variables d'env, donc relancer
create <produit> relit les valeurs déjà posées (inutile de repasser NAO_CONTEXT_GIT_URL) et ne
régénère jamais BETTER_AUTH_SECRET.
Opérations courantes :
./nao-scalingo.sh deploy <produit> # redéploie le HEAD courant (ne touche PAS à l'env)
./nao-scalingo.sh logs <produit> # suit les logs
./nao-scalingo.sh set-key <produit> # pose ANTHROPIC_API_KEY=... lu dans l'env
./nao-scalingo.sh set-env <produit> K=V # définit une variable d'env
./nao-scalingo.sh restart <produit>
./nao-scalingo.sh scale <produit> XL
./nao-scalingo.sh destroy <produit> # détruit l'app + base (confirmation)Pour personnaliser une seule instance sans toucher la base commune, dépose des fichiers dans
instances/<produit>/ : au déploiement, ils écrasent les fichiers de la racine (Procfile,
Aptfile, .buildpacks, bin/…).
Exemple — base SOURCE derrière un bastion SSH via un tunnel (cf. instances/inclusion-numerique-prod/) :
-
instances/<produit>/.buildpacksajoute le ssh-private-key-buildpack Scalingo en premier :https://github.com/Scalingo/ssh-private-key-buildpack.git https://github.com/Scalingo/apt-buildpack.git https://github.com/Scalingo/python-buildpack.git https://github.com/incubateur-territoires/scalingo-buildpack-bun -
instances/<produit>/Procfileouvre le tunnel avant le serveur (le buildpack a installé la clé dans~/.ssh, donc pas de-i) :release: bash bin/release.sh web: ssh -N -f -L $DS_BDD_IP $DS_BASTION_IP -p $DS_BASTION_PORT && bash bin/web.sh
Les secrets restent en variables d'env Scalingo (./nao-scalingo.sh set-env <produit> ...) :
SSH_KEY (clé privée en base64, lue par le buildpack), SSH_HOSTS (hôtes ajoutés à
known_hosts, dont le bastion), DS_BDD_IP (spec -L, ex. 5432:<hote-db>:5432), DS_BASTION_IP
(<user>@<bastion>), DS_BASTION_PORT, et NAO_DB_URL=postgres://…@127.0.0.1:<port_local>/…
(la base source vue à travers le tunnel). La base interne de nao reste l'addon PostgreSQL
Scalingo (DB_URI auto-dérivé) — on garde l'addon (pas de --no-pg).
DB_URI: non défini manuellement —bin/web.shle dérive deSCALINGO_POSTGRESQL_URL(auto-roté).BETTER_AUTH_SECRET: secret de session, stable (sinon déconnexions au restart).BETTER_AUTH_URL: URL publique de l'app.- LLM :
ANTHROPIC_API_KEY(test initial) ouMISTRAL_API_KEY(Mistral est supporté nativement, cf.cli/.[all]). Le provider effectif se choisit dans lenao_config.yamldu repo de contexte.
Tous les ajouts sont des fichiers neufs ; seule modif amont = le script heroku-postbuild dans
package.json. La synchro se fait via le bouton « Sync fork » de GitHub sur main. En cas de conflit,
il ne portera que sur package.json (trivial à résoudre).
- Taille du slug (limite 2 GiB) : chromium + node_modules + site-packages Python. Si dépassement,
retirer
chromium/chromium-driverde l'Aptfile(à valider selon les fonctions utilisées). - Buildpack Bun (
jakeg/heroku-buildpack-bun, communautaire) : vérifier qu'il joue bienheroku-postbuildet fournitbunau runtime. Au besoin épingler un commit (URL#<sha>) ou testerconfact/bun-buildpack. - Install Python via uv : confirmer que
apps.backend.fastapi.main:apps'importe au runtime (paquets dans le bon interpréteur). Voirscalingo --app <app> run python -c "import fastapi". - Boxlite/KVM :
@boxlite-ai/boxliteattend/dev/kvm(absent) ; validé que l'exécution SQL passe par le sidecar FastAPI. vite buildsous Bun : si un plugin exige le binairenode, ajouterScalingo/nodejs-buildpack.gitavant bun dans.buildpacks.