Files
landing-api/Dockerfile
Josep Piguillem 3897113313 Initial commit: Pireon Admin — headless admin panel and public API
Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:55:27 +02:00

123 lines
4.2 KiB
Docker

# syntax=docker/dockerfile:1
###############################################################################
# Base
#
# Both stages share it, so dependencies are resolved against exactly the PHP
# that will run them — Filament requires ext-intl, and a composer image that
# lacks it would either refuse to install or, worse, install something the
# runtime cannot use.
###############################################################################
# PHP 8.4, not the 8.3 that composer.json's `^8.3` suggests: the lock file
# resolved Symfony 8, which requires >=8.4.1. Building on 8.3 fails at
# `composer install`, which is a much better place to find out than a deploy.
FROM dunglas/frankenphp:1-php8.4 AS base
WORKDIR /app
# gd with WebP is not optional: it is what App\Services\Media\ImageProcessor
# derives every responsive version with. exif is what stops a photo taken
# sideways staying sideways. pcntl lets the queue worker shut down cleanly
# instead of being killed mid-job.
RUN install-php-extensions \
pdo_pgsql \
gd \
exif \
intl \
zip \
pcntl \
opcache \
&& apt-get update \
&& apt-get install -y --no-install-recommends postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# In base rather than only in runtime, so the test stage runs under the same
# PHP settings production does — a memory limit that is only set in production
# is a memory limit nothing has tried.
COPY docker/php.ini /usr/local/etc/php/conf.d/app.ini
###############################################################################
# Dependencies
###############################################################################
FROM base AS vendor
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
# `--no-scripts` because the post-install hooks boot the application, and the
# application cannot boot before its code is here. They are run explicitly in
# the runtime stage instead, where everything exists.
RUN composer install \
--no-dev \
--no-scripts \
--no-autoloader \
--prefer-dist \
--no-interaction
COPY . .
RUN composer dump-autoload --no-dev --optimize --classmap-authoritative
###############################################################################
# Test
#
# The production image is built with --no-dev, which leaves out the test runner
# itself. Verifying this application against Postgres therefore needs its own
# stage — see docker-compose.test.yml, and the JSON-column queries it exists to
# exercise.
###############################################################################
FROM base AS test
# Only the dev dependencies need this one, so production stays without it.
RUN install-php-extensions bcmath
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
RUN composer install --no-scripts --no-autoloader --prefer-dist --no-interaction
COPY . .
# An empty .env: the file is excluded from the build context on purpose, and
# without it every single test emits a "failed to open stream" warning that
# buries the real output.
RUN touch .env \
&& composer dump-autoload --optimize \
&& php artisan package:discover --ansi
###############################################################################
# Runtime
###############################################################################
FROM base AS runtime
LABEL org.opencontainers.image.title="Pireon Admin" \
org.opencontainers.image.description="Admin panel and REST API for the collective"
COPY docker/Caddyfile /etc/caddy/Caddyfile
COPY --from=vendor /app/vendor ./vendor
COPY . .
COPY docker/entrypoint.sh /usr/local/bin/entrypoint
RUN chmod +x /usr/local/bin/entrypoint
# Filament serves its own CSS and JS from public/; publishing them at build
# time means the running container never writes to public/.
#
# There is deliberately no Node stage: the only `@vite` directive left is in
# Laravel's default welcome page, which this application redirects away from.
RUN php artisan package:discover --ansi \
&& php artisan filament:assets
RUN chown -R www-data:www-data storage bootstrap/cache
ENV SERVER_NAME=:80
EXPOSE 80
ENTRYPOINT ["entrypoint"]
CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile"]