FROM dunglas/frankenphp:php8.4-alpine AS php WORKDIR /app RUN install-php-extensions \ bcmath \ pdo_pgsql \ opcache \ pcntl \ zip \ intl COPY --from=composer:2 /usr/bin/composer /usr/bin/composer COPY composer.json composer.lock ./ RUN --mount=type=cache,target=/tmp/composer-cache \ --mount=type=secret,id=composer_github_token,required=false \ set -eu; \ if [ -s /run/secrets/composer_github_token ]; then \ export COMPOSER_AUTH="{\"github-oauth\":{\"github.com\":\"$(cat /run/secrets/composer_github_token)\"}}"; \ fi; \ export COMPOSER_CACHE_DIR=/tmp/composer-cache; \ export COMPOSER_MAX_PARALLEL_HTTP=4; \ attempt=1; \ until composer install \ --no-dev \ --no-interaction \ --prefer-dist \ --optimize-autoloader \ --no-scripts; do \ if [ "$attempt" -ge 4 ]; then \ exit 1; \ fi; \ delay=$((attempt * 15)); \ echo "Composer install failed (attempt $attempt/4), retrying in ${delay}s..."; \ sleep "$delay"; \ attempt=$((attempt + 1)); \ done COPY . . RUN composer dump-autoload --optimize FROM node:22-alpine AS frontend WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . COPY --from=php /app/vendor ./vendor RUN npm run build FROM php AS production COPY --from=frontend /app/public/build ./public/build RUN mkdir -p \ storage/app/public \ storage/framework/cache/data \ storage/framework/sessions \ storage/framework/views \ storage/logs \ bootstrap/cache \ && chown -R www-data:www-data storage bootstrap/cache COPY docker/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh EXPOSE 80 ENTRYPOINT ["/entrypoint.sh"]