128 lines
3 KiB
Docker
128 lines
3 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
# ============================================================
|
|
# Stage 1: Build frontend assets
|
|
# ============================================================
|
|
FROM node:20-alpine AS frontend
|
|
|
|
WORKDIR /app
|
|
|
|
COPY package.json package-lock.json vite.config.js ./
|
|
COPY resources/ resources/
|
|
|
|
RUN npm ci --no-audit --no-fund
|
|
RUN npm run build
|
|
|
|
# ============================================================
|
|
# Stage 2: Runtime (FrankenPHP)
|
|
# ============================================================
|
|
FROM dunglas/frankenphp:1.1-php8.3-alpine AS runtime
|
|
|
|
RUN apk add --no-cache \
|
|
git \
|
|
postgresql-client \
|
|
curl
|
|
|
|
RUN install-php-extensions \
|
|
pdo_pgsql \
|
|
redis \
|
|
opcache \
|
|
zip \
|
|
gd \
|
|
intl
|
|
|
|
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
|
|
|
WORKDIR /app
|
|
|
|
ENV APP_ENV=production \
|
|
APP_DEBUG=false \
|
|
LOG_CHANNEL=stack \
|
|
LOG_LEVEL=warning \
|
|
DB_CONNECTION=pgsql \
|
|
DB_HOST=db \
|
|
DB_PORT=5432 \
|
|
REDIS_HOST=redis \
|
|
REDIS_PORT=6379 \
|
|
CACHE_STORE=redis \
|
|
QUEUE_CONNECTION=redis \
|
|
SESSION_DRIVER=redis \
|
|
BROADCAST_CONNECTION=log \
|
|
MAIL_MAILER=log
|
|
|
|
# Copy only the files composer needs before install, so the composer layer stays
|
|
# cached when application source changes. packages/ is required because composer.json
|
|
# declares it as a path repository.
|
|
COPY composer.json composer.lock ./
|
|
COPY packages/ packages/
|
|
|
|
# Skip post-autoload scripts (package:discover) during build — they need a runtime
|
|
# Laravel boot which fails without proper env. Discovery happens at runtime via
|
|
# start-prod.sh. --classmap-authoritative implies --optimize-autoloader.
|
|
RUN composer install --no-dev --no-interaction --prefer-dist --classmap-authoritative --no-scripts
|
|
|
|
COPY . .
|
|
COPY --from=frontend /app/public/build /app/public/build
|
|
|
|
RUN chown -R www-data:www-data /app/storage /app/bootstrap/cache
|
|
|
|
RUN cat > /etc/caddy/Caddyfile <<'EOF'
|
|
{
|
|
frankenphp
|
|
order php_server before file_server
|
|
}
|
|
|
|
:8000 {
|
|
root * /app/public
|
|
|
|
php_server {
|
|
index index.php
|
|
}
|
|
|
|
encode gzip zstd
|
|
|
|
file_server
|
|
|
|
header {
|
|
X-Frame-Options "SAMEORIGIN"
|
|
X-Content-Type-Options "nosniff"
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
}
|
|
}
|
|
EOF
|
|
|
|
EXPOSE 8000
|
|
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD curl -fsS http://localhost:8000/up || exit 1
|
|
|
|
RUN cat > /start-prod.sh <<'EOF'
|
|
#!/bin/sh
|
|
set -e
|
|
|
|
echo "Waiting for PostgreSQL at ${DB_HOST}:${DB_PORT}..."
|
|
for i in $(seq 1 60); do
|
|
if pg_isready -h "${DB_HOST}" -p "${DB_PORT}" -q; then
|
|
echo "PostgreSQL is ready."
|
|
break
|
|
fi
|
|
if [ "$i" = "60" ]; then
|
|
echo "Timed out waiting for PostgreSQL after 60s." >&2
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
php artisan package:discover --ansi
|
|
php artisan config:cache
|
|
php artisan route:cache
|
|
php artisan view:cache
|
|
|
|
php artisan migrate --force
|
|
|
|
exec frankenphp run --config /etc/caddy/Caddyfile
|
|
EOF
|
|
|
|
RUN chmod +x /start-prod.sh
|
|
|
|
CMD ["/start-prod.sh"]
|