#!/usr/bin/env bash set -euo pipefail echo "==========================================" echo " docker-laravel container starting" echo "==========================================" echo "Date: $(date)" echo "Hostname: $(hostname)" echo "PHP: $(php -r 'echo PHP_VERSION;')" echo "Node: $(node --version 2>/dev/null || echo 'n/a')" echo "Composer: $(composer --version --no-ansi 2>/dev/null | head -1 || echo 'n/a')" echo "==========================================" start_ts=$(date +%s) # --------------------------------------------------------------------------- # 1) Writable directories # --------------------------------------------------------------------------- echo "[1/5] Preparing writable dirs..." mkdir -p /.composer && chmod 0777 /.composer 2>/dev/null || true if command -v git >/dev/null 2>&1; then if git config --system --get-all safe.directory 2>/dev/null | grep -Fxq "/var/www/html"; then echo " git safe.directory already includes /var/www/html" else git config --system --add safe.directory /var/www/html 2>/dev/null || true echo " Added git safe.directory: /var/www/html" fi fi # ENABLE_LARAVEL_PERMS defaults to 1 — set to 0 to opt out (e.g. when # you manage perms yourself with a custom entrypoint or volume init). # The fix is idempotent and cheap on already-correct trees, so leaving # it on is the right default. if [ "${ENABLE_LARAVEL_PERMS:-1}" != "0" ]; then echo " Applying Laravel writable-dir fixes (ENABLE_LARAVEL_PERMS=${ENABLE_LARAVEL_PERMS:-1})" APP_ROOT="/var/www/html" if [ -d "$APP_ROOT/storage" ] || [ -d "$APP_ROOT/bootstrap/cache" ]; then # Pre-create the subdirs that ship empty in fresh Laravel repos # plus the ones common Blax packages need (workkit:db:backup writes # here). Doing it once at boot — as root — means whichever artisan # command runs first never creates them owned by the wrong user. mkdir -p \ "$APP_ROOT/storage/app/public" \ "$APP_ROOT/storage/app/private" \ "$APP_ROOT/storage/framework/cache/data" \ "$APP_ROOT/storage/framework/sessions" \ "$APP_ROOT/storage/framework/views" \ "$APP_ROOT/storage/framework/testing" \ "$APP_ROOT/storage/logs" \ "$APP_ROOT/storage/backups" \ "$APP_ROOT/bootstrap/cache" \ 2>/dev/null || true # Recursive chown so any stray files written earlier as the wrong # user (root, nobody, …) get repaired. chown is fast on already- # correct trees — pure stat, no I/O writes. chown -R www-data:www-data "$APP_ROOT/storage" "$APP_ROOT/bootstrap/cache" 2>/dev/null || true # ug+rwX gives both owner and group write; capital X only adds +x # on directories. SGID on the dirs (g+s) makes new files inherit # the www-data group — so a file written by a sidecar container # running as a different UID still ends up group-writable. chmod -R ug+rwX "$APP_ROOT/storage" "$APP_ROOT/bootstrap/cache" 2>/dev/null || true find "$APP_ROOT/storage" "$APP_ROOT/bootstrap/cache" -type d -exec chmod g+s {} + 2>/dev/null || true else echo " WARN: $APP_ROOT/storage and $APP_ROOT/bootstrap/cache do not exist (volume not mounted yet?)" fi else echo " Skipping Laravel writable-dir fixes (ENABLE_LARAVEL_PERMS=0)" fi mkdir -p /var/log/supervisor /var/log/nginx /var/log/php # --------------------------------------------------------------------------- # 1b) MySQL/MariaDB client defaults # --------------------------------------------------------------------------- # Self-hosted Laravel stacks typically talk to a MySQL container on a # private docker network. Modern mysql/mariadb server images ship a # self-signed cert by default, and modern clients auto-enable # ssl-verify-server-cert when a password is passed on argv (as Laravel's # `php artisan db` does) — which then fails with # "TLS/SSL error: self-signed certificate in certificate chain". # # Disabling verification here keeps the connection encrypted (the client # still negotiates TLS) but skips the chain check — the right tradeoff # for in-cluster traffic where the network is the trust boundary. # Override per host: set MYSQL_CLIENT_VERIFY=ON, or mount your own # /etc/mysql/conf.d/*.cnf with stricter settings (later files override # earlier ones in lexical order). mkdir -p /etc/mysql/conf.d cat > /etc/mysql/conf.d/00-laravel-client.cnf </dev/null || true if [ "${ENABLE_QUEUE:-false}" = "true" ]; then echo " + queue worker enabled" cat > "${LARAVEL_D}/queue.conf" <<'CONF' [program:queue] command=/usr/local/bin/php -d variables_order=EGPCS /var/www/html/artisan queue:work --tries=3 --sleep=5 --timeout=600 --max-jobs=500 --max-time=3600 autostart=true autorestart=true user=www-data priority=20 startsecs=5 stdout_logfile=/proc/1/fd/1 stdout_logfile_maxbytes=0 stderr_logfile=/proc/1/fd/2 stderr_logfile_maxbytes=0 CONF fi if [ "${ENABLE_SCHEDULER:-false}" = "true" ]; then echo " + scheduler enabled" cat > "${LARAVEL_D}/scheduler.conf" <<'CONF' [program:scheduler] command=/usr/local/bin/php -d variables_order=EGPCS /var/www/html/artisan schedule:work autostart=true autorestart=true user=www-data priority=20 startsecs=5 stdout_logfile=/proc/1/fd/1 stdout_logfile_maxbytes=0 stderr_logfile=/proc/1/fd/2 stderr_logfile_maxbytes=0 CONF fi if [ "${ENABLE_HORIZON:-false}" = "true" ]; then echo " + Horizon enabled" cat > "${LARAVEL_D}/horizon.conf" <<'CONF' [program:horizon] command=/usr/local/bin/php -d variables_order=EGPCS /var/www/html/artisan horizon autostart=true autorestart=true user=www-data priority=20 startsecs=5 stdout_logfile=/proc/1/fd/1 stdout_logfile_maxbytes=0 stderr_logfile=/proc/1/fd/2 stderr_logfile_maxbytes=0 CONF fi # Report custom programs CUSTOM_COUNT=$(find /etc/supervisor/custom.d/ -name '*.conf' 2>/dev/null | wc -l) if [ "$CUSTOM_COUNT" -gt 0 ]; then echo " Custom programs (custom.d/):" for f in /etc/supervisor/custom.d/*.conf; do [ -f "$f" ] && echo " $(basename "$f")" done fi # --------------------------------------------------------------------------- # 3) Config tests # --------------------------------------------------------------------------- echo "[3/5] Testing configs..." php-fpm -t 2>&1 || echo " PHP-FPM config test failed (continuing)" nginx -t 2>&1 || echo " Nginx config test failed (continuing)" # --------------------------------------------------------------------------- # 4) Diagnostics # --------------------------------------------------------------------------- echo "[4/5] Environment" echo " APP_ENV=${APP_ENV:-}" echo " APP_DEBUG=${APP_DEBUG:-}" echo " ENABLE_QUEUE=${ENABLE_QUEUE:-false}" echo " ENABLE_SCHEDULER=${ENABLE_SCHEDULER:-false}" echo " ENABLE_HORIZON=${ENABLE_HORIZON:-false}" echo " ENABLE_LARAVEL_PERMS=${ENABLE_LARAVEL_PERMS:-1}" echo " MYSQL_CLIENT_VERIFY=${MYSQL_CLIENT_VERIFY:-OFF}" end_ts=$(date +%s) echo " Preflight took $((end_ts - start_ts))s" # --------------------------------------------------------------------------- # 5) Launch # --------------------------------------------------------------------------- if [ $# -gt 0 ]; then echo "[5/5] Running ad-hoc command: $*" php-fpm -D sleep 1 nginx exec gosu 1000 "$@" else echo "[5/5] Starting Supervisord..." echo "==========================================" exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf fi