Compare commits

..

1 Commits

Author SHA1 Message Date
Lance Pioch
60324225a7 docker: enable opcache, production php.ini, and tuned FPM pool
The image previously ran with no opcache (the official php images do not
enable it), no php.ini (defaults), and the stock FPM pool capped at
pm.max_children = 5. Every request recompiled the full framework and the
panel could serve at most five concurrent PHP requests.

- install and enable opcache in the base image, with timestamp validation
  kept on so runtime plugin installs still take effect
- use php.ini-production as the baseline and raise memory_limit to 256M
- tune the www pool (max_children 15, max_requests 500)
- cache Blade views at container start; config/route/event caches are
  deliberately skipped because settings live in .env and plugins register
  providers/routes at runtime
2026-07-15 20:32:42 -04:00
6 changed files with 60 additions and 38 deletions

View File

@@ -84,6 +84,9 @@ RUN mkdir -p /pelican-data/storage /var/run/supervisord \
&& chown -R www-data: /pelican-data .env ./storage ./bootstrap/cache /var/run/supervisord /var/www/html/public/storage \
&& chmod -R 770 /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \
&& chown -R www-data: /usr/local/etc/php/ /usr/local/etc/php-fpm.d/ /var/www/html/composer.json /var/www/html/composer.lock
# Configure PHP and PHP-FPM
COPY docker/php/pelican.ini /usr/local/etc/php/conf.d/zz-pelican.ini
COPY docker/php/pelican-pool.conf /usr/local/etc/php-fpm.d/zz-pelican.conf
# Configure Supervisor
COPY docker/supervisord.conf /etc/supervisord.conf
COPY docker/Caddyfile /etc/caddy/Caddyfile

View File

@@ -5,6 +5,9 @@ FROM --platform=$TARGETOS/$TARGETARCH php:8.5-fpm-alpine
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN install-php-extensions bcmath gd intl zip pcntl pdo_mysql pdo_pgsql bz2
RUN install-php-extensions bcmath gd intl zip opcache pcntl pdo_mysql pdo_pgsql bz2
RUN rm /usr/local/bin/install-php-extensions
# Use the production php.ini shipped with the image as the baseline
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

View File

@@ -5,10 +5,13 @@ FROM --platform=$TARGETOS/$TARGETARCH php:8.5-fpm-alpine AS base
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN install-php-extensions bcmath gd intl zip pcntl pdo_mysql pdo_pgsql bz2
RUN install-php-extensions bcmath gd intl zip opcache pcntl pdo_mysql pdo_pgsql bz2
RUN rm /usr/local/bin/install-php-extensions
# Use the production php.ini shipped with the image as the baseline
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# ================================
# Stage 1-1: Composer Install
# ================================
@@ -90,6 +93,9 @@ RUN mkdir -p /pelican-data/storage /var/run/supervisord \
&& chmod -R 770 /pelican-data ./storage ./bootstrap/cache /var/run/supervisord \
&& chown -R www-data: /usr/local/etc/php/ /usr/local/etc/php-fpm.d/ /var/www/html/composer.json /var/www/html/composer.lock
# Configure PHP and PHP-FPM
COPY docker/php/pelican.ini /usr/local/etc/php/conf.d/zz-pelican.ini
COPY docker/php/pelican-pool.conf /usr/local/etc/php-fpm.d/zz-pelican.conf
# Configure Supervisor
COPY docker/supervisord.conf /etc/supervisord.conf
COPY docker/Caddyfile /etc/caddy/Caddyfile

View File

@@ -4,36 +4,19 @@
# check for .env file or symlink and generate app keys if missing
if [ -f /pelican-data/.env ]; then
echo ".env vars exist."
# load specific env vars from .env used in the entrypoint if they are not already set
for VAR in APP_KEY APP_INSTALLED DB_CONNECTION DB_HOST DB_PORT TRUSTED_PROXIES; do
# load specific env vars from .env used in the entrypoint and they are not already set
for VAR in "APP_KEY" "APP_INSTALLED" "DB_CONNECTION" "DB_HOST" "DB_PORT" "TRUSTED_PROXIES"; do
echo "checking for ${VAR}"
# the container environment takes precedence, matching Laravel's own behavior
eval "CURRENT=\${${VAR}:-}"
if [ -n "${CURRENT}" ]; then
echo "${VAR} already set in environment, skipping"
continue
fi
# match only a real assignment at the start of a line, never comments or
# other variables that merely contain the name
if ! LINE=$(grep -m1 "^${VAR}=" .env); then
echo "didn't find variable to set"
continue
fi
## skip if it looks like it might try to execute code
case "$LINE" in
*'$('*|*'`'*)
echo "var in .env may be executable, skipping"
continue
;;
esac
echo "loading ${VAR} from .env"
# strip quotes and carriage returns so values from quoted or
# Windows-edited .env files export cleanly
export "$(echo "$LINE" | tr -d "\r\"'")"
if (grep "${VAR}" .env | grep -qE "\$\(|=\`|\$#"); then echo "var in .env may be executable or a comment, skipping"; continue; fi
# if the variable is in .env then set it
if (grep -q "${VAR}" .env); then
echo "loading ${VAR} from .env"
export "$(grep "${VAR}" .env | sed 's/"//g')"
continue
fi
## variable wasn't loaded or in the env to set
echo "didn't find variable to set"
done
else
echo ".env vars don't exist."
@@ -43,7 +26,7 @@ else
# manually generate a key because key generate --force fails
if [ -z "${APP_KEY}" ]; then
echo "No key set, Generating key."
APP_KEY="base64:$(head -c 32 /dev/urandom | base64)"
APP_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
echo "APP_KEY=$APP_KEY" > /pelican-data/.env
echo "Generated app key written to .env file"
else
@@ -74,13 +57,8 @@ if [ "${APP_INSTALLED}" = "true" ]; then
echo "using sqlite database"
fi
# run migration, unless disabled (e.g. when running multiple replicas
# against the same database)
if [ "${SKIP_MIGRATIONS:-false}" = "true" ]; then
echo "Skipping migrations (SKIP_MIGRATIONS=true)"
else
php artisan migrate --force
fi
# run migration
php artisan migrate --force
php artisan p:plugin:composer
fi
@@ -88,6 +66,12 @@ fi
echo "Optimizing Filament"
php artisan filament:optimize
# Note: config/route/event caches are intentionally NOT built here. Settings
# live in .env and are edited at runtime, and plugins register providers (and
# routes) at runtime - those caches would freeze them until the next restart.
echo "Caching Blade views"
php artisan view:cache
# default to caddy not starting
export SUPERVISORD_CADDY=false
export CADDY_APP_URL="${APP_URL}"

View File

@@ -0,0 +1,12 @@
; Pelican overrides for the default www pool.
; The image default of pm.max_children = 5 caps the panel at five concurrent
; PHP requests. Sizing assumes roughly 64-128MB per worker; lower
; pm.max_children on very small hosts.
[www]
pm = dynamic
pm.max_children = 15
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
; recycle workers periodically to guard against memory leaks
pm.max_requests = 500

14
docker/php/pelican.ini Normal file
View File

@@ -0,0 +1,14 @@
; Pelican overrides, layered on top of php.ini-production
; Filament recommends at least 256M
memory_limit = 256M
[opcache]
opcache.enable = 1
opcache.memory_consumption = 192
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 30000
; keep timestamp validation enabled so runtime plugin installs pick up
; new and changed files without a container restart
opcache.validate_timestamps = 1
opcache.revalidate_freq = 2