diff --git a/Dockerfile b/Dockerfile index 92aeed8..74e74fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,99 +1,112 @@ - # syntax=docker/dockerfile:1.6 +# syntax=docker/dockerfile:1.6 - ARG PHP_VERSION=8.3 - ARG NODE_VERSION=20 +ARG PHP_VERSION=8.3 +ARG NODE_VERSION=20 - ################################################################################ - # Composer dependencies (install vendor/) - ################################################################################ - FROM composer:2 AS vendor +################################################################################ +# Composer dependencies +################################################################################ +FROM composer:2 AS vendor - WORKDIR /var/www/html +WORKDIR /var/www/html - COPY composer.json composer.lock ./ +COPY composer.json composer.lock ./ - RUN composer install \ - --no-dev \ - --no-scripts \ - --no-interaction \ - --prefer-dist \ - --optimize-autoloader \ - --ignore-platform-reqs +RUN composer install \ + --no-dev \ + --no-scripts \ + --no-interaction \ + --prefer-dist \ + --optimize-autoloader \ + --ignore-platform-reqs - COPY . . +COPY . . - ################################################################################ - # Node build stage - compile front-end assets (needs PHP + vendor) - ################################################################################ - FROM node:${NODE_VERSION}-bookworm AS node_builder +################################################################################ +# Node build stage - compile front-end assets (needs PHP + vendor) +################################################################################ +FROM node:${NODE_VERSION}-bookworm AS node_builder - WORKDIR /var/www/html +WORKDIR /var/www/html - RUN apt-get update \ - && apt-get install -y --no-install-recommends php-cli php-mbstring php-xml php-curl php-zip \ - && rm -rf /var/lib/apt/lists/* +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + php-cli \ + php-mbstring \ + php-xml \ + php-curl \ + php-zip \ + && rm -rf /var/lib/apt/lists/* - COPY package.json package-lock.json ./ - RUN npm ci --no-audit --prefer-offline +COPY package.json package-lock.json ./ +RUN npm ci --no-audit --prefer-offline - COPY . . - COPY --from=vendor /var/www/html/vendor ./vendor +COPY . . +COPY --from=vendor /var/www/html/vendor ./vendor - RUN npm run build +RUN npm run build - ################################################################################ - # PHP-FPM runtime image - ################################################################################ - FROM php:${PHP_VERSION}-fpm-bullseye AS app +################################################################################ +# PHP-FPM runtime image +################################################################################ +FROM php:${PHP_VERSION}-fpm-bullseye AS app - ARG UID=1000 - ARG GID=1000 +ARG UID=1000 +ARG GID=1000 - ENV APP_ENV=production \ - APP_DEBUG=false \ - PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 +ENV APP_ENV=production \ + APP_DEBUG=false \ + PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 - WORKDIR /opt/app +WORKDIR /var/www/html - RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - git \ - curl \ - libjpeg62-turbo-dev \ - libpng-dev \ - libfreetype6-dev \ - libzip-dev \ - libonig-dev \ - libicu-dev \ - libxml2-dev \ - libmagickwand-dev \ - unzip \ - nano \ - rsync \ - && docker-php-ext-configure gd --with-freetype --with-jpeg \ - && docker-php-ext-install -j$(nproc) \ - bcmath \ - exif \ - gd \ - intl \ - opcache \ - pcntl \ - pdo_mysql \ - zip \ - && pecl install redis imagick \ - && docker-php-ext-enable redis imagick \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git \ + curl \ + libjpeg62-turbo-dev \ + libpng-dev \ + libfreetype6-dev \ + libzip-dev \ + libonig-dev \ + libicu-dev \ + libxml2-dev \ + libmagickwand-dev \ + unzip \ + nano \ + rsync \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" \ + bcmath \ + exif \ + gd \ + intl \ + opcache \ + pcntl \ + pdo_mysql \ + zip \ + && pecl install redis imagick \ + && docker-php-ext-enable redis imagick \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* - COPY . . - COPY --from=vendor /var/www/html/vendor ./vendor - COPY --from=node_builder /var/www/html/public/build ./public/build +COPY . . +COPY --from=vendor /var/www/html/vendor ./vendor +COPY --from=node_builder /var/www/html/public/build ./public/build - COPY docker/php/php.ini /usr/local/etc/php/conf.d/app.ini - COPY docker/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini - COPY docker/app/entrypoint.sh /usr/local/bin/app-entrypoint - RUN chmod +x /usr/local/bin/app-entrypoint +RUN mkdir -p /opt/app \ + && rsync -a /var/www/html/ /opt/app/ \ + && chown -R www-data:www-data /opt/app - EXPOSE 9000 - ENTRYPOINT ["app-entrypoint"] - CMD ["php-fpm"] +RUN groupmod -o -g "$GID" www-data \ + && usermod -o -u "$UID" -g www-data www-data + +COPY docker/php/php.ini /usr/local/etc/php/conf.d/zz-app.ini +COPY docker/php/opcache.ini /usr/local/etc/php/conf.d/zz-opcache.ini +COPY docker/app/entrypoint.sh /usr/local/bin/app-entrypoint +RUN chmod +x /usr/local/bin/app-entrypoint + +EXPOSE 9000 + +ENTRYPOINT ["app-entrypoint"] +CMD ["php-fpm"] diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml index 9debf5b..4ddcae3 100644 --- a/docker-compose.coolify.yml +++ b/docker-compose.coolify.yml @@ -47,12 +47,23 @@ services: condition: service_healthy redis: condition: service_started + healthcheck: + test: + - CMD + - php + - -r + - "exit(file_exists('/var/www/html/vendor/autoload.php') ? 0 : 1);" + interval: 15s + timeout: 5s + retries: 5 + start_period: 30s restart: unless-stopped web: image: nginx:1.27-alpine depends_on: - - app + app: + condition: service_healthy volumes: - app-code:/var/www/html:ro - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro @@ -78,11 +89,14 @@ services: command: /var/www/html/docs/queue-supervisor/queue-worker.sh default environment: <<: *app-env + SKIP_CODE_SYNC: "1" volumes: - app-code:/var/www/html depends_on: - - app - - redis + app: + condition: service_healthy + redis: + condition: service_started restart: unless-stopped media-storage-worker: @@ -92,11 +106,14 @@ services: <<: *app-env QUEUE_TRIES: 5 QUEUE_SLEEP: 5 + SKIP_CODE_SYNC: "1" volumes: - app-code:/var/www/html depends_on: - - app - - redis + app: + condition: service_healthy + redis: + condition: service_started restart: unless-stopped scheduler: @@ -104,10 +121,12 @@ services: command: php artisan schedule:work environment: <<: *app-env + SKIP_CODE_SYNC: "1" volumes: - app-code:/var/www/html depends_on: - - app + app: + condition: service_healthy restart: unless-stopped horizon: @@ -115,11 +134,14 @@ services: command: /var/www/html/docs/queue-supervisor/horizon.sh environment: <<: *app-env + SKIP_CODE_SYNC: "1" volumes: - app-code:/var/www/html depends_on: - - app - - redis + app: + condition: service_healthy + redis: + condition: service_started restart: unless-stopped redis: @@ -159,4 +181,4 @@ volumes: networks: coolify: - external: true \ No newline at end of file + external: true diff --git a/docker/app/entrypoint.sh b/docker/app/entrypoint.sh index 2999acf..3db581d 100644 --- a/docker/app/entrypoint.sh +++ b/docker/app/entrypoint.sh @@ -1,33 +1,50 @@ #!/usr/bin/env bash - set -euo pipefail APP_SOURCE=${APP_SOURCE:-/opt/app} APP_TARGET=${APP_TARGET:-/var/www/html} APP_USER=${APP_USER:-www-data} APP_GROUP=${APP_GROUP:-www-data} +SKIP_CODE_SYNC=${SKIP_CODE_SYNC:-0} -mkdir -p "${APP_TARGET}" +sync_code() { + if [[ "$SKIP_CODE_SYNC" == "1" ]]; then + return + fi -# Sync the built application from the immutable image into the shared app volume -# while preserving runtime data that lives under storage/. -rsync -a --delete \ - --exclude storage \ - --exclude public/storage \ - --exclude bootstrap/cache \ - --exclude .env \ - "${APP_SOURCE}/" "${APP_TARGET}/" + if [[ ! -d "$APP_TARGET" ]]; then + mkdir -p "$APP_TARGET" + fi -cd "${APP_TARGET}" + rsync -a --delete --omit-dir-times --no-perms --no-owner --no-group \ + --exclude="storage/logs" \ + --exclude="storage/framework/sessions" \ + "$APP_SOURCE"/ "$APP_TARGET"/ || true -mkdir -p storage/framework/{cache,sessions,testing,views} storage/logs bootstrap/cache -chown -R "${APP_USER}:${APP_GROUP}" storage bootstrap/cache || true -find storage -type d -exec chmod 775 {} \; -find storage -type f -exec chmod 664 {} \; -chmod -R ug+rwx bootstrap/cache + chown -R "$APP_USER:$APP_GROUP" "$APP_TARGET" +} -php artisan config:cache --quiet || true -php artisan route:cache --quiet || true -php artisan event:cache --quiet || true +ensure_helper_scripts() { + if compgen -G "$APP_TARGET/docs/queue-supervisor/*.sh" > /dev/null; then + chmod +x "$APP_TARGET"/docs/queue-supervisor/*.sh || true + fi +} +prepare_storage() { + cd "$APP_TARGET" + + if [[ ! -h public/storage ]]; then + php artisan storage:link >/dev/null 2>&1 || true + fi + + if [[ -n "${PENDING_MIGRATIONS:-}" ]]; then + php artisan migrate --force >/dev/null 2>&1 || true + fi +} + +sync_code +ensure_helper_scripts +prepare_storage + +cd "$APP_TARGET" exec "$@"