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:
75
.env.example
75
.env.example
@@ -1,74 +1,91 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# TerManager2 — environment template
|
# TerManager2 — configurazione
|
||||||
#
|
#
|
||||||
# LOCAL DEVELOPMENT: copy to .env and adjust.
|
# Questo file e' l'UNICA fonte di configurazione: docker-compose.yml lo legge
|
||||||
# cp .env.example .env && docker compose -f docker-compose.dev.yml up -d
|
# cosi' com'e' e non ridichiara nulla.
|
||||||
#
|
#
|
||||||
# PRODUCTION (Dokploy): do NOT ship a .env file. Set these as environment
|
# In locale: cp .env.example .env e modifica quello che serve
|
||||||
# variables in the Dokploy UI instead — the container reads them directly.
|
# Su Dokploy: incolla queste variabili in Environment, il .env lo
|
||||||
# Variables marked [REQUIRED] have no safe default and the stack will refuse
|
# genera lui
|
||||||
# to start without them.
|
#
|
||||||
|
# Le variabili marcate [OBBLIGATORIA] non hanno default: lo stack si rifiuta
|
||||||
|
# di partire senza.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Applicazione
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
APP_NAME=TerManager2
|
APP_NAME=TerManager2
|
||||||
APP_ENV=production
|
APP_ENV=production
|
||||||
APP_DEBUG=false
|
APP_DEBUG=false
|
||||||
APP_TIMEZONE=Europe/Rome
|
APP_TIMEZONE=Europe/Rome
|
||||||
|
|
||||||
# [REQUIRED] Generate with: php artisan key:generate --show
|
# [OBBLIGATORIA] Deve combaciare ESATTAMENTE col dominio servito, schema
|
||||||
# Proclamatori personal data is encrypted with this key. If it changes or is
|
# incluso. L'app rifiuta richieste con un Host diverso (protezione contro
|
||||||
# lost, that data becomes permanently unreadable. Back it up.
|
# l'host header injection): un valore sbagliato da' 400 su tutto.
|
||||||
APP_KEY=
|
# In locale: http://localhost:8080
|
||||||
|
|
||||||
# [REQUIRED] Public URL, including scheme. Used for links in PDFs and emails,
|
|
||||||
# and to validate the incoming Host header.
|
|
||||||
APP_URL=https://territori.example.org
|
APP_URL=https://territori.example.org
|
||||||
|
|
||||||
|
# [OBBLIGATORIA] Genera con: echo "base64:$(openssl rand -base64 32)"
|
||||||
|
# I dati personali dei proclamatori sono cifrati con questa chiave: se la
|
||||||
|
# perdi non sono piu' leggibili nemmeno avendo il backup del database.
|
||||||
|
# Conservala in un password manager.
|
||||||
|
APP_KEY=
|
||||||
|
|
||||||
LOG_CHANNEL=stderr
|
LOG_CHANNEL=stderr
|
||||||
LOG_LEVEL=warning
|
LOG_LEVEL=warning
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# First-run bootstrap
|
# Primo avvio
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# The admin account created on first deploy. Change the password after login.
|
# L'amministratore creato al primo deploy. Cambia la password dopo il login.
|
||||||
INITIAL_ADMIN_NAME=Amministratore
|
INITIAL_ADMIN_NAME=Amministratore
|
||||||
INITIAL_ADMIN_EMAIL=
|
INITIAL_ADMIN_EMAIL=
|
||||||
INITIAL_ADMIN_PASSWORD=
|
INITIAL_ADMIN_PASSWORD=
|
||||||
|
|
||||||
# Leave these at their defaults; they exist as escape hatches.
|
# Lasciale cosi': esistono come via di fuga se qualcosa va storto.
|
||||||
RUN_MIGRATIONS=true
|
RUN_MIGRATIONS=true
|
||||||
RUN_DB_SEED_ON_FIRST_START=true
|
RUN_DB_SEED_ON_FIRST_START=true
|
||||||
ENSURE_INITIAL_ADMIN_ON_EMPTY_DB=true
|
ENSURE_INITIAL_ADMIN_ON_EMPTY_DB=true
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Database — hostnames are fixed by docker-compose.yml
|
# Database
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
# DB_HOST deve restare "mariadb": e' il nome del servizio in
|
||||||
|
# docker-compose.yml, non un indirizzo di rete.
|
||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=mysql
|
||||||
DB_HOST=mariadb
|
DB_HOST=mariadb
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
DB_DATABASE=termanager2
|
DB_DATABASE=termanager2
|
||||||
DB_USERNAME=termanager2
|
DB_USERNAME=termanager2
|
||||||
|
|
||||||
# [REQUIRED] No defaults on purpose — generate with: openssl rand -base64 24
|
# [OBBLIGATORIE] Genera con: openssl rand -base64 24
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
DB_ROOT_PASSWORD=
|
DB_ROOT_PASSWORD=
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Redis (cache, sessions, queue)
|
# Redis — cache, sessioni, code
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
# Come sopra: "redis" e' il nome del servizio, non va cambiato.
|
||||||
REDIS_HOST=redis
|
REDIS_HOST=redis
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
# [REQUIRED] openssl rand -base64 24
|
|
||||||
|
# [OBBLIGATORIA] openssl rand -base64 24
|
||||||
REDIS_PASSWORD=
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
CACHE_STORE=redis
|
CACHE_STORE=redis
|
||||||
SESSION_DRIVER=redis
|
SESSION_DRIVER=redis
|
||||||
SESSION_SECURE_COOKIE=true
|
|
||||||
QUEUE_CONNECTION=redis
|
QUEUE_CONNECTION=redis
|
||||||
|
|
||||||
|
# true in produzione (HTTPS), false se accedi in HTTP su localhost.
|
||||||
|
SESSION_SECURE_COOKIE=true
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Mail — external SMTP. Use MAIL_MAILER=log to disable delivery entirely.
|
# Posta
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
# In produzione: SMTP esterno.
|
||||||
|
# In locale con --profile dev: MAIL_HOST=mailpit, MAIL_PORT=1025
|
||||||
|
# Per disattivare del tutto l'invio: MAIL_MAILER=log
|
||||||
MAIL_MAILER=smtp
|
MAIL_MAILER=smtp
|
||||||
MAIL_HOST=
|
MAIL_HOST=
|
||||||
MAIL_PORT=587
|
MAIL_PORT=587
|
||||||
@@ -79,10 +96,10 @@ MAIL_FROM_ADDRESS=noreply@example.org
|
|||||||
MAIL_FROM_NAME="${APP_NAME}"
|
MAIL_FROM_NAME="${APP_NAME}"
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Local development only — ignored in production
|
# Porte
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Host port for docker-compose.dev.yml. In production Traefik handles routing
|
# Di default l'app ascolta solo su localhost. In produzione va bene cosi': il
|
||||||
# and no port is published.
|
# traffico arriva da Traefik sulla rete interna, non da questa porta.
|
||||||
APP_PORT=8080
|
# Per esporla davvero (server senza reverse proxy): APP_PORT=0.0.0.0:8080
|
||||||
MAILPIT_UI_PORT=8025
|
APP_PORT=127.0.0.1:8080
|
||||||
SEED_DEV_DATA=false
|
MAILPIT_UI_PORT=127.0.0.1:8025
|
||||||
|
|||||||
51
HANDOFF.md
51
HANDOFF.md
@@ -19,7 +19,7 @@ redeploy, login, tutte le pagine, code e scheduler.
|
|||||||
| Deploy Dokploy (Compose) | ✅ verificato |
|
| Deploy Dokploy (Compose) | ✅ verificato |
|
||||||
| Primo avvio a zero-config | ✅ migrazioni + seed + admin automatici |
|
| Primo avvio a zero-config | ✅ migrazioni + seed + admin automatici |
|
||||||
| Redeploy senza interventi | ✅ ~25 secondi |
|
| 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) |
|
| 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
|
### La configurazione arriva dall'ambiente, non da `.env` riscritto
|
||||||
|
|
||||||
Il vecchio entrypoint riscriveva `.env` con `sed` a ogni avvio. Ora Laravel
|
Il vecchio entrypoint riscriveva `.env` con `sed` a ogni avvio. Ora Laravel
|
||||||
legge direttamente l'ambiente di processo, e `docker-compose.yml` usa
|
legge direttamente l'ambiente di processo, e `docker-compose.yml` si limita a
|
||||||
`env_file` senza ridichiarare le variabili.
|
`env_file: .env` senza ridichiarare nulla.
|
||||||
|
|
||||||
Fissati nel compose solo i valori che sono una proprietà *della topologia*
|
Il `.env` è quindi l'**unica** fonte di configurazione. Una versione precedente
|
||||||
(`DB_HOST: mariadb`, `CACHE_STORE: redis`, `LOG_CHANNEL: stderr`, …). Questi
|
fissava nel compose i valori dettati dalla topologia (`DB_HOST: mariadb` e
|
||||||
**vincono** su quanto scritto nel `.env`, così un file copiato da una macchina
|
simili) perché vincessero su un `.env` sbagliato, ma richiedeva gli anchor YAML
|
||||||
di sviluppo non rompe il container.
|
(`x-app-env`, `<<:`) ed è stata scartata a favore della leggibilità.
|
||||||
|
|
||||||
Nota: perché questo funzioni, `www.conf` ha `clear_env = no`. Senza, php-fpm
|
Il rovescio della medaglia: `DB_HOST` e `REDIS_HOST` devono valere `mariadb` e
|
||||||
sanifica l'ambiente e Laravel non vedrebbe nessuna variabile.
|
`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
|
### 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:
|
Dopo una modifica sostanziale, questo giro copre i percorsi critici:
|
||||||
|
|
||||||
Si usa il compose di **sviluppo**: quello di produzione richiede la rete
|
C'è un solo `docker-compose.yml`, usato sia in produzione sia in locale. In
|
||||||
esterna `dokploy-network` e non pubblica porte sull'host, quindi non è
|
locale serve creare una volta la rete esterna attesa da Dokploy.
|
||||||
verificabile in locale così com'è.
|
|
||||||
|
|
||||||
```bash
|
```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
|
# 1. L'immagine builda
|
||||||
docker build -f docker/php/Dockerfile -t termanager2-check .
|
docker build -f docker/php/Dockerfile -t termanager2-check .
|
||||||
|
|
||||||
# 2. Stack pulito da zero
|
# 2. Stack pulito da zero
|
||||||
docker compose -f docker-compose.dev.yml down -v
|
docker compose --profile dev down -v
|
||||||
docker compose -f docker-compose.dev.yml up -d --build
|
docker compose --profile dev up -d --build
|
||||||
docker compose -f docker-compose.dev.yml ps # tutti i servizi healthy
|
docker compose --profile dev ps # tutti i servizi healthy
|
||||||
|
|
||||||
# 3. L'applicazione risponde
|
# 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/up # 200
|
||||||
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/login # 200
|
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/login # 200
|
||||||
|
|
||||||
# 4. Il redeploy è idempotente
|
# 4. Il redeploy è idempotente
|
||||||
docker compose -f docker-compose.dev.yml up -d --force-recreate app
|
docker compose up -d --force-recreate app
|
||||||
docker compose -f docker-compose.dev.yml logs app | grep -E "Migrations|Seed"
|
docker compose logs app | grep -E "Migrations|Seed"
|
||||||
# atteso: "Migrations applied" + "Seed already applied"
|
# atteso: "Migrations applied" + "Seed already applied"
|
||||||
|
|
||||||
# 5. La cifratura funziona
|
# 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]);
|
\$p = App\Models\Proclamatore::create(['nome'=>'Test','cognome'=>'Verifica','attivo'=>true]);
|
||||||
\$raw = DB::table('proclamatori')->where('id',\$p->id)->value('nome');
|
\$raw = DB::table('proclamatori')->where('id',\$p->id)->value('nome');
|
||||||
echo \$raw === 'Test' ? 'ERRORE: dati in chiaro' : 'OK: cifrato';
|
echo \$raw === 'Test' ? 'ERRORE: dati in chiaro' : 'OK: cifrato';
|
||||||
"
|
"
|
||||||
```
|
```
|
||||||
|
|
||||||
Per provare davvero il compose di produzione serve creare la rete
|
È lo stesso file che gira in produzione: l'unica differenza è il profilo `dev`
|
||||||
(`docker network create dokploy-network`) e aggiungere un override che pubblichi
|
(che aggiunge Mailpit) e i valori nel `.env`.
|
||||||
la porta 80 del servizio `app`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
126
README.md
126
README.md
@@ -102,17 +102,29 @@ DB_PASSWORD=...
|
|||||||
DB_ROOT_PASSWORD=...
|
DB_ROOT_PASSWORD=...
|
||||||
REDIS_PASSWORD=...
|
REDIS_PASSWORD=...
|
||||||
|
|
||||||
# --- Database ---
|
# --- Database (mariadb e' il nome del servizio, non cambiarlo) ---
|
||||||
|
DB_CONNECTION=mysql
|
||||||
|
DB_HOST=mariadb
|
||||||
|
DB_PORT=3306
|
||||||
DB_DATABASE=termanager2
|
DB_DATABASE=termanager2
|
||||||
DB_USERNAME=termanager2
|
DB_USERNAME=termanager2
|
||||||
|
|
||||||
|
# --- Redis (idem: redis e' il nome del servizio) ---
|
||||||
|
REDIS_HOST=redis
|
||||||
|
REDIS_PORT=6379
|
||||||
|
CACHE_STORE=redis
|
||||||
|
SESSION_DRIVER=redis
|
||||||
|
QUEUE_CONNECTION=redis
|
||||||
|
|
||||||
# --- Primo amministratore (creato al primo avvio) ---
|
# --- Primo amministratore (creato al primo avvio) ---
|
||||||
INITIAL_ADMIN_NAME=Nome Cognome
|
INITIAL_ADMIN_NAME=Nome Cognome
|
||||||
INITIAL_ADMIN_EMAIL=admin@tuodominio.it
|
INITIAL_ADMIN_EMAIL=admin@tuodominio.it
|
||||||
INITIAL_ADMIN_PASSWORD=UnaPasswordSolida
|
INITIAL_ADMIN_PASSWORD=UnaPasswordSolida
|
||||||
|
|
||||||
# --- Sessioni ---
|
# --- Sessioni e log ---
|
||||||
SESSION_SECURE_COOKIE=true
|
SESSION_SECURE_COOKIE=true
|
||||||
|
LOG_CHANNEL=stderr
|
||||||
|
LOG_LEVEL=warning
|
||||||
|
|
||||||
# --- Posta (SMTP esterno) ---
|
# --- Posta (SMTP esterno) ---
|
||||||
MAIL_MAILER=smtp
|
MAIL_MAILER=smtp
|
||||||
@@ -186,63 +198,84 @@ Lo stack è Docker Compose standard e funziona su **Coolify**, **Portainer**,
|
|||||||
|
|
||||||
Le uniche due particolarità sono:
|
Le uniche due particolarità sono:
|
||||||
|
|
||||||
1. **La rete `dokploy-network`.** In fondo a `docker-compose.yml` c'è una rete
|
1. **Il file `.env`.** Dokploy lo genera dalle variabili della sua interfaccia.
|
||||||
esterna che serve al Traefik di Dokploy per raggiungere il container. Su
|
Altrove crealo a mano: `cp .env.example .env` e compila i valori.
|
||||||
altri sistemi rimuovila e pubblica una porta:
|
|
||||||
|
|
||||||
```yaml
|
2. **La rete `dokploy-network`.** In fondo a `docker-compose.yml` c'è una rete
|
||||||
services:
|
esterna che serve al Traefik di Dokploy per raggiungere il container. Hai
|
||||||
app:
|
due possibilità:
|
||||||
ports:
|
|
||||||
- "8080:80"
|
|
||||||
networks:
|
|
||||||
- internal # rimuovi "- dokploy-network"
|
|
||||||
|
|
||||||
networks:
|
- **Tenerla**, creandola una volta sola:
|
||||||
internal:
|
`docker network create dokploy-network`
|
||||||
driver: bridge
|
- **Rimuoverla**: elimina `- dokploy-network` dalle reti del servizio `app`
|
||||||
# rimuovi il blocco dokploy-network
|
e il blocco `dokploy-network:` in fondo al file.
|
||||||
```
|
|
||||||
|
|
||||||
Poi metti il tuo reverse proxy (Nginx, Caddy, Traefik) davanti alla porta
|
Poi imposta nel `.env` la porta su cui esporre l'applicazione e avvia:
|
||||||
`8080`, con certificato TLS.
|
|
||||||
|
|
||||||
2. **Il file `.env`.** Dokploy lo genera dalle variabili della sua interfaccia.
|
|
||||||
Altrove crealo a mano nella cartella del progetto, con lo stesso contenuto
|
|
||||||
del [Passo 3](#passo-3--imposta-le-variabili-dambiente).
|
|
||||||
|
|
||||||
Dopodiché:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# ascolta su tutte le interfacce, per metterci davanti un reverse proxy
|
||||||
|
echo "APP_PORT=0.0.0.0:8080" >> .env
|
||||||
|
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
docker compose logs -f app
|
docker compose logs -f app
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Infine metti il tuo reverse proxy (Nginx, Caddy, Traefik) davanti alla porta
|
||||||
|
`8080`, con certificato TLS.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Sviluppo locale
|
## Sviluppo locale
|
||||||
|
|
||||||
|
Si usa lo stesso `docker-compose.yml` della produzione, con il profilo `dev`
|
||||||
|
che aggiunge Mailpit:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
docker compose -f docker-compose.dev.yml up -d --build
|
docker network create dokploy-network # una volta sola
|
||||||
|
|
||||||
|
# nel .env, per l'uso locale:
|
||||||
|
# APP_URL=http://localhost:8080
|
||||||
|
# APP_ENV=local
|
||||||
|
# SESSION_SECURE_COOKIE=false
|
||||||
|
# MAIL_MAILER=smtp MAIL_HOST=mailpit MAIL_PORT=1025
|
||||||
|
# e le tre password a piacere
|
||||||
|
|
||||||
|
docker compose --profile dev up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
| Cosa | Dove |
|
| Cosa | Dove |
|
||||||
|---------------------|-------------------------------|
|
|---------------------|-------------------------------|
|
||||||
| Applicazione | http://localhost:8080 |
|
| Applicazione | http://localhost:8080 |
|
||||||
| Mailpit (posta test)| http://localhost:8025 |
|
| Mailpit (posta test)| http://localhost:8025 |
|
||||||
| Login | `admin@termanager2.local` / `password` |
|
| Login | quello che hai messo in `INITIAL_ADMIN_*` |
|
||||||
|
|
||||||
A differenza della produzione, l'ambiente di sviluppo monta il codice sorgente
|
Se la porta 8080 è occupata, cambia `APP_PORT` nel `.env`
|
||||||
in bind mount — le modifiche PHP sono immediate — e pubblica le porte
|
(es. `APP_PORT=127.0.0.1:8081`).
|
||||||
sull'host.
|
|
||||||
|
|
||||||
Se la porta 8080 è occupata:
|
### Modifiche al codice senza ricostruire l'immagine
|
||||||
|
|
||||||
```bash
|
L'immagine è autosufficiente: `vendor/` e `public/build` stanno dentro, quindi
|
||||||
APP_PORT=8081 docker compose -f docker-compose.dev.yml up -d
|
di norma una modifica al codice richiede `docker compose up -d --build`.
|
||||||
|
|
||||||
|
Per avere invece le modifiche PHP immediate, crea un
|
||||||
|
`docker-compose.override.yml` — Docker Compose lo applica da solo, senza
|
||||||
|
bisogno di alcun flag:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
environment:
|
||||||
|
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 1
|
||||||
|
volumes:
|
||||||
|
- ./:/var/www/html
|
||||||
|
# vendor e build restano quelli dell'immagine, non quelli dell'host
|
||||||
|
- /var/www/html/vendor
|
||||||
|
- /var/www/html/public/build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Il file è già in `.gitignore`, quindi resta solo tuo.
|
||||||
|
|
||||||
Per lavorare sugli asset frontend con hot reload:
|
Per lavorare sugli asset frontend con hot reload:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -285,15 +318,20 @@ ridichiara le variabili applicative: le prende dal `.env` così com'è.
|
|||||||
| `RUN_MIGRATIONS` | `true` | Migrazioni automatiche all'avvio |
|
| `RUN_MIGRATIONS` | `true` | Migrazioni automatiche all'avvio |
|
||||||
| `RUN_DB_SEED_ON_FIRST_START` | `true` | Seed al primo avvio |
|
| `RUN_DB_SEED_ON_FIRST_START` | `true` | Seed al primo avvio |
|
||||||
|
|
||||||
### Fissate dal compose
|
### Da non cambiare
|
||||||
|
|
||||||
Questi valori sono imposti da `docker-compose.yml` e **ignorano** quanto scritto
|
Questi valori sono **nomi di servizi** definiti in `docker-compose.yml`, non
|
||||||
nel `.env`, perché descrivono la topologia dello stack: `DB_HOST`, `DB_PORT`,
|
indirizzi di rete. Se li modifichi il container non trova più il database o
|
||||||
`REDIS_HOST`, `REDIS_PORT`, `CACHE_STORE`, `SESSION_DRIVER`,
|
Redis:
|
||||||
`QUEUE_CONNECTION`, `LOG_CHANNEL`.
|
|
||||||
|
|
||||||
Serve a evitare che un `.env` copiato da una macchina di sviluppo (con, per
|
```dotenv
|
||||||
dire, `DB_HOST=127.0.0.1`) rompa il container.
|
DB_HOST=mariadb
|
||||||
|
REDIS_HOST=redis
|
||||||
|
```
|
||||||
|
|
||||||
|
Nemmeno `CACHE_STORE`, `SESSION_DRIVER` e `QUEUE_CONNECTION` andrebbero
|
||||||
|
spostati altrove: lo stack fornisce Redis e il queue worker consuma la coda
|
||||||
|
Redis, quindi puntarli su un driver diverso interrompe l'elaborazione dei job.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -451,8 +489,8 @@ cifrati).
|
|||||||
### Reset completo (solo sviluppo)
|
### Reset completo (solo sviluppo)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -f docker-compose.dev.yml down -v
|
docker compose --profile dev down -v
|
||||||
docker compose -f docker-compose.dev.yml up -d --build
|
docker compose --profile dev up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
`-v` cancella i volumi: database, Redis e file caricati vengono persi.
|
`-v` cancella i volumi: database, Redis e file caricati vengono persi.
|
||||||
@@ -673,8 +711,8 @@ TerManager2/
|
|||||||
│ └── supervisor/ # nginx + php-fpm nello stesso container
|
│ └── supervisor/ # nginx + php-fpm nello stesso container
|
||||||
├── resources/ # CSS, JS, viste Blade
|
├── resources/ # CSS, JS, viste Blade
|
||||||
├── routes/web.php # Rotte con i relativi permessi
|
├── routes/web.php # Rotte con i relativi permessi
|
||||||
├── docker-compose.yml # Produzione
|
├── docker-compose.yml # Unico, produzione e sviluppo
|
||||||
├── docker-compose.dev.yml # Sviluppo locale
|
├── .env.example # Tutta la configurazione
|
||||||
├── HANDOFF.md # Stato del progetto per chi subentra
|
├── HANDOFF.md # Stato del progetto per chi subentra
|
||||||
└── README.md
|
└── README.md
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,149 +0,0 @@
|
|||||||
# =============================================================================
|
|
||||||
# TerManager2 — local development
|
|
||||||
#
|
|
||||||
# cp .env.example .env
|
|
||||||
# docker compose -f docker-compose.dev.yml up -d --build
|
|
||||||
# open http://localhost:8080 (Mailpit UI on http://localhost:8025)
|
|
||||||
#
|
|
||||||
# This is a standalone file, not an overlay: it bind-mounts the source so edits
|
|
||||||
# apply without a rebuild, publishes ports to the host, and swaps the external
|
|
||||||
# Traefik network for a local one. Production uses docker-compose.yml.
|
|
||||||
# =============================================================================
|
|
||||||
|
|
||||||
x-dev-env: &dev-env
|
|
||||||
APP_NAME: ${APP_NAME:-TerManager2}
|
|
||||||
APP_ENV: local
|
|
||||||
APP_DEBUG: "true"
|
|
||||||
APP_KEY: ${APP_KEY:-}
|
|
||||||
APP_URL: http://localhost:${APP_PORT:-8080}
|
|
||||||
APP_TIMEZONE: ${APP_TIMEZONE:-Europe/Rome}
|
|
||||||
|
|
||||||
DB_CONNECTION: mysql
|
|
||||||
DB_HOST: mariadb
|
|
||||||
DB_PORT: 3306
|
|
||||||
DB_DATABASE: ${DB_DATABASE:-termanager2}
|
|
||||||
DB_USERNAME: ${DB_USERNAME:-termanager2}
|
|
||||||
DB_PASSWORD: ${DB_PASSWORD:-secret}
|
|
||||||
|
|
||||||
REDIS_HOST: redis
|
|
||||||
REDIS_PORT: 6379
|
|
||||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-redissecret}
|
|
||||||
|
|
||||||
CACHE_STORE: redis
|
|
||||||
SESSION_DRIVER: redis
|
|
||||||
SESSION_SECURE_COOKIE: "false"
|
|
||||||
QUEUE_CONNECTION: redis
|
|
||||||
|
|
||||||
LOG_CHANNEL: stderr
|
|
||||||
LOG_LEVEL: debug
|
|
||||||
|
|
||||||
MAIL_MAILER: smtp
|
|
||||||
MAIL_HOST: mailpit
|
|
||||||
MAIL_PORT: 1025
|
|
||||||
MAIL_FROM_ADDRESS: noreply@termanager2.local
|
|
||||||
MAIL_FROM_NAME: TerManager2
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: docker/php/Dockerfile
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "${APP_PORT:-8080}:80"
|
|
||||||
environment:
|
|
||||||
<<: *dev-env
|
|
||||||
# Pick up source edits without restarting php-fpm.
|
|
||||||
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 1
|
|
||||||
RUN_DB_SEED_ON_FIRST_START: "true"
|
|
||||||
SEED_DEV_DATA: ${SEED_DEV_DATA:-false}
|
|
||||||
INITIAL_ADMIN_NAME: ${INITIAL_ADMIN_NAME:-Amministratore}
|
|
||||||
INITIAL_ADMIN_EMAIL: ${INITIAL_ADMIN_EMAIL:-admin@termanager2.local}
|
|
||||||
INITIAL_ADMIN_PASSWORD: ${INITIAL_ADMIN_PASSWORD:-password}
|
|
||||||
volumes:
|
|
||||||
# Source is bind-mounted for live edits. vendor/ and public/build stay as
|
|
||||||
# anonymous volumes so the host (which has neither) doesn't mask what the
|
|
||||||
# image built.
|
|
||||||
- ./:/var/www/html
|
|
||||||
- /var/www/html/vendor
|
|
||||||
- /var/www/html/public/build
|
|
||||||
networks:
|
|
||||||
- termanager2
|
|
||||||
depends_on:
|
|
||||||
mariadb:
|
|
||||||
condition: service_healthy
|
|
||||||
redis:
|
|
||||||
condition: service_healthy
|
|
||||||
|
|
||||||
queue-worker:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: docker/php/Dockerfile
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
<<: *dev-env
|
|
||||||
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 1
|
|
||||||
volumes:
|
|
||||||
- ./:/var/www/html
|
|
||||||
- /var/www/html/vendor
|
|
||||||
- /var/www/html/public/build
|
|
||||||
networks:
|
|
||||||
- termanager2
|
|
||||||
depends_on:
|
|
||||||
app:
|
|
||||||
condition: service_healthy
|
|
||||||
entrypoint: ["queue-entrypoint.sh"]
|
|
||||||
command: ["php", "artisan", "queue:work", "redis", "--queue=default", "--sleep=3", "--tries=1", "--timeout=600"]
|
|
||||||
|
|
||||||
mariadb:
|
|
||||||
image: mariadb:11
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
MARIADB_DATABASE: ${DB_DATABASE:-termanager2}
|
|
||||||
MARIADB_USER: ${DB_USERNAME:-termanager2}
|
|
||||||
MARIADB_PASSWORD: ${DB_PASSWORD:-secret}
|
|
||||||
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootsecret}
|
|
||||||
ports:
|
|
||||||
# Exposed locally so you can attach a GUI client. Never in production.
|
|
||||||
- "${DB_PORT_HOST:-3306}:3306"
|
|
||||||
volumes:
|
|
||||||
- db_data_dev:/var/lib/mysql
|
|
||||||
networks:
|
|
||||||
- termanager2
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 10
|
|
||||||
start_period: 30s
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis:7-alpine
|
|
||||||
restart: unless-stopped
|
|
||||||
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD:-redissecret}"]
|
|
||||||
volumes:
|
|
||||||
- redis_data_dev:/data
|
|
||||||
networks:
|
|
||||||
- termanager2
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "redis-cli -a '${REDIS_PASSWORD:-redissecret}' ping | grep -q PONG"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 10
|
|
||||||
|
|
||||||
mailpit:
|
|
||||||
image: axllent/mailpit:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "${MAIL_PORT:-1025}:1025"
|
|
||||||
- "${MAILPIT_UI_PORT:-8025}:8025"
|
|
||||||
networks:
|
|
||||||
- termanager2
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
db_data_dev:
|
|
||||||
redis_data_dev:
|
|
||||||
|
|
||||||
networks:
|
|
||||||
termanager2:
|
|
||||||
driver: bridge
|
|
||||||
@@ -1,60 +1,37 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# TerManager2 — production stack (Dokploy)
|
# TerManager2
|
||||||
#
|
#
|
||||||
# The application image is self-contained: PHP dependencies and compiled
|
# Un solo file per produzione e sviluppo. Tutta la configurazione sta nel file
|
||||||
# frontend assets are baked in at build time, and nginx runs alongside php-fpm
|
# .env (su Dokploy lo genera l'interfaccia, in locale lo copi da .env.example).
|
||||||
# inside the app container. There are no bind mounts and no build steps at
|
|
||||||
# boot, so a redeploy is: build image, migrate, serve.
|
|
||||||
#
|
#
|
||||||
# Configuration comes from the .env file, which Dokploy writes from the
|
# Produzione (Dokploy): deploy dall'interfaccia, niente da fare qui
|
||||||
# variables you set in its UI. This file does not re-declare them — it only
|
# Locale: docker compose --profile dev up -d --build
|
||||||
# pins the handful of values dictated by the topology below (service names,
|
|
||||||
# which datastores exist), because those must match this file, not .env.
|
|
||||||
#
|
#
|
||||||
# For local development use docker-compose.dev.yml instead.
|
# L'immagine e' autosufficiente: dipendenze PHP e asset frontend sono compilati
|
||||||
|
# durante il build, non all'avvio.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
x-app: &app
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: docker/php/Dockerfile
|
|
||||||
restart: unless-stopped
|
|
||||||
# required:false so `docker compose config` and a bare checkout still work;
|
|
||||||
# a missing .env then surfaces as the explicit DB_PASSWORD error below.
|
|
||||||
env_file:
|
|
||||||
- path: .env
|
|
||||||
required: false
|
|
||||||
volumes:
|
|
||||||
# Uploaded territory PDFs, thumbnails, logs and the persisted APP_KEY.
|
|
||||||
- storage_data:/var/www/html/storage
|
|
||||||
networks:
|
|
||||||
- internal
|
|
||||||
|
|
||||||
# Values that are a property of THIS compose file rather than of the
|
|
||||||
# deployment, so they override whatever .env happens to say. A DB_HOST of
|
|
||||||
# 127.0.0.1 left over from a local .env would otherwise break the container.
|
|
||||||
x-app-env: &app-env
|
|
||||||
DB_CONNECTION: mysql
|
|
||||||
DB_HOST: mariadb
|
|
||||||
DB_PORT: 3306
|
|
||||||
REDIS_HOST: redis
|
|
||||||
REDIS_PORT: 6379
|
|
||||||
# The stack ships Redis and the worker below consumes the redis queue;
|
|
||||||
# pointing these elsewhere would silently break job processing.
|
|
||||||
CACHE_STORE: redis
|
|
||||||
SESSION_DRIVER: redis
|
|
||||||
QUEUE_CONNECTION: redis
|
|
||||||
# Logs go to the container's stderr, where `docker logs` and the Dokploy
|
|
||||||
# log viewer can see them, rather than into a file inside the volume.
|
|
||||||
LOG_CHANNEL: stderr
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
|
# --- Applicazione (nginx + php-fpm) ---------------------------------------
|
||||||
app:
|
app:
|
||||||
<<: *app
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/php/Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
<<: *app-env
|
# I sorgenti non cambiano dentro un'immagine gia' costruita.
|
||||||
# Sources never change in a built image, so skip the stat() on every hit.
|
|
||||||
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0
|
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0
|
||||||
|
ports:
|
||||||
|
# Di default ascolta solo su localhost: in produzione il traffico passa
|
||||||
|
# da Traefik sulla rete interna, non da questa porta. In locale invece
|
||||||
|
# apri http://localhost:8080.
|
||||||
|
# Per esporlo davvero: APP_PORT=0.0.0.0:8080 nel .env
|
||||||
|
- "${APP_PORT:-127.0.0.1:8080}:80"
|
||||||
|
volumes:
|
||||||
|
# PDF territori, miniature, log e chiave di fallback.
|
||||||
|
- storage_data:/var/www/html/storage
|
||||||
networks:
|
networks:
|
||||||
- internal
|
- internal
|
||||||
- dokploy-network
|
- dokploy-network
|
||||||
@@ -64,18 +41,26 @@ services:
|
|||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
|
# --- Job in coda (import PDF massivi) -------------------------------------
|
||||||
queue-worker:
|
queue-worker:
|
||||||
<<: *app
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/php/Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
user: www-data
|
user: www-data
|
||||||
environment:
|
env_file: .env
|
||||||
<<: *app-env
|
volumes:
|
||||||
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0
|
- storage_data:/var/www/html/storage
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
depends_on:
|
depends_on:
|
||||||
app:
|
app:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
# Non esegue migrazioni: le fa solo il container app, per evitare che due
|
||||||
|
# container in gara lascino uno schema applicato a meta'.
|
||||||
entrypoint: ["queue-entrypoint.sh"]
|
entrypoint: ["queue-entrypoint.sh"]
|
||||||
# --max-time recycles the worker hourly so a leaked handle or grown heap
|
# --max-time ricicla il worker ogni ora, cosi' un handle non rilasciato o
|
||||||
# can't accumulate indefinitely; restarting it is Docker's job.
|
# la memoria cresciuta non si accumulano indefinitamente.
|
||||||
command:
|
command:
|
||||||
- php
|
- php
|
||||||
- artisan
|
- artisan
|
||||||
@@ -86,7 +71,7 @@ services:
|
|||||||
- --tries=3
|
- --tries=3
|
||||||
- --timeout=600
|
- --timeout=600
|
||||||
- --max-time=3600
|
- --max-time=3600
|
||||||
# Overrides the image's nginx-based HEALTHCHECK (no web server here).
|
# Sostituisce l'healthcheck dell'immagine, che interroga nginx: qui non c'e'.
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pgrep -f 'queue:work' > /dev/null || exit 1"]
|
test: ["CMD-SHELL", "pgrep -f 'queue:work' > /dev/null || exit 1"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -94,21 +79,25 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 60s
|
start_period: 60s
|
||||||
|
|
||||||
|
# --- Task pianificati (pulizia audit log) ---------------------------------
|
||||||
scheduler:
|
scheduler:
|
||||||
<<: *app
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/php/Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
user: www-data
|
user: www-data
|
||||||
environment:
|
env_file: .env
|
||||||
<<: *app-env
|
volumes:
|
||||||
|
- storage_data:/var/www/html/storage
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
depends_on:
|
depends_on:
|
||||||
app:
|
app:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
entrypoint: ["queue-entrypoint.sh"]
|
entrypoint: ["queue-entrypoint.sh"]
|
||||||
# `schedule:work` is the container-native replacement for a host crontab —
|
# Sostituisce il crontab di sistema: si sveglia ogni minuto ed esegue
|
||||||
# it wakes every minute and dispatches whatever routes/console.php defines
|
# quanto definito in routes/console.php.
|
||||||
# (currently the audit-log cleanup).
|
|
||||||
command: ["php", "artisan", "schedule:work"]
|
command: ["php", "artisan", "schedule:work"]
|
||||||
# Same reason as above: this container runs neither nginx nor php-fpm, so
|
|
||||||
# the image's HTTP health check would mark it unhealthy forever.
|
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pgrep -f 'schedule:work' > /dev/null || exit 1"]
|
test: ["CMD-SHELL", "pgrep -f 'schedule:work' > /dev/null || exit 1"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -116,18 +105,17 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
start_period: 60s
|
start_period: 60s
|
||||||
|
|
||||||
|
# --- Database -------------------------------------------------------------
|
||||||
mariadb:
|
mariadb:
|
||||||
image: mariadb:11
|
image: mariadb:11
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# Interpolated (not env_file) because these are the *image's* own bootstrap
|
# Le ":?" fanno fallire subito il deploy se manca una password, invece di
|
||||||
# variables under different names. The `:?` guards are what make a deploy
|
# avviare un database di produzione con credenziali di default.
|
||||||
# with no configured passwords fail loudly instead of coming up with
|
|
||||||
# whatever default happened to be lying around.
|
|
||||||
environment:
|
environment:
|
||||||
MARIADB_DATABASE: ${DB_DATABASE:-termanager2}
|
MARIADB_DATABASE: ${DB_DATABASE:-termanager2}
|
||||||
MARIADB_USER: ${DB_USERNAME:-termanager2}
|
MARIADB_USER: ${DB_USERNAME:-termanager2}
|
||||||
MARIADB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required}
|
MARIADB_PASSWORD: ${DB_PASSWORD:?manca DB_PASSWORD nel .env}
|
||||||
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?DB_ROOT_PASSWORD is required}
|
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?manca DB_ROOT_PASSWORD nel .env}
|
||||||
MARIADB_AUTO_UPGRADE: "1"
|
MARIADB_AUTO_UPGRADE: "1"
|
||||||
command:
|
command:
|
||||||
- --character-set-server=utf8mb4
|
- --character-set-server=utf8mb4
|
||||||
@@ -144,20 +132,21 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
|
# --- Cache, sessioni, code ------------------------------------------------
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
REDIS_PASSWORD: ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
|
REDIS_PASSWORD: ${REDIS_PASSWORD:?manca REDIS_PASSWORD nel .env}
|
||||||
command:
|
command:
|
||||||
- redis-server
|
- redis-server
|
||||||
- --appendonly
|
- --appendonly
|
||||||
- "yes"
|
- "yes"
|
||||||
- --requirepass
|
- --requirepass
|
||||||
- ${REDIS_PASSWORD:?REDIS_PASSWORD is required}
|
- ${REDIS_PASSWORD:?manca REDIS_PASSWORD nel .env}
|
||||||
- --maxmemory
|
- --maxmemory
|
||||||
- 256mb
|
- 256mb
|
||||||
# Sessions and cache live here; evict volatile keys rather than OOM.
|
# Qui vivono sessioni e cache: meglio sfrattare chiavi che esaurire memoria.
|
||||||
- --maxmemory-policy
|
- --maxmemory-policy
|
||||||
- allkeys-lru
|
- allkeys-lru
|
||||||
volumes:
|
volumes:
|
||||||
@@ -171,16 +160,28 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
start_period: 10s
|
start_period: 10s
|
||||||
|
|
||||||
|
# --- Posta di test (solo sviluppo) ----------------------------------------
|
||||||
|
# Parte solo con --profile dev, mai in produzione.
|
||||||
|
mailpit:
|
||||||
|
image: axllent/mailpit:latest
|
||||||
|
profiles: ["dev"]
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${MAILPIT_UI_PORT:-127.0.0.1:8025}:8025"
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db_data:
|
db_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
storage_data:
|
storage_data:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
# Datastores stay on an isolated network with no route to the outside.
|
# Database e Redis restano qui, senza rotta verso l'esterno.
|
||||||
internal:
|
internal:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
# Dokploy's shared Traefik network. Only `app` joins it, and Dokploy attaches
|
# Rete condivisa del Traefik di Dokploy: e' cosi' che il dominio raggiunge
|
||||||
# the routing labels when you assign a domain to that service in the UI.
|
# il container. In locale creala una volta con:
|
||||||
|
# docker network create dokploy-network
|
||||||
dokploy-network:
|
dokploy-network:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
Reference in New Issue
Block a user