# Stage 1: Build environment and Composer dependencies FROM php:8.4-fpm AS builder LABEL maintainer="Topaz" # Install system dependencies and PHP extensions for Laravel with MySQL/PostgreSQL support. RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ unzip \ libpq-dev \ libonig-dev \ libssl-dev \ libxml2-dev \ libcurl4-openssl-dev \ libicu-dev \ libzip-dev \ libjpeg-dev \ libpng-dev \ libfreetype6-dev \ && docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install -j$(nproc) \ pdo_mysql \ pdo_pgsql \ pgsql \ opcache \ intl \ zip \ bcmath \ soap \ gd \ pcntl \ && pecl install redis \ && docker-php-ext-enable redis \ && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Set the working directory inside the container WORKDIR /var/www # Copy the entire Laravel application code into the container COPY be/ /var/www # Copy Composer from official image (avoids flaky getcomposer.org in CI) COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer # Install PHP dependencies RUN composer install --no-dev --optimize-autoloader --no-interaction --no-progress --prefer-dist --no-scripts \ && rm -rf bootstrap/cache/services.php bootstrap/cache/packages.php # Stage 1b: Node + Puppeteer for Browsershot PDF generation FROM node:22-bookworm-slim AS node-deps WORKDIR /var/www RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ unzip \ && apt-get clean && rm -rf /var/lib/apt/lists/* COPY be/package.json be/package-lock.json ./ ENV PUPPETEER_SKIP_DOWNLOAD=true \ PUPPETEER_CACHE_DIR=/var/www/.cache/puppeteer RUN npm ci --omit=dev RUN npx puppeteer browsers install chrome-headless-shell # Stage 2: Unified production image with PHP-FPM + Nginx FROM php:8.4-fpm AS base LABEL maintainer="Topaz" # Set system timezone to Asia/Kuala_Lumpur RUN apt-get update && apt-get install -y --no-install-recommends tzdata \ && ln -snf /usr/share/zoneinfo/Asia/Kuala_Lumpur /etc/localtime \ && echo "Asia/Kuala_Lumpur" > /etc/timezone \ && dpkg-reconfigure -f noninteractive tzdata \ && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Install Nginx and all runtime dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ nginx \ libpq-dev \ libicu-dev \ libzip-dev \ libpng-dev \ libjpeg-dev \ libfreetype6-dev \ libfcgi-bin \ procps \ netcat-openbsd \ supervisor \ nano \ curl \ && docker-php-ext-configure gd --with-freetype --with-jpeg \ && docker-php-ext-install gd \ && pecl install redis \ && docker-php-ext-enable redis \ && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Chromium runtime libraries for Puppeteer (Browsershot PDF generation) RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ fonts-liberation \ libasound2 \ libatk-bridge2.0-0 \ libatk1.0-0 \ libcairo2 \ libcups2 \ libdbus-1-3 \ libdrm2 \ libexpat1 \ libfontconfig1 \ libgbm1 \ libglib2.0-0 \ libnspr4 \ libnss3 \ libpango-1.0-0 \ libpangocairo-1.0-0 \ libx11-6 \ libx11-xcb1 \ libxcb1 \ libxcomposite1 \ libxdamage1 \ libxext6 \ libxfixes3 \ libxkbcommon0 \ libxrandr2 \ libxrender1 \ && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* # Download and install php-fpm health check script RUN curl -o /usr/local/bin/php-fpm-healthcheck \ https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/master/php-fpm-healthcheck \ && chmod +x /usr/local/bin/php-fpm-healthcheck # Copy the initialization script COPY be/docker/common/unified/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh # Copy supervisor configuration files COPY be/docker/common/unified/supervisor/supervisord.conf /etc/supervisor/supervisord.conf # Create supervisor conf.d directory RUN mkdir -p /etc/supervisor/conf.d # Use common supervisor and nginx config inside the image. # If you need environment-specific behavior (training vs production), override at runtime # by mounting files into: # - /etc/nginx/nginx.conf # - /etc/supervisor/conf.d/ COPY be/docker/common/unified/supervisor/unified.conf /etc/supervisor/conf.d/unified.conf COPY be/docker/common/unified/nginx/nginx.conf /etc/nginx/nginx.conf # Copy the initial storage structure COPY be/storage /var/www/storage-init # Copy PHP extensions and libraries from the builder stage COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/ COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/ # Use the recommended production PHP configuration RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" # Custom PHP overrides (upload limits, etc.) — official image loads conf.d/*.ini COPY be/docker/common/unified/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini # Enable PHP-FPM status page via separate config file (avoid modifying zz-docker.conf) RUN echo '[www]' > /usr/local/etc/php-fpm.d/zzz-status.conf && \ echo 'pm.status_path = /status' >> /usr/local/etc/php-fpm.d/zzz-status.conf # Copy the application code and dependencies from the build stage first COPY --from=builder /var/www /var/www # Node runtime + Puppeteer for Browsershot (letter PDF generation) COPY --from=node-deps /usr/local/bin/node /usr/local/bin/node COPY --from=node-deps /var/www/node_modules /var/www/node_modules COPY --from=node-deps /var/www/.cache/puppeteer /var/www/.cache/puppeteer ENV PUPPETEER_CACHE_DIR=/var/www/.cache/puppeteer \ BROWSERSHOT_NO_SANDBOX=true \ BROWSERSHOT_NODE_BINARY=/usr/local/bin/node # Set working directory WORKDIR /var/www # Ensure correct permissions RUN chown -R www-data:www-data /var/www /var/www/.cache/puppeteer # Create Nginx log directory RUN mkdir -p /var/log/nginx && chown -R www-data:www-data /var/log/nginx # Run the entrypoint script ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] # Expose port 80 (Nginx) and 9000 (PHP-FPM for health checks) EXPOSE 80 9000 # Start supervisor which manages both Nginx and PHP-FPM CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"] # CI: install dev dependencies and run PHPUnit (build with --target backend-test) FROM builder AS backend-test ENV APP_KEY=base64:2fl3rk3lI6+k9tJ0lZp2aXh6sL9mN4pQ7rT1vW5yZ8A= RUN composer install --no-interaction --prefer-dist --no-scripts \ && ./vendor/bin/phpunit --no-coverage # Default production image target (use --target base for deploy builds) FROM base AS production