services: php-api-stack: image: kariricode/php-api-stack:latest container_name: php-api-stack restart: unless-stopped env_file: - .env ports: # Expose the application port to the host - "${APP_PORT:-8089}:80" # Expose the internal redis port to the host if REDIS_HOST_PORT is set - "${REDIS_HOST_PORT:-6379}:6379" expose: # Expose PHP-FPM metrics port internally for Prometheus - "${METRICS_PHP_FPM_PORT:-9000}" volumes: # Application code - ./app:/var/www/html:rw,cached # Persistent storage - php_sessions:/var/lib/php/sessions - redis_data:/var/lib/redis # Logs (optional - comment out to use container logs) - ./logs/nginx:/var/log/nginx:rw - ./logs/php:/var/log/php:rw - ./logs/redis:/var/log/redis:rw # - ./logs/supervisor:/var/log/supervisor:rw # Custom configurations (optional) - ./example/config/nginx/conf.d:/etc/nginx/conf.d:ro # - ./example/config/php/conf.d:/usr/local/etc/php/conf.d:ro networks: - app-network - db-network healthcheck: test: ["CMD", "curl", "-f", "http://localhost/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s deploy: resources: limits: cpus: "${CONTAINER_CPU_LIMIT:-2}" memory: "${CONTAINER_MEMORY_LIMIT:-1G}" reservations: cpus: "${CONTAINER_CPU_RESERVATION:-0.5}" memory: "${CONTAINER_MEMORY_RESERVATION:-512M}" labels: - "com.kariricode.description=PHP API Stack with Nginx, Redis, and Symfony" - "com.kariricode.version=latest" - "com.kariricode.environment=${APP_ENV:-production}" # Security options security_opt: - no-new-privileges:true # Capabilities (minimal required) cap_drop: - ALL cap_add: - CHOWN - DAC_OVERRIDE - SETGID - SETUID - NET_BIND_SERVICE - FOWNER # Optional: External MySQL Database mysql: image: mysql:8.0 container_name: php-api-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root_secret} MYSQL_DATABASE: ${DB_DATABASE:-api_db} MYSQL_USER: ${DB_USERNAME:-api_user} MYSQL_PASSWORD: ${DB_PASSWORD:-api_password} volumes: - mysql_data:/var/lib/mysql - ./example/config/mysql/my.cnf:/etc/mysql/conf.d/custom.cnf:ro ports: - "${DB_PORT:-3306}:3306" networks: - db-network profiles: - db healthcheck: test: [ "CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:-root_secret}", ] interval: 30s timeout: 10s retries: 5 start_period: 30s deploy: resources: limits: cpus: "1" memory: 1G # Optional: Nginx as Load Balancer (for scaling) nginx-lb: image: nginx:alpine container_name: php-api-nginx-lb restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./example/config/nginx-lb/nginx.conf:/etc/nginx/nginx.conf:ro - ./example/config/nginx-lb/conf.d:/etc/nginx/conf.d:ro - ./certs:/etc/nginx/certs:ro networks: - app-network depends_on: - php-api-stack profiles: - loadbalancer # Optional: Monitoring with Prometheus prometheus: image: prom/prometheus:latest container_name: php-api-prometheus restart: unless-stopped volumes: - ./example/config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus_data:/prometheus command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.path=/prometheus" - "--web.console.libraries=/usr/share/prometheus/console_libraries" - "--web.console.templates=/usr/share/prometheus/consoles" ports: - "${PROMETHEUS_PORT:-9091}:9090" networks: - monitoring - app-network profiles: - monitoring # Optional: Grafana for visualization grafana: image: grafana/grafana:latest container_name: php-api-grafana restart: unless-stopped environment: - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin} - GF_USERS_ALLOW_SIGN_UP=false volumes: - grafana_data:/var/lib/grafana - ./example/config/grafana/provisioning:/etc/grafana/provisioning:ro ports: - "${GRAFANA_PORT:-3000}:3000" networks: - monitoring depends_on: - prometheus profiles: - monitoring nginx-exporter: image: nginx/nginx-prometheus-exporter:0.11.0 container_name: php-api-nginx-exporter restart: unless-stopped command: - -nginx.scrape-uri=http://php-api-stack/nginx_status - -nginx.retries=10 - -web.listen-address=:9113 - -web.telemetry-path=/metrics networks: - app-network depends_on: - php-api-stack profiles: - monitoring php-fpm-exporter: image: hipages/php-fpm_exporter:latest container_name: php-api-phpfpm-exporter restart: unless-stopped environment: PHP_FPM_SCRAPE_URI: "tcp://php-api-stack:${METRICS_PHP_FPM_PORT:-9000}/status" networks: - app-network depends_on: - php-api-stack profiles: - monitoring redis-exporter: image: oliver006/redis_exporter:latest container_name: php-api-redis-exporter restart: unless-stopped env_file: # Load variables from .env file to get credentials - .env environment: # Connect to the service name and internal port 6379 - REDIS_ADDR=tcp://php-api-stack:6379 # Provide password for Redis authentication (read from .env file) # Defaults to empty if not set, allowing connection to Redis without a password - REDIS_PASSWORD=${REDIS_PASSWORD:-} networks: - app-network depends_on: - php-api-stack profiles: - monitoring networks: app-network: driver: bridge ipam: config: - subnet: 172.20.0.0/16 db-network: driver: bridge ipam: config: - subnet: 172.21.0.0/16 monitoring: driver: bridge ipam: config: - subnet: 172.22.0.0/16 volumes: php_sessions: driver: local redis_data: driver: local mysql_data: driver: local prometheus_data: driver: local grafana_data: driver: local