From 6bc5a2060666a0f804f70fd06ff6817c9f4ec811 Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 12 Aug 2026 20:20:06 +0200 Subject: [PATCH] Speed up Docker builds with a single image and parallel deps. Build app once for queue/reverb, run Composer and npm in parallel with cache mounts, and skip redundant vite npm ci on restart. --- .dockerignore | 1 + Dockerfile | 46 +++++++++++++++++++++++++++++++++--------- README.md | 2 ++ docker-compose.dev.yml | 4 +++- docker-compose.yml | 22 ++++---------------- 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/.dockerignore b/.dockerignore index e374712..4e4ea8f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -28,5 +28,6 @@ npm-debug.log yarn-error.log tests docs +todo.txt *.md !README.md diff --git a/Dockerfile b/Dockerfile index bb4e702..5fcf5a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,46 @@ # syntax=docker/dockerfile:1 +# --------------------------------------------------------------------------- +# Composer deps (runs in parallel with npm ci) +# --------------------------------------------------------------------------- FROM composer:2 AS vendor WORKDIR /app COPY composer.json composer.lock ./ -RUN composer install \ + +RUN --mount=type=cache,target=/tmp/cache \ + composer install \ --no-dev \ --no-scripts \ --no-autoloader \ --prefer-dist \ --no-interaction -FROM node:22-bookworm AS assets +# --------------------------------------------------------------------------- +# npm ci only (parallel with vendor — does not wait on Composer) +# --------------------------------------------------------------------------- +FROM node:22-bookworm AS npm WORKDIR /app COPY package.json package-lock.json ./ -RUN npm ci -COPY --from=vendor /app/vendor ./vendor -COPY composer.json composer.lock ./ +RUN --mount=type=cache,target=/root/.npm \ + npm ci + +# --------------------------------------------------------------------------- +# Vite production build (needs Flux/Livewire + Laravel pagination views) +# --------------------------------------------------------------------------- +FROM node:22-bookworm AS assets + +WORKDIR /app + +COPY --from=npm /app/node_modules ./node_modules +COPY package.json package-lock.json ./ +COPY --from=vendor /app/vendor/livewire ./vendor/livewire +COPY --from=vendor /app/vendor/laravel/framework/src/Illuminate/Pagination \ + ./vendor/laravel/framework/src/Illuminate/Pagination COPY vite.config.js ./ COPY resources ./resources COPY public ./public @@ -39,8 +59,12 @@ ENV VITE_APP_NAME=$VITE_APP_NAME \ RUN npm run build +# --------------------------------------------------------------------------- +# Runtime image +# --------------------------------------------------------------------------- FROM dunglas/frankenphp:php8.5-bookworm +# Rarely changes — keep early for cache hits RUN install-php-extensions \ pcntl \ pdo_sqlite \ @@ -50,16 +74,21 @@ RUN install-php-extensions \ intl \ opcache -COPY docker/php.ini /usr/local/etc/php/conf.d/99-uploads.ini - COPY --from=composer:2 /usr/bin/composer /usr/bin/composer +COPY docker/php.ini /usr/local/etc/php/conf.d/99-uploads.ini +COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh WORKDIR /app +# Dependency layer (invalidates when lockfiles / vendor change) COPY --from=vendor /app/vendor ./vendor COPY composer.json composer.lock ./ +# Application source (.dockerignore excludes vendor, node_modules, public/build) COPY . . + +# Built frontend assets COPY --from=assets /app/public/build ./public/build RUN composer dump-autoload --optimize --no-dev \ @@ -74,9 +103,6 @@ RUN composer dump-autoload --optimize --no-dev \ bootstrap/cache \ && chown -R www-data:www-data storage bootstrap/cache database -COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh -RUN chmod +x /usr/local/bin/entrypoint.sh - EXPOSE 80 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/README.md b/README.md index a26f8b7..bc0017f 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ sed -i "s|^APP_KEY=.*|APP_KEY=${KEY}|" .env docker compose up --build -d ``` +Compose builds the **app** image once; `queue` and `reverb` reuse `andytranscribe-app:latest` (no triple rebuild). With the [dev overlay](#local-development-hot-reload), skip `--build` for routine PHP/Blade/JS work — the repo is bind-mounted. + On first start the app container will: - create `database/database.sqlite` if needed diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index d6a747f..28128a9 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -39,7 +39,9 @@ services: image: node:22-bookworm container_name: andytranscribe-vite working_dir: /app - command: sh -c "npm ci && npm run dev -- --host 0.0.0.0 --port 5173" + command: > + sh -c "if [ ! -x node_modules/.bin/vite ]; then npm ci; fi; + npm run dev -- --host 0.0.0.0 --port 5173" ports: - "${VITE_HOST_PORT:-5173}:5173" environment: diff --git a/docker-compose.yml b/docker-compose.yml index a11f7c3..c13139e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -70,17 +70,11 @@ services: condition: service_started restart: unless-stopped + # Shares andytranscribe-app:latest — do not declare build: here (avoids rebuilding 3×). + # `docker compose up --build` builds `app` first, then starts these with the tagged image. queue: - build: - context: . - dockerfile: Dockerfile - args: - VITE_APP_NAME: ${VITE_APP_NAME:-AndyTranscribe} - VITE_REVERB_APP_KEY: ${REVERB_APP_KEY:-andytranscribe-key} - VITE_REVERB_HOST: ${VITE_REVERB_HOST:-localhost} - VITE_REVERB_PORT: ${REVERB_HOST_PORT:-8081} - VITE_REVERB_SCHEME: ${VITE_REVERB_SCHEME:-http} image: andytranscribe-app:latest + pull_policy: never container_name: andytranscribe-queue command: - php @@ -107,16 +101,8 @@ services: restart: unless-stopped reverb: - build: - context: . - dockerfile: Dockerfile - args: - VITE_APP_NAME: ${VITE_APP_NAME:-AndyTranscribe} - VITE_REVERB_APP_KEY: ${REVERB_APP_KEY:-andytranscribe-key} - VITE_REVERB_HOST: ${VITE_REVERB_HOST:-localhost} - VITE_REVERB_PORT: ${REVERB_HOST_PORT:-8081} - VITE_REVERB_SCHEME: ${VITE_REVERB_SCHEME:-http} image: andytranscribe-app:latest + pull_policy: never container_name: andytranscribe-reverb command: - php