Helios (Practice Websites)¶
Scope¶
This page covers Helios infrastructure managed from the Aether repo — nginx configs, environment files, monitoring rules, and the aether-side deployment pattern.
For Helios application specifics (content editing, adding a practice workflow, Next.js deploy via Helios's own Makefile), see the Helios repo's own docs:
helios/repo/docs/adding-a-practice.md— full practice onboarding walkthroughhelios/repo/docs/operations.md— content updates, image pipeline, cache invalidation
Architecture¶
Helios is a multi-tenant Next.js frontend serving 9+ dental practice websites from a single codebase. Each practice has its own domain and its own config in Aletheia.
┌──────────────────────────────────────┐
│ nginx (aether) │
│ cabinet-dentaire-aubagne.fr ──┐
│ le-canet.chirurgiens-dentistes.fr ──┤
│ cabinet-bodin.fr ──┼── helios-prod-web:3000
│ dr-david-simon... ──┤
│ ~^[^.]+\.prod\.groupe-suffren\.com$──┤ (any {code}.prod, derived)
│ ... ──┘
└──────────┬───────────────────────────┘
│
▼
┌──────────────────────────┐
│ helios-{env}-web │
│ (Next.js + Node.js) │
└────────────┬─────────────┘
│ REST
▼
┌──────────────────────────┐
│ aletheia-{env}-web │
│ /api/v1/websites │
└──────────────────────────┘
One Next.js container per environment serves all practice sites; routing by Host
header is done in nginx and in the Next.js middleware.
Managed by Aether¶
Nginx vhosts¶
Three base configs (one per environment) in nginx/conf.d/:
helios-prod.conf.full+.conf.temphelios-staging.conf.full+.conf.temphelios-dev.conf.full+.conf.temp
Each prod vhost routes the real .fr practice domains (explicit, auth-off) plus
any {code}.prod.groupe-suffren.com preview host — matched by a single regex
server_name ~^[^.]+\.prod\.groupe-suffren\.com$ (Phase C: preview codes are
derived, never enumerated) — to the same helios-prod-web:3000 upstream. Staging
and dev vhosts are the same regex per env (.staging./.dev.), gated whole-vhost
by basic auth. Distinguishing by practice is done by Helios itself via the Host
header (codeFromHost). Onboarding a preview needs no nginx edit.
Monitored domains¶
SSL certificate monitoring:
https://aletheia.groupe-suffren.comhttps://aletheia-staging.groupe-suffren.comhttps://aletheia-dev.groupe-suffren.comhttps://monitoring.groupe-suffren.comhttps://analytics.groupe-suffren.comhttps://cosmos-staging.groupe-suffren.com
Aletheia backend health monitoring (HealthCheckFailing):
https://aletheia.groupe-suffren.com/health/https://aletheia-staging.groupe-suffren.com/health/https://aletheia-dev.groupe-suffren.com/health/https://aletheia-staging.groupe-suffren.com/api/v1/websites/sites/cda/config/
Helios-specific alerts¶
| Alert | Condition | Source |
|---|---|---|
HeliosContainerRestarting |
> 2 restarts in 15 min | helios.rules.yml |
HeliosHealthCheckFailing |
Practice site probe failing > 5 min | helios.rules.yml |
HeliosSSLExpiringSoon |
Practice domain cert < 14 days | helios.rules.yml |
See runbooks for the response procedures.
Environment files¶
Encrypted env files for Helios live in envs/helios/ in the aether repo
(see environment management for the full list):
envs/helios/.env.dev.encenvs/helios/.env.staging.encenvs/helios/.env.prod.enc
Each contains:
| Variable | Purpose |
|---|---|
ALETHEIA_API_URL |
Internal URL Helios uses to fetch content (e.g., http://aletheia-prod-web:8000/api/v1/websites) |
REVALIDATION_SECRET |
Shared secret for Aletheia → Helios ISR webhook (Next.js cache invalidation) |
NODE_ENV |
Always production (even for dev/staging — this is about Next.js optimization, not environment) |
ENVIRONMENT |
prod, staging, or dev — used for logging/sentry tagging |
Bootstrapping a new Helios install¶
For a brand-new deployment (fresh server or first Helios deploy on an existing server):
Preflight for the cert step: the wildcard is issued via DNS-01, so the certbot container must run the
certbot/dns-ovhimage with/etc/ovh/dns_ovh.inimounted. Runmake decrypt(drops the OVH creds onto the server) thenmake restart-nginx(recreates certbot with the plugin) beforehelios-init.
This target:
- Creates
/opt/docker/helios/{repo,envs}/on the server - Generates
.env.{prod,staging,dev}with freshREVALIDATION_SECRETvalues (skips any env file that already exists — safe to re-run without rotating secrets) - Issues the per-env wildcard cert (
--cert-name helios, DNS-01 via OVH) — one cert file with three SANs*.prod.+*.staging.+*.dev.groupe-suffren.com, covering every{code}.{env}preview host, write-once (Phase C dotted env) - Deploys nginx configs for Helios domains
- Deploys monitoring rules (
helios.rules.yml) - Reloads nginx and Prometheus
After helios-init:
- Copy the printed
REVALIDATION_SECRETvalues into the matching Aletheia env files (HELIOS_REVALIDATION_SECRET) - Clone the Helios repo:
git clone git@github.com:baudry-suffren/helios.git /opt/docker/helios/repo - Preview hosts need no cert work — the per-env wildcard from
helios-init(*.{prod,staging,dev}.groupe-suffren.com) covers them. Real.frpractice-domain certs are separate and minted at cutover (see Adding a New Practice) - Deploy:
cd /opt/docker/helios/repo && make deploy ENV=prod - Run
make encryptin aether to persist the new secrets
Deploying Helios updates¶
Helios-side deploys (code, content, dependencies) happen from the Helios repo:
cd /opt/docker/helios/repo
make deploy ENV=prod # deploys current branch
make deploy ENV=prod REF=v1.2 # deploys a tag
Aether-side deploys only needed when Helios infrastructure changes:
| Change | How to deploy |
|---|---|
| New practice preview onboarded | Nothing in aether (Phase C: host is derived; only the Aletheia SiteConfig row is created) |
New practice .fr cutover (real domain goes live) |
cd aether && make deploy && make restart-nginx (add the .fr server_name + cert + monitoring — see below) |
New alert rule in helios.rules.yml |
cd aether && make deploy (Prometheus auto-reloads) |
| Env variable change | Edit server env file → make restart in helios repo → make encrypt in aether |
New environment added (e.g. helios-qa) |
Create new .env.qa + new nginx config + redeploy |
Adding a New Practice¶
Preview (before cutover) — zero aether changes¶
Phase C: a preview host is {code}.{env}.groupe-suffren.com, derived by nginx (the
per-env regex server_name) and by Helios (codeFromHost), and already covered by
the per-env wildcard cert + wildcard DNS. From aether you do nothing. Onboarding
a preview is a single action in Aletheia: create the SiteConfig row. It is then
live at {code}.{env}.groupe-suffren.com behind the shared basic-auth gate.
Cutover (real .fr goes live) — make helios-golive CODE=… (Phase D)¶
Only when a practice's real domain goes live do aether touchpoints appear. Phase D
turns this into one command that reads the domain from the one source (Aletheia
SiteConfig.domain, never re-typed), issues the cert, and prints the lines to
paste — it does not auto-edit the hand-maintained files.
Pre-flight: the .fr (+ www.) must already resolve to THIS server (HTTP-01,
like cosmos-prod-init), and SiteConfig.domain must be set in the CMS (the
command aborts loudly otherwise, so golive never emits empty/broken config).
It does two things and prints the rest:
- Reads the domain:
manage.py site_domain <CODE>onaletheia-prodreads the model directly (nois_activegate, no auth) so it works mid-cutover. - Issues the per-domain HTTP-01 cert (
--cert-name <domain>,-d <domain> -d www.<domain>) — a separate cert, not theheliosDNS-01 wildcard (the wildcard doesn't cover a.fr, so a real domain served on the preview vhost gets the wrong cert). - Prints (paste by hand): a whole
.frserver block (own cert, auth-off — fromnginx/conf.d/helios-fr.conf.template) to save ashelios-fr-<code>.conf; theserver_name+$helios_preview_realmmap lines to remove fromhelios-prod.conf.full(elseconflicting server name); the 2 prometheus targets (blackbox-ssl+blackbox-helios-frontend); the Heliospractices.tsrealDomain→codeentries. - Reminds you of the two manual gates: set the DNS A-record (external), then flip
SiteConfig.is_active = Trueonce it resolves.
For everything else (SiteConfig in Aletheia, Helios deploy, DNS setup, content creation), see Helios's adding-a-practice guide.
Troubleshooting¶
Practice site returns 502: Helios container is down or network-isolated. Check
docker ps --filter name=helios-prod and docker logs helios-prod-web.
Content changes don't appear on the site: ISR revalidation webhook failed.
Check Aletheia's logs for REVALIDATION_SECRET mismatches, and verify Next.js
is reachable from Aletheia: docker exec aletheia-prod-web curl http://helios-prod-web:3000/api/revalidate.
SSL cert expiring: see SSL Certificate Expiring runbook. Practice domains renew the same way as groupe-suffren.com domains.
New practice added but site 404s: verify the domain resolves, reaches nginx
(curl -v https://newdomain.fr), that nginx has a matching server_name, and
that Helios's Next.js router knows about the practice (Aletheia SiteConfig with
matching domain).