Le porte erano legate a 127.0.0.1, quindi l'applicazione non era raggiungibile per IP: andava bene solo accedendo da un dominio via Traefik. Ora interfaccia e porta si decidono separatamente nel .env, per ogni servizio. APP_BIND=0.0.0.0 APP_PORT=8080 app esposta (default) DB_BIND=127.0.0.1 DB_PORT_HOST=3306 database solo da localhost MAILPIT_BIND=127.0.0.1 solo col profilo dev Il database resta su localhost per due motivi: ci si arriva comunque con un tunnel SSH senza esporlo a Internet, e su un host con piu' progetti la 3306 va spesso in conflitto (per questo e' cambiabile con DB_PORT_HOST). Redis non pubblica nulla: non serve da fuori. SESSION_SECURE_COOKIE passa a false come default, perche' ora l'accesso tipico e' in HTTP per IP. Con true il cookie di sessione e' marcato "secure" e il browser non lo rimanda su HTTP: verificato, il login risponde 419 Page Expired e si torna alla schermata di accesso. Il tranello e' che provandolo su localhost non si manifesta, perche' i browser considerano localhost un contesto sicuro: emerge solo accedendo per IP. E' documentato in .env.example insieme al sintomo esatto. Verificato: app raggiungibile dall'IP di rete, porta del database chiusa dall'esterno e aperta da localhost, login e pagine funzionanti via IP. Nota: senza dominio il traffico e' in chiaro. Il README segnala il passaggio a HTTPS come consigliato, con le tre variabili da cambiare. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
196 lines
6.2 KiB
YAML
196 lines
6.2 KiB
YAML
# =============================================================================
|
|
# TerManager2
|
|
#
|
|
# Un solo file per produzione e sviluppo. Tutta la configurazione sta nel file
|
|
# .env (su Dokploy lo genera l'interfaccia, in locale lo copi da .env.example).
|
|
#
|
|
# Produzione (Dokploy): deploy dall'interfaccia, niente da fare qui
|
|
# Locale: docker compose --profile dev up -d --build
|
|
#
|
|
# L'immagine e' autosufficiente: dipendenze PHP e asset frontend sono compilati
|
|
# durante il build, non all'avvio.
|
|
# =============================================================================
|
|
|
|
services:
|
|
|
|
# --- Applicazione (nginx + php-fpm) ---------------------------------------
|
|
app:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/php/Dockerfile
|
|
restart: unless-stopped
|
|
env_file: .env
|
|
environment:
|
|
# I sorgenti non cambiano dentro un'immagine gia' costruita.
|
|
PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0
|
|
ports:
|
|
# Interfaccia e porta si decidono separatamente nel .env:
|
|
# APP_BIND=0.0.0.0 raggiungibile dall'esterno (http://IP:8080)
|
|
# APP_BIND=127.0.0.1 solo dalla macchina stessa, per quando il
|
|
# traffico arriva da un reverse proxy
|
|
- "${APP_BIND:-0.0.0.0}:${APP_PORT:-8080}:80"
|
|
volumes:
|
|
# PDF territori, miniature, log e chiave di fallback.
|
|
- storage_data:/var/www/html/storage
|
|
networks:
|
|
- internal
|
|
- dokploy-network
|
|
depends_on:
|
|
mariadb:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
|
|
# --- Job in coda (import PDF massivi) -------------------------------------
|
|
queue-worker:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/php/Dockerfile
|
|
restart: unless-stopped
|
|
user: www-data
|
|
env_file: .env
|
|
volumes:
|
|
- storage_data:/var/www/html/storage
|
|
networks:
|
|
- internal
|
|
depends_on:
|
|
app:
|
|
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"]
|
|
# --max-time ricicla il worker ogni ora, cosi' un handle non rilasciato o
|
|
# la memoria cresciuta non si accumulano indefinitamente.
|
|
command:
|
|
- php
|
|
- artisan
|
|
- queue:work
|
|
- redis
|
|
- --queue=default
|
|
- --sleep=3
|
|
- --tries=3
|
|
- --timeout=600
|
|
- --max-time=3600
|
|
# Sostituisce l'healthcheck dell'immagine, che interroga nginx: qui non c'e'.
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pgrep -f 'queue:work' > /dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 60s
|
|
|
|
# --- Task pianificati (pulizia audit log) ---------------------------------
|
|
scheduler:
|
|
build:
|
|
context: .
|
|
dockerfile: docker/php/Dockerfile
|
|
restart: unless-stopped
|
|
user: www-data
|
|
env_file: .env
|
|
volumes:
|
|
- storage_data:/var/www/html/storage
|
|
networks:
|
|
- internal
|
|
depends_on:
|
|
app:
|
|
condition: service_healthy
|
|
entrypoint: ["queue-entrypoint.sh"]
|
|
# Sostituisce il crontab di sistema: si sveglia ogni minuto ed esegue
|
|
# quanto definito in routes/console.php.
|
|
command: ["php", "artisan", "schedule:work"]
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pgrep -f 'schedule:work' > /dev/null || exit 1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 60s
|
|
|
|
# --- Database -------------------------------------------------------------
|
|
mariadb:
|
|
image: mariadb:11
|
|
restart: unless-stopped
|
|
# Le ":?" fanno fallire subito il deploy se manca una password, invece di
|
|
# avviare un database di produzione con credenziali di default.
|
|
environment:
|
|
MARIADB_DATABASE: ${DB_DATABASE:-termanager2}
|
|
MARIADB_USER: ${DB_USERNAME:-termanager2}
|
|
MARIADB_PASSWORD: ${DB_PASSWORD:?manca DB_PASSWORD nel .env}
|
|
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?manca DB_ROOT_PASSWORD nel .env}
|
|
MARIADB_AUTO_UPGRADE: "1"
|
|
command:
|
|
- --character-set-server=utf8mb4
|
|
- --collation-server=utf8mb4_unicode_ci
|
|
- --innodb-buffer-pool-size=256M
|
|
ports:
|
|
# Legata a localhost: ci arrivi con un client grafico via tunnel SSH
|
|
# ssh -L 3306:127.0.0.1:3306 utente@server
|
|
# senza esporre il database a Internet. Per esporlo davvero (sconsigliato)
|
|
# metti DB_BIND=0.0.0.0 nel .env.
|
|
# Se la 3306 e' gia' occupata sull'host, cambia DB_PORT_HOST.
|
|
- "${DB_BIND:-127.0.0.1}:${DB_PORT_HOST:-3306}:3306"
|
|
volumes:
|
|
- db_data:/var/lib/mysql
|
|
networks:
|
|
- internal
|
|
healthcheck:
|
|
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
start_period: 30s
|
|
|
|
# --- Cache, sessioni, code ------------------------------------------------
|
|
redis:
|
|
image: redis:7-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
REDIS_PASSWORD: ${REDIS_PASSWORD:?manca REDIS_PASSWORD nel .env}
|
|
command:
|
|
- redis-server
|
|
- --appendonly
|
|
- "yes"
|
|
- --requirepass
|
|
- ${REDIS_PASSWORD:?manca REDIS_PASSWORD nel .env}
|
|
- --maxmemory
|
|
- 256mb
|
|
# Qui vivono sessioni e cache: meglio sfrattare chiavi che esaurire memoria.
|
|
- --maxmemory-policy
|
|
- allkeys-lru
|
|
volumes:
|
|
- redis_data:/data
|
|
networks:
|
|
- internal
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "redis-cli -a \"$$REDIS_PASSWORD\" ping | grep -q PONG"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 10
|
|
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_BIND:-127.0.0.1}:${MAILPIT_UI_PORT:-8025}:8025"
|
|
networks:
|
|
- internal
|
|
|
|
volumes:
|
|
db_data:
|
|
redis_data:
|
|
storage_data:
|
|
|
|
networks:
|
|
# Rete di servizio fra i container. Redis vive solo qui; MariaDB pubblica
|
|
# anche una porta sull'host, legata a localhost salvo diversa indicazione.
|
|
internal:
|
|
driver: bridge
|
|
# Rete condivisa del Traefik di Dokploy: e' cosi' che il dominio raggiunge
|
|
# il container. In locale creala una volta con:
|
|
# docker network create dokploy-network
|
|
dokploy-network:
|
|
external: true
|