# =========================================================================== # docker-laravel — Multi-version PHP + Nginx image for Laravel # # Build args: # PHP_VERSION — 7.4 | 8.0 | 8.1 | 8.2 | 8.3 | 8.4 | 8.5 (default: 8.4) # NODE_MAJOR — Node.js major version (default: 22) # =========================================================================== ARG PHP_VERSION=8.4 FROM php:${PHP_VERSION}-fpm ARG PHP_VERSION LABEL maintainer="docker-laravel" LABEL description="Laravel-optimized PHP-FPM + Nginx image" LABEL php.version="${PHP_VERSION}" WORKDIR /var/www/html ENV DEBIAN_FRONTEND=noninteractive ENV TZ=UTC RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # --------------------------------------------------------------------------- # System dependencies (single layer) # --------------------------------------------------------------------------- RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg gosu curl wget ca-certificates zip unzip git \ supervisor sqlite3 libcap2-bin python3 pkg-config \ # GD libfreetype6-dev libjpeg62-turbo-dev libpng-dev libwebp-dev \ # PHP extension libs libonig-dev libxml2-dev libzip-dev libicu-dev libcurl4-openssl-dev \ # PostgreSQL libpq-dev \ # ImageMagick libmagickwand-dev \ # Nginx + headers-more module nginx libnginx-mod-http-headers-more-filter \ # Database clients default-mysql-client \ # Ghostscript (PDF rendering) ghostscript \ && apt-get clean && rm -rf /var/lib/apt/lists/* # --------------------------------------------------------------------------- # PHP extensions # --------------------------------------------------------------------------- # GD configure flags changed between PHP 7.x and 8.0 RUN PHP_MAJOR=$(php -r "echo PHP_MAJOR_VERSION;") && \ if [ "$PHP_MAJOR" -ge 8 ]; then \ docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp; \ else \ docker-php-ext-configure gd --with-freetype-dir=/usr --with-jpeg-dir=/usr --with-webp-dir=/usr; \ fi && \ docker-php-ext-install -j$(nproc) \ pdo_mysql \ pdo_pgsql \ mysqli \ mbstring \ exif \ pcntl \ bcmath \ gd \ sockets \ zip \ xml \ soap \ intl \ opcache # PECL extensions (fail gracefully for bleeding-edge PHP) RUN pecl install redis && docker-php-ext-enable redis || echo "NOTICE: redis unavailable for PHP ${PHP_VERSION}" RUN pecl install ev && docker-php-ext-enable ev || echo "NOTICE: ev unavailable for PHP ${PHP_VERSION}" RUN pecl install igbinary && docker-php-ext-enable igbinary || echo "NOTICE: igbinary unavailable for PHP ${PHP_VERSION}" RUN pecl install imagick && docker-php-ext-enable imagick || echo "NOTICE: imagick unavailable for PHP ${PHP_VERSION}" # --------------------------------------------------------------------------- # OPcache configuration (JIT enabled automatically for PHP 8.0+) # --------------------------------------------------------------------------- COPY config/opcache.ini /usr/local/etc/php/conf.d/opcache.ini RUN PHP_MAJOR=$(php -r "echo PHP_MAJOR_VERSION;") && \ if [ "$PHP_MAJOR" -ge 8 ]; then \ { echo ""; echo "; JIT (PHP 8.0+)"; echo "opcache.jit=1255"; echo "opcache.jit_buffer_size=128M"; } \ >> /usr/local/etc/php/conf.d/opcache.ini; \ echo "JIT enabled for PHP $(php -r 'echo PHP_VERSION;')"; \ else \ echo "JIT skipped (PHP $(php -r 'echo PHP_VERSION;') < 8.0)"; \ fi # ImageMagick PDF policy (for spatie/pdf-to-image etc.) RUN IMGK_CONF=$(find /etc/ImageMagick* -name policy.xml 2>/dev/null | head -1) && \ if [ -n "$IMGK_CONF" ]; then \ sed -i 's///g' "$IMGK_CONF" && \ sed -i '/<\/policymap>/i\ ' "$IMGK_CONF"; \ fi # Custom PHP configuration COPY config/php.ini /usr/local/etc/php/conf.d/99-custom.ini # --------------------------------------------------------------------------- # Composer # --------------------------------------------------------------------------- COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer # --------------------------------------------------------------------------- # Node.js + npm # --------------------------------------------------------------------------- ARG NODE_MAJOR=22 RUN mkdir -p /etc/apt/keyrings && \ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" \ > /etc/apt/sources.list.d/nodesource.list && \ apt-get update && apt-get install -y --no-install-recommends nodejs && \ rm -rf /var/lib/apt/lists/* # --------------------------------------------------------------------------- # Image optimization tools (spatie/image-optimizer) + ffmpeg # --------------------------------------------------------------------------- RUN apt-get update && apt-get install -y --no-install-recommends \ optipng pngquant gifsicle webp libavif-bin ffmpeg \ nano procps net-tools \ && npm install -g svgo \ && apt-get clean && rm -rf /var/lib/apt/lists/* # --------------------------------------------------------------------------- # Users & directories # --------------------------------------------------------------------------- RUN usermod -u 1000 www-data && groupmod -g 1000 www-data # PsySH config for artisan tinker RUN mkdir -p /var/www/.config/psysh && chown -R www-data:www-data /var/www/.config # --------------------------------------------------------------------------- # Nginx configuration # --------------------------------------------------------------------------- COPY config/nginx.conf /etc/nginx/nginx.conf COPY config/default.conf /etc/nginx/sites-available/default RUN rm -f /etc/nginx/sites-enabled/default && \ ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default && \ rm -f /etc/nginx/conf.d/default.conf # --------------------------------------------------------------------------- # Supervisor configuration # conf.d/ — core programs (php-fpm, nginx), override via volume mount # laravel.d/ — generated at boot from ENABLE_* env vars # custom.d/ — mount your own .conf files here # --------------------------------------------------------------------------- COPY config/supervisord.conf /etc/supervisor/supervisord.conf COPY config/supervisor/php-fpm.conf /etc/supervisor/conf.d/php-fpm.conf COPY config/supervisor/nginx.conf /etc/supervisor/conf.d/nginx.conf RUN mkdir -p /etc/supervisor/conf.d /etc/supervisor/laravel.d /etc/supervisor/custom.d \ /var/log/supervisor /var/log/nginx /var/log/php # --------------------------------------------------------------------------- # Startup script # --------------------------------------------------------------------------- COPY scripts/start-container /usr/local/bin/start-container RUN chmod +x /usr/local/bin/start-container # Composer cache RUN mkdir -p /.composer && chmod 0777 /.composer # --------------------------------------------------------------------------- # Environment variables for optional Laravel services # Set to "true" to enable at container start # --------------------------------------------------------------------------- ENV ENABLE_QUEUE=false ENV ENABLE_SCHEDULER=false ENV ENABLE_HORIZON=false # storage/ + bootstrap/cache/ ownership and writable-bit fixes — runs # every container start, idempotent. Set to "0" to opt out. ENV ENABLE_LARAVEL_PERMS=1 # Self-signed certs are normal for in-cluster MySQL on a private docker # network; the client still negotiates TLS but skips chain verification. # Set to "ON" if you've mounted a real CA or run against an external host. ENV MYSQL_CLIENT_VERIFY=OFF EXPOSE 80 ENTRYPOINT ["start-container"]