# ============================================================================= # 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: # 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: - 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 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_UI_PORT:-127.0.0.1:8025}:8025" networks: - internal volumes: db_data: redis_data: storage_data: networks: # Database e Redis restano qui, senza rotta verso l'esterno. 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