Semplifica il deploy: un solo docker-compose senza anchor YAML

I due file compose andavano tenuti allineati a mano, e gli anchor
(x-app-env, <<:) sono sintassi che va conosciuta per riuscire a leggere il
file. Per un deploy che deve restare semplice era complessita' non
giustificata.

Ora c'e' un solo docker-compose.yml, esplicito, che copre produzione e
sviluppo:

- ogni servizio dichiara quello che usa, senza merge key
- mailpit e' dietro il profilo "dev", quindi non parte mai in produzione
- la porta e' legata a 127.0.0.1 per default: in produzione il traffico
  arriva da Traefik sulla rete interna, in locale basta localhost:8080.
  Per esporla davvero: APP_PORT=0.0.0.0:8080
- .env diventa l'unica fonte di configurazione, senza valori ridichiarati

Compromesso accettato: sparendo i valori fissati nel compose, DB_HOST e
REDIS_HOST devono contenere i nomi dei servizi (mariadb, redis) e non un
indirizzo. E' segnalato in .env.example, nel README e in HANDOFF.md.

Per le modifiche al codice senza ricostruire l'immagine il README documenta
un docker-compose.override.yml, che Compose applica da solo ed e' gia'
ignorato da git.

Verificato: sei servizi healthy, login e pagine a 200, redeploy idempotente,
porta esposta solo su localhost, mailpit assente senza il profilo dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 17:41:48 +02:00
parent 0502fad996
commit afaaeb5927
5 changed files with 232 additions and 318 deletions

View File

@@ -19,7 +19,7 @@ redeploy, login, tutte le pagine, code e scheduler.
| Deploy Dokploy (Compose) | ✅ verificato |
| Primo avvio a zero-config | ✅ migrazioni + seed + admin automatici |
| Redeploy senza interventi | ✅ ~25 secondi |
| Sviluppo locale | ✅ `docker-compose.dev.yml` |
| Sviluppo locale | ✅ stesso compose, profilo `dev` |
| Test automatici | ❌ **assenti** — vedi [Cosa resta aperto](#cosa-resta-aperto) |
---
@@ -62,16 +62,22 @@ muore definitivamente — altrimenti resterebbe "up" senza servire nulla.
### La configurazione arriva dall'ambiente, non da `.env` riscritto
Il vecchio entrypoint riscriveva `.env` con `sed` a ogni avvio. Ora Laravel
legge direttamente l'ambiente di processo, e `docker-compose.yml` usa
`env_file` senza ridichiarare le variabili.
legge direttamente l'ambiente di processo, e `docker-compose.yml` si limita a
`env_file: .env` senza ridichiarare nulla.
Fissati nel compose solo i valori che sono una proprietà *della topologia*
(`DB_HOST: mariadb`, `CACHE_STORE: redis`, `LOG_CHANNEL: stderr`, …). Questi
**vincono** su quanto scritto nel `.env`, così un file copiato da una macchina
di sviluppo non rompe il container.
Il `.env` è quindi l'**unica** fonte di configurazione. Una versione precedente
fissava nel compose i valori dettati dalla topologia (`DB_HOST: mariadb` e
simili) perché vincessero su un `.env` sbagliato, ma richiedeva gli anchor YAML
(`x-app-env`, `<<:`) ed è stata scartata a favore della leggibilità.
Nota: perché questo funzioni, `www.conf` ha `clear_env = no`. Senza, php-fpm
sanifica l'ambiente e Laravel non vedrebbe nessuna variabile.
Il rovescio della medaglia: `DB_HOST` e `REDIS_HOST` devono valere `mariadb` e
`redis`, cioè i nomi dei servizi. Se qualcuno vi mette un indirizzo IP il
container non trova più il database. È documentato in `.env.example` e nel
README, ma è il punto fragile di questa scelta.
Nota: perché la configurazione da ambiente funzioni, `www.conf` ha
`clear_env = no`. Senza, php-fpm sanifica l'ambiente e Laravel non vedrebbe
nessuna variabile.
### Fail-fast sulle variabili mancanti
@@ -191,41 +197,42 @@ riavviare il container: `vendor/` e `public/build` vivono dentro l'immagine.
Dopo una modifica sostanziale, questo giro copre i percorsi critici:
Si usa il compose di **sviluppo**: quello di produzione richiede la rete
esterna `dokploy-network` e non pubblica porte sull'host, quindi non è
verificabile in locale così com'è.
C'è un solo `docker-compose.yml`, usato sia in produzione sia in locale. In
locale serve creare una volta la rete esterna attesa da Dokploy.
```bash
cp .env.example .env # se non c'è già
cp .env.example .env
# nel .env: APP_URL=http://localhost:8080, APP_ENV=local,
# SESSION_SECURE_COOKIE=false, e le tre password
docker network create dokploy-network
# 1. L'immagine builda
docker build -f docker/php/Dockerfile -t termanager2-check .
# 2. Stack pulito da zero
docker compose -f docker-compose.dev.yml down -v
docker compose -f docker-compose.dev.yml up -d --build
docker compose -f docker-compose.dev.yml ps # tutti i servizi healthy
docker compose --profile dev down -v
docker compose --profile dev up -d --build
docker compose --profile dev ps # tutti i servizi healthy
# 3. L'applicazione risponde
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/up # 200
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/login # 200
# 4. Il redeploy è idempotente
docker compose -f docker-compose.dev.yml up -d --force-recreate app
docker compose -f docker-compose.dev.yml logs app | grep -E "Migrations|Seed"
docker compose up -d --force-recreate app
docker compose logs app | grep -E "Migrations|Seed"
# atteso: "Migrations applied" + "Seed already applied"
# 5. La cifratura funziona
docker compose -f docker-compose.dev.yml exec app php artisan tinker --execute="
docker compose exec app php artisan tinker --execute="
\$p = App\Models\Proclamatore::create(['nome'=>'Test','cognome'=>'Verifica','attivo'=>true]);
\$raw = DB::table('proclamatori')->where('id',\$p->id)->value('nome');
echo \$raw === 'Test' ? 'ERRORE: dati in chiaro' : 'OK: cifrato';
"
```
Per provare davvero il compose di produzione serve creare la rete
(`docker network create dokploy-network`) e aggiungere un override che pubblichi
la porta 80 del servizio `app`.
È lo stesso file che gira in produzione: l'unica differenza è il profilo `dev`
(che aggiunge Mailpit) e i valori nel `.env`.
---