Files
clinicpro/docker-compose.yml
T

127 lines
4.9 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Coolify deployment — Build Pack: Docker Compose
# This is the production APP stack (app + workers ONLY). MariaDB and Redis are
# run as SEPARATE Coolify Database Resources, not part of this compose.
# - Create a standalone MariaDB resource and a standalone Redis resource in Coolify.
# - Enable "Connect to Predefined Network" on THIS app stack.
# - Set DATABASE_URL / REDIS_URL / MESSENGER_TRANSPORT_DSN in Coolify's env tab,
# pointing at the resources via their internal hostnames (mariadb-<uuid> / redis-<uuid>).
# The ddev local env uses its own compose.yaml; do not confuse the two.
# Select this file (docker-compose.yml) as the Compose file in the Coolify resource.
# Do NOT define custom `networks:` — Coolify manages the network; custom ones
# break Traefik routing (per Coolify docs).
# Shared application environment, reused by app + both workers.
x-app-env: &app-env
APP_ENV: prod
APP_DEBUG: "0"
APP_SECRET: ${APP_SECRET}
# Connection strings point at the SEPARATE Coolify DB/Redis resources.
# Real values are injected from Coolify's Environment Variables tab — the
# hostname is mariadb-<uuid> / redis-<uuid> from each resource's page, so they
# cannot be hardcoded here (the UUID differs per installation).
DATABASE_URL: ${DATABASE_URL}
MESSENGER_TRANSPORT_DSN: ${MESSENGER_TRANSPORT_DSN}
REDIS_URL: ${REDIS_URL}
JWT_SECRET_KEY: "%kernel.project_dir%/config/jwt/private.pem"
JWT_PUBLIC_KEY: "%kernel.project_dir%/config/jwt/public.pem"
JWT_PASSPHRASE: ${JWT_PASSPHRASE}
DEFAULT_URI: ${APP_BASE_URL}
APP_BASE_URL: ${APP_BASE_URL}
ALLOWED_FRONTEND_HOSTS: ${ALLOWED_FRONTEND_HOSTS}
TRUSTED_PROXIES: ${TRUSTED_PROXIES}
API_IR_BASE_URL: ${API_IR_BASE_URL}
API_IR_TOKEN: ${API_IR_TOKEN}
KAVENEGAR_API_KEY: ${KAVENEGAR_API_KEY}
REFRESH_TOKEN_TTL: "2592000"
OTP_TTL: "1200"
MAX_FILE_SIZE_BYTES: "5242880"
UPLOAD_DIR: var/uploads
# Shared volume mounts for services that read/write JWT keys and uploads.
x-app-volumes: &app-volumes
- jwt_keys:/app/config/jwt
- uploads_public:/app/public/uploads
- uploads_var:/app/var/uploads
services:
# Web app (PHP-FPM + Nginx).
# In Coolify, assign ALL serving domains to THIS service (port 80) — the backend
# API host plus every frontend domain you want Traefik to route + issue TLS for.
# Coolify supports a comma-separated domain list on the service.
# CORS/payload allow-listing for those frontends is driven by ALLOWED_FRONTEND_HOSTS
# (the CORS regex is built from it in PHP via CorsRegexEnvProcessor; see
# docker/frontend-domains.json). CORS_ALLOW_ORIGIN is no longer used.
app:
# Shared image tag — app builds it once; the two workers reuse the SAME image
# (image: below, no build:). Without this Coolify builds the Dockerfile 3×,
# tripling apk network fetches and intermittently failing on the host.
image: clinicpro-app:latest
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
environment:
<<: *app-env
RUN_INIT: "1" # runs JWT keygen + migrations on start (only this service)
volumes: *app-volumes
networks: [coolify]
# Hit the real /health route (nginx on 8080, non-root) via docker/healthcheck.sh.
healthcheck:
test: ["CMD", "/usr/local/bin/healthcheck.sh"]
interval: 15s
timeout: 5s
retries: 5
start_period: 60s
# SMS / async message consumer. Reuses the image built by `app` (no build:).
worker-async:
image: clinicpro-app:latest
restart: unless-stopped
command: sh -c "while :; do php bin/console messenger:consume async --time-limit=3600 --memory-limit=128M; sleep 1; done"
environment:
<<: *app-env
RUN_INIT: "0"
volumes: *app-volumes
networks: [coolify]
depends_on: [app]
# Messenger consumers handle SIGTERM gracefully — give the in-flight message
# time to finish before the container is killed.
stop_grace_period: 30s
# No port to probe — just confirm the consumer process is alive (busybox ps).
healthcheck:
test: ["CMD-SHELL", "ps -o args 2>/dev/null | grep -q '[m]essenger:consume async' || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
# Scheduler consumer — expires unpaid appointments every minute. Reuses app's image.
worker-scheduler:
image: clinicpro-app:latest
restart: unless-stopped
command: sh -c "while :; do php bin/console messenger:consume scheduler_default --time-limit=3600 --memory-limit=128M; sleep 1; done"
environment:
<<: *app-env
RUN_INIT: "0"
volumes: *app-volumes
networks: [coolify]
depends_on: [app]
stop_grace_period: 30s
healthcheck:
test: ["CMD-SHELL", "ps -o args 2>/dev/null | grep -q '[m]essenger:consume scheduler_default' || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
volumes:
jwt_keys:
uploads_public:
uploads_var:
networks:
coolify:
name: coolify
driver: bridge
external: true