Compare commits

..

2 Commits

Author SHA1 Message Date
Lance Pioch
d7b6914170 docker: strip quotes and carriage returns when loading .env vars
A .env edited on Windows leaves a trailing \r in exported values, which
breaks the APP_INSTALLED comparison and the DB_HOST wait, and single
quoted values kept their quotes. Strip both along with double quotes.
2026-07-18 10:25:53 -04:00
Lance Pioch
717370ed48 docker: harden entrypoint .env parsing and key generation
The .env loader used unanchored greps, so a comment containing a variable
name, or another variable that merely contains the name (e.g.
SOME_APP_KEY_BACKUP), matched too. With multiple matching lines the
export received a multiline string, failed, and 'ash -e' killed the
container at boot.

- match only real assignments anchored at the start of a line
- let container environment variables take precedence over .env for the
  entrypoint's own vars, matching Laravel's precedence and the comment's
  stated intent
- generate APP_KEY in the standard base64: format (32 random bytes)
  instead of a hand-rolled alphanumeric string
- add a SKIP_MIGRATIONS escape hatch for multi-replica deployments where
  automatic 'migrate --force' on every boot would race
2026-07-15 20:39:12 -04:00
6 changed files with 37 additions and 59 deletions

View File

@@ -84,9 +84,6 @@ 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,9 +5,6 @@ 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 opcache pcntl pdo_mysql pdo_pgsql bz2
RUN install-php-extensions bcmath gd intl zip 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,13 +5,10 @@ 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 opcache pcntl pdo_mysql pdo_pgsql bz2
RUN install-php-extensions bcmath gd intl zip 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
# ================================
@@ -93,9 +90,6 @@ 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,19 +4,36 @@
# 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 and 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 if 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}"
## skip if it looks like it might try to execute code
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')"
# 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
## variable wasn't loaded or in the env to set
echo "didn't find variable to set"
# 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\"'")"
done
else
echo ".env vars don't exist."
@@ -26,7 +43,7 @@ else
# manually generate a key because key generate --force fails
if [ -z "${APP_KEY}" ]; then
echo "No key set, Generating key."
APP_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
APP_KEY="base64:$(head -c 32 /dev/urandom | base64)"
echo "APP_KEY=$APP_KEY" > /pelican-data/.env
echo "Generated app key written to .env file"
else
@@ -57,8 +74,13 @@ if [ "${APP_INSTALLED}" = "true" ]; then
echo "using sqlite database"
fi
# run migration
php artisan migrate --force
# 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
php artisan p:plugin:composer
fi
@@ -66,12 +88,6 @@ 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

@@ -1,12 +0,0 @@
; 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

View File

@@ -1,14 +0,0 @@
; 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