|
| 1 | +# ============================================================================= |
| 2 | +# Stage 1: Build assets (Node + Composer) |
| 3 | +# ============================================================================= |
| 4 | +FROM php:8.2-cli AS builder |
| 5 | + |
| 6 | +# Install system dependencies for building |
| 7 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 8 | + git zip unzip curl \ |
| 9 | + libpng-dev libjpeg62-turbo-dev libfreetype6-dev libzip-dev libicu-dev libxml2-dev \ |
| 10 | + && docker-php-ext-configure gd --with-freetype --with-jpeg \ |
| 11 | + && docker-php-ext-install pdo_mysql bcmath zip intl gd \ |
| 12 | + && apt-get clean && rm -rf /var/lib/apt/lists/* |
| 13 | + |
| 14 | +# Install xmlrpc via pecl |
| 15 | +RUN pecl install channel://pecl.php.net/xmlrpc-1.0.0RC3 && docker-php-ext-enable xmlrpc |
| 16 | + |
| 17 | +# Install composer |
| 18 | +COPY --from=composer/composer:2-bin /composer /usr/bin/composer |
| 19 | + |
| 20 | +# Install Node 18 |
| 21 | +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ |
| 22 | + && apt-get install -y nodejs \ |
| 23 | + && apt-get clean && rm -rf /var/lib/apt/lists/* |
| 24 | + |
| 25 | +WORKDIR /build |
| 26 | + |
| 27 | +# Copy composer files first for layer caching |
| 28 | +COPY composer.json composer.lock ./ |
| 29 | +RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist |
| 30 | + |
| 31 | +# Copy package files for npm layer caching |
| 32 | +COPY package.json package-lock.json ./ |
| 33 | +RUN npm ci --legacy-peer-deps |
| 34 | +RUN npx update-browserslist-db@latest |
| 35 | + |
| 36 | +# Copy all source code |
| 37 | +COPY . . |
| 38 | + |
| 39 | +# Create a minimal .env for artisan commands during build (no DB needed) |
| 40 | +RUN echo "APP_KEY=base64:$(openssl rand -base64 32)" > .env && \ |
| 41 | + echo "APP_ENV=production" >> .env |
| 42 | + |
| 43 | +# Finish composer autoload (skip post-autoload scripts that need DB/app context) |
| 44 | +ENV COMPOSER_ALLOW_SUPERUSER=1 |
| 45 | +RUN composer dump-autoload --optimize --no-dev --no-scripts |
| 46 | + |
| 47 | +# Run package discovery manually |
| 48 | +RUN php artisan package:discover --ansi || true |
| 49 | + |
| 50 | +# Build frontend assets |
| 51 | +# The webpack.mix.js runs `php artisan lang:js` via WebpackShellPlugin, but that |
| 52 | +# needs the full app bootstrapped. Generate translations first, then build. |
| 53 | +RUN php artisan lang:js --no-lib resources/js/translations.js 2>/dev/null || true |
| 54 | +RUN npm run production |
| 55 | + |
| 56 | +# Generate swagger docs (non-fatal if it fails) |
| 57 | +RUN php artisan l5-swagger:generate 2>/dev/null || true |
| 58 | + |
| 59 | +# Remove build .env (real env comes from Fly secrets at runtime) |
| 60 | +RUN rm -f .env |
| 61 | + |
| 62 | +# ============================================================================= |
| 63 | +# Stage 2: Production image (Nginx + PHP-FPM + Supervisord + Cron) |
| 64 | +# ============================================================================= |
| 65 | +FROM php:8.2-fpm |
| 66 | + |
| 67 | +# Install runtime dependencies |
| 68 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 69 | + nginx \ |
| 70 | + supervisor \ |
| 71 | + cron \ |
| 72 | + gettext-base \ |
| 73 | + libpng-dev libjpeg62-turbo-dev libfreetype6-dev libzip-dev libicu-dev libxml2-dev \ |
| 74 | + && docker-php-ext-configure gd --with-freetype --with-jpeg \ |
| 75 | + && docker-php-ext-install pdo_mysql bcmath zip intl gd \ |
| 76 | + && apt-get clean && rm -rf /var/lib/apt/lists/* |
| 77 | + |
| 78 | +# Install xmlrpc |
| 79 | +RUN pecl install channel://pecl.php.net/xmlrpc-1.0.0RC3 && docker-php-ext-enable xmlrpc |
| 80 | + |
| 81 | +# Configure PHP-FPM to use unix socket |
| 82 | +RUN sed -i 's|listen = 127.0.0.1:9000|listen = /var/run/php-fpm.sock|' /usr/local/etc/php-fpm.d/www.conf && \ |
| 83 | + sed -i 's|;listen.owner = www-data|listen.owner = www-data|' /usr/local/etc/php-fpm.d/www.conf && \ |
| 84 | + sed -i 's|;listen.group = www-data|listen.group = www-data|' /usr/local/etc/php-fpm.d/www.conf && \ |
| 85 | + sed -i 's|;listen.mode = 0660|listen.mode = 0660|' /usr/local/etc/php-fpm.d/www.conf && \ |
| 86 | + rm -f /usr/local/etc/php-fpm.d/zz-docker.conf |
| 87 | + |
| 88 | +# PHP production settings |
| 89 | +RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/conf.d/php.ini && \ |
| 90 | + echo "upload_max_filesize = 100M" >> /usr/local/etc/php/conf.d/php.ini && \ |
| 91 | + echo "post_max_size = 100M" >> /usr/local/etc/php/conf.d/php.ini && \ |
| 92 | + echo "memory_limit = 256M" >> /usr/local/etc/php/conf.d/php.ini |
| 93 | + |
| 94 | +# Copy nginx config |
| 95 | +COPY docker/nginx-fly.conf /etc/nginx/nginx.conf |
| 96 | + |
| 97 | +# Copy supervisord config |
| 98 | +COPY docker/supervisord-fly.conf /etc/supervisor/conf.d/supervisord.conf |
| 99 | + |
| 100 | +# Set up cron for Laravel scheduler (every minute) |
| 101 | +RUN echo "* * * * * cd /var/www && php artisan schedule:run >> /dev/null 2>&1" | crontab - |
| 102 | + |
| 103 | +# Set working directory |
| 104 | +WORKDIR /var/www |
| 105 | + |
| 106 | +# Copy application from builder |
| 107 | +COPY --from=builder --chown=www-data:www-data /build /var/www |
| 108 | + |
| 109 | +# Remove dev files not needed in production |
| 110 | +RUN rm -rf node_modules tests .git .github |
| 111 | + |
| 112 | +# Ensure storage and cache directories exist |
| 113 | +RUN mkdir -p storage/framework/{sessions,views,cache/data} \ |
| 114 | + storage/logs bootstrap/cache \ |
| 115 | + && chown -R www-data:www-data storage bootstrap/cache |
| 116 | + |
| 117 | +# Copy startup script |
| 118 | +COPY .fly/scripts/startup.sh /usr/local/bin/startup.sh |
| 119 | +RUN chmod +x /usr/local/bin/startup.sh |
| 120 | + |
| 121 | +EXPOSE 80 |
| 122 | + |
| 123 | +CMD ["/usr/local/bin/startup.sh"] |
0 commit comments