dunglas / frankenphp

🧟 The modern PHP app server
https://frankenphp.dev
MIT License
6.93k stars 241 forks source link

Crashes when run against installed nextcloud. #549

Open blmhemu opened 9 months ago

blmhemu commented 9 months ago

What happened?

Disclaimer: I have zero prior experience in php - so if you find this bug report irrelevant, lmk.

I tried to hack together the nextcloud dockerfile to make it work with frankenphp - The original code can be found at https://github.com/nextcloud/docker/tree/master/28/apache

I have basically change the build container to frakenphp and the entry point to frankenphp run --config /etc/caddy/Caddyfile

The TLDR can be - how to run nextcloud under frankenphp ?

Dockerfile ```dockerfile # DO NOT EDIT: created by update.sh from Dockerfile-alpine.template FROM dunglas/frankenphp:1.1-php8.2-alpine # FROM php:8.2-fpm-alpine3.19 # entrypoint.sh and cron.sh dependencies RUN set -ex; \ \ apk add --no-cache \ imagemagick \ rsync \ ; \ \ rm /var/spool/cron/crontabs/root; \ echo '*/5 * * * * php -f /var/www/html/cron.php' > /var/spool/cron/crontabs/www-data # install the PHP extensions we need # see https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html RUN set -ex; \ \ apk add --no-cache --virtual .build-deps \ $PHPIZE_DEPS \ autoconf \ freetype-dev \ gmp-dev \ icu-dev \ imagemagick-dev \ libevent-dev \ libjpeg-turbo-dev \ libmcrypt-dev \ libmemcached-dev \ libpng-dev \ libwebp-dev \ libxml2-dev \ libzip-dev \ openldap-dev \ pcre-dev \ postgresql-dev \ ; \ \ docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp; \ docker-php-ext-configure ldap; \ docker-php-ext-install -j "$(nproc)" \ bcmath \ exif \ gd \ gmp \ intl \ ldap \ opcache \ pcntl \ pdo_mysql \ pdo_pgsql \ sysvsem \ zip \ ; \ \ # pecl will claim success even if one install fails, so we need to perform each install separately pecl install APCu-5.1.23; \ pecl install imagick-3.7.0; \ pecl install memcached-3.2.0; \ pecl install redis-6.0.2; \ \ docker-php-ext-enable \ apcu \ imagick \ memcached \ redis \ ; \ rm -r /tmp/pear; \ \ runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ )"; \ apk add --no-network --virtual .nextcloud-phpext-rundeps $runDeps; \ apk del --no-network .build-deps # set recommended PHP.ini settings # see https://docs.nextcloud.com/server/latest/admin_manual/installation/server_tuning.html#enable-php-opcache ENV PHP_MEMORY_LIMIT 512M ENV PHP_UPLOAD_LIMIT 512M RUN { \ echo 'opcache.enable=1'; \ echo 'opcache.interned_strings_buffer=32'; \ echo 'opcache.max_accelerated_files=10000'; \ echo 'opcache.memory_consumption=128'; \ echo 'opcache.save_comments=1'; \ echo 'opcache.revalidate_freq=60'; \ echo 'opcache.jit=1255'; \ echo 'opcache.jit_buffer_size=128M'; \ } > "${PHP_INI_DIR}/conf.d/opcache-recommended.ini"; \ \ echo 'apc.enable_cli=1' >> "${PHP_INI_DIR}/conf.d/docker-php-ext-apcu.ini"; \ \ { \ echo 'memory_limit=${PHP_MEMORY_LIMIT}'; \ echo 'upload_max_filesize=${PHP_UPLOAD_LIMIT}'; \ echo 'post_max_size=${PHP_UPLOAD_LIMIT}'; \ } > "${PHP_INI_DIR}/conf.d/nextcloud.ini"; \ \ mkdir /var/www/data; \ mkdir -p /docker-entrypoint-hooks.d/pre-installation \ /docker-entrypoint-hooks.d/post-installation \ /docker-entrypoint-hooks.d/pre-upgrade \ /docker-entrypoint-hooks.d/post-upgrade \ /docker-entrypoint-hooks.d/before-starting; \ chown -R www-data:root /var/www; \ chmod -R g=u /var/www VOLUME /var/www/html ENV NEXTCLOUD_VERSION 28.0.2 RUN set -ex; \ apk add --no-cache --virtual .fetch-deps \ bzip2 \ gnupg \ ; \ \ curl -fsSL -o nextcloud.tar.bz2 "https://download.nextcloud.com/server/releases/nextcloud-28.0.2.tar.bz2"; \ curl -fsSL -o nextcloud.tar.bz2.asc "https://download.nextcloud.com/server/releases/nextcloud-28.0.2.tar.bz2.asc"; \ export GNUPGHOME="$(mktemp -d)"; \ # gpg key from https://nextcloud.com/nextcloud.asc gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 28806A878AE423A28372792ED75899B9A724937A; \ gpg --batch --verify nextcloud.tar.bz2.asc nextcloud.tar.bz2; \ tar -xjf nextcloud.tar.bz2 -C /usr/src/; \ gpgconf --kill all; \ rm nextcloud.tar.bz2.asc nextcloud.tar.bz2; \ rm -rf "$GNUPGHOME" /usr/src/nextcloud/updater; \ mkdir -p /usr/src/nextcloud/data; \ mkdir -p /usr/src/nextcloud/custom_apps; \ chmod +x /usr/src/nextcloud/occ; \ apk del --no-network .fetch-deps # RUN setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp # Caddy requires write access to /data/caddy and /config/caddy # RUN chown -R www-data:www-data /data/caddy && chown -R www-data:www-data /config/caddy COPY *.sh upgrade.exclude / COPY config/* /usr/src/nextcloud/config/ ENTRYPOINT ["/entrypoint.sh"] # CMD ["php-fpm"] CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"] ```

I also made a small change to entrypoint into run nextcloud installations for frankenphp

entrypoint ```bash #!/bin/sh set -eu # version_greater A B returns whether A > B version_greater() { [ "$(printf '%s\n' "$@" | sort -t '.' -n -k1,1 -k2,2 -k3,3 -k4,4 | head -n 1)" != "$1" ] } # return true if specified directory is empty directory_empty() { [ -z "$(ls -A "$1/")" ] } run_as() { if [ "$(id -u)" = 0 ]; then su -p "$user" -s /bin/sh -c "$1" else sh -c "$1" fi } # Execute all executable files in a given directory in alphanumeric order run_path() { local hook_folder_path="/docker-entrypoint-hooks.d/$1" local return_code=0 if ! [ -d "${hook_folder_path}" ]; then echo "=> Skipping the folder \"${hook_folder_path}\", because it doesn't exist" return 0 fi echo "=> Searching for scripts (*.sh) to run, located in the folder: ${hook_folder_path}" ( find "${hook_folder_path}" -maxdepth 1 -iname '*.sh' '(' -type f -o -type l ')' -print | sort | while read -r script_file_path; do if ! [ -x "${script_file_path}" ]; then echo "==> The script \"${script_file_path}\" was skipped, because it didn't have the executable flag" continue fi echo "==> Running the script (cwd: $(pwd)): \"${script_file_path}\"" run_as "${script_file_path}" || return_code="$?" if [ "${return_code}" -ne "0" ]; then echo "==> Failed at executing \"${script_file_path}\". Exit code: ${return_code}" exit 1 fi echo "==> Finished the script: \"${script_file_path}\"" done ) } # usage: file_env VAR [DEFAULT] # ie: file_env 'XYZ_DB_PASSWORD' 'example' # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) file_env() { local var="$1" local fileVar="${var}_FILE" local def="${2:-}" local varValue=$(env | grep -E "^${var}=" | sed -E -e "s/^${var}=//") local fileVarValue=$(env | grep -E "^${fileVar}=" | sed -E -e "s/^${fileVar}=//") if [ -n "${varValue}" ] && [ -n "${fileVarValue}" ]; then echo >&2 "error: both $var and $fileVar are set (but are exclusive)" exit 1 fi if [ -n "${varValue}" ]; then export "$var"="${varValue}" elif [ -n "${fileVarValue}" ]; then export "$var"="$(cat "${fileVarValue}")" elif [ -n "${def}" ]; then export "$var"="$def" fi unset "$fileVar" } if expr "$1" : "apache" 1>/dev/null; then if [ -n "${APACHE_DISABLE_REWRITE_IP+x}" ]; then a2disconf remoteip fi fi if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ] || [ "$1" = "frankenphp" ] || [ "${NEXTCLOUD_UPDATE:-0}" -eq 1 ]; then uid="$(id -u)" gid="$(id -g)" if [ "$uid" = '0' ]; then case "$1" in apache2*) user="${APACHE_RUN_USER:-www-data}" group="${APACHE_RUN_GROUP:-www-data}" # strip off any '#' symbol ('#1000' is valid syntax for Apache) user="${user#'#'}" group="${group#'#'}" ;; *) # php-fpm user='www-data' group='www-data' ;; esac else user="$uid" group="$gid" fi if [ -n "${REDIS_HOST+x}" ]; then echo "Configuring Redis as session handler" { file_env REDIS_HOST_PASSWORD echo 'session.save_handler = redis' # check if redis host is an unix socket path if [ "$(echo "$REDIS_HOST" | cut -c1-1)" = "/" ]; then if [ -n "${REDIS_HOST_PASSWORD+x}" ]; then echo "session.save_path = \"unix://${REDIS_HOST}?auth=${REDIS_HOST_PASSWORD}\"" else echo "session.save_path = \"unix://${REDIS_HOST}\"" fi # check if redis password has been set elif [ -n "${REDIS_HOST_PASSWORD+x}" ]; then echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_HOST_PORT:=6379}?auth=${REDIS_HOST_PASSWORD}\"" else echo "session.save_path = \"tcp://${REDIS_HOST}:${REDIS_HOST_PORT:=6379}\"" fi echo "redis.session.locking_enabled = 1" echo "redis.session.lock_retries = -1" # redis.session.lock_wait_time is specified in microseconds. # Wait 10ms before retrying the lock rather than the default 2ms. echo "redis.session.lock_wait_time = 10000" } >/usr/local/etc/php/conf.d/redis-session.ini fi # If another process is syncing the html folder, wait for # it to be done, then escape initalization. ( if ! flock -n 9; then # If we couldn't get it immediately, show a message, then wait for real echo "Another process is initializing Nextcloud. Waiting..." flock 9 fi installed_version="0.0.0.0" if [ -f /var/www/html/version.php ]; then # shellcheck disable=SC2016 installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')" fi # shellcheck disable=SC2016 image_version="$(php -r 'require "/usr/src/nextcloud/version.php"; echo implode(".", $OC_Version);')" if version_greater "$installed_version" "$image_version"; then echo "Can't start Nextcloud because the version of the data ($installed_version) is higher than the docker image version ($image_version) and downgrading is not supported. Are you sure you have pulled the newest image version?" exit 1 fi if version_greater "$image_version" "$installed_version"; then echo "Initializing nextcloud $image_version ..." if [ "$installed_version" != "0.0.0.0" ]; then if [ "${image_version%%.*}" -gt "$((${installed_version%%.*} + 1))" ]; then echo "Can't start Nextcloud because upgrading from $installed_version to $image_version is not supported." echo "It is only possible to upgrade one major version at a time. For example, if you want to upgrade from version 14 to 16, you will have to upgrade from version 14 to 15, then from 15 to 16." exit 1 fi echo "Upgrading nextcloud from $installed_version ..." run_as 'php /var/www/html/occ app:list' | sed -n "/Enabled:/,/Disabled:/p" >/tmp/list_before fi if [ "$(id -u)" = 0 ]; then rsync_options="-rlDog --chown $user:$group" else rsync_options="-rlD" fi rsync $rsync_options --delete --exclude-from=/upgrade.exclude /usr/src/nextcloud/ /var/www/html/ for dir in config data custom_apps themes; do if [ ! -d "/var/www/html/$dir" ] || directory_empty "/var/www/html/$dir"; then rsync $rsync_options --include "/$dir/" --exclude '/*' /usr/src/nextcloud/ /var/www/html/ fi done rsync $rsync_options --include '/version.php' --exclude '/*' /usr/src/nextcloud/ /var/www/html/ # Install if [ "$installed_version" = "0.0.0.0" ]; then echo "New nextcloud instance" file_env NEXTCLOUD_ADMIN_PASSWORD file_env NEXTCLOUD_ADMIN_USER if [ -n "${NEXTCLOUD_ADMIN_USER+x}" ] && [ -n "${NEXTCLOUD_ADMIN_PASSWORD+x}" ]; then # shellcheck disable=SC2016 install_options='-n --admin-user "$NEXTCLOUD_ADMIN_USER" --admin-pass "$NEXTCLOUD_ADMIN_PASSWORD"' if [ -n "${NEXTCLOUD_DATA_DIR+x}" ]; then # shellcheck disable=SC2016 install_options=$install_options' --data-dir "$NEXTCLOUD_DATA_DIR"' fi file_env MYSQL_DATABASE file_env MYSQL_PASSWORD file_env MYSQL_USER file_env POSTGRES_DB file_env POSTGRES_PASSWORD file_env POSTGRES_USER install=false if [ -n "${SQLITE_DATABASE+x}" ]; then echo "Installing with SQLite database" # shellcheck disable=SC2016 install_options=$install_options' --database-name "$SQLITE_DATABASE"' install=true elif [ -n "${MYSQL_DATABASE+x}" ] && [ -n "${MYSQL_USER+x}" ] && [ -n "${MYSQL_PASSWORD+x}" ] && [ -n "${MYSQL_HOST+x}" ]; then echo "Installing with MySQL database" # shellcheck disable=SC2016 install_options=$install_options' --database mysql --database-name "$MYSQL_DATABASE" --database-user "$MYSQL_USER" --database-pass "$MYSQL_PASSWORD" --database-host "$MYSQL_HOST"' install=true elif [ -n "${POSTGRES_DB+x}" ] && [ -n "${POSTGRES_USER+x}" ] && [ -n "${POSTGRES_PASSWORD+x}" ] && [ -n "${POSTGRES_HOST+x}" ]; then echo "Installing with PostgreSQL database" # shellcheck disable=SC2016 install_options=$install_options' --database pgsql --database-name "$POSTGRES_DB" --database-user "$POSTGRES_USER" --database-pass "$POSTGRES_PASSWORD" --database-host "$POSTGRES_HOST"' install=true fi if [ "$install" = true ]; then run_path pre-installation echo "Starting nextcloud installation" max_retries=10 try=0 until run_as "php /var/www/html/occ maintenance:install $install_options" || [ "$try" -gt "$max_retries" ]; do echo "Retrying install..." try=$((try + 1)) sleep 10s done if [ "$try" -gt "$max_retries" ]; then echo "Installing of nextcloud failed!" exit 1 fi if [ -n "${NEXTCLOUD_TRUSTED_DOMAINS+x}" ]; then echo "Setting trusted domains…" NC_TRUSTED_DOMAIN_IDX=1 for DOMAIN in $NEXTCLOUD_TRUSTED_DOMAINS; do DOMAIN=$(echo "$DOMAIN" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') run_as "php /var/www/html/occ config:system:set trusted_domains $NC_TRUSTED_DOMAIN_IDX --value=$DOMAIN" NC_TRUSTED_DOMAIN_IDX=$((NC_TRUSTED_DOMAIN_IDX + 1)) done fi run_path post-installation else echo "Please run the web-based installer on first connect!" fi fi # Upgrade else run_path pre-upgrade run_as 'php /var/www/html/occ upgrade' run_as 'php /var/www/html/occ app:list' | sed -n "/Enabled:/,/Disabled:/p" >/tmp/list_after echo "The following apps have been disabled:" diff /tmp/list_before /tmp/list_after | grep '<' | cut -d- -f2 | cut -d: -f1 rm -f /tmp/list_before /tmp/list_after run_path post-upgrade fi echo "Initializing finished" fi # Update htaccess after init if requested if [ -n "${NEXTCLOUD_INIT_HTACCESS+x}" ] && [ "$installed_version" != "0.0.0.0" ]; then run_as 'php /var/www/html/occ maintenance:update:htaccess' fi ) 9>/var/www/html/nextcloud-init-sync.lock run_path before-starting fi exec "$@" ```
Caddy logs ``` app-1 | New nextcloud instance app-1 | Installing with PostgreSQL database app-1 | => Searching for scripts (*.sh) to run, located in the folder: /docker-entrypoint-hooks.d/pre-installation app-1 | Starting nextcloud installation app-1 | Nextcloud was successfully installed app-1 | => Searching for scripts (*.sh) to run, located in the folder: /docker-entrypoint-hooks.d/post-installation app-1 | Initializing finished app-1 | => Searching for scripts (*.sh) to run, located in the folder: /docker-entrypoint-hooks.d/before-starting app-1 | {"level":"info","ts":1707121293.4429927,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":""} app-1 | {"level":"info","ts":1707121293.4698567,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]} app-1 | {"level":"info","ts":1707121293.4729865,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443} app-1 | {"level":"info","ts":1707121293.4733138,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"} app-1 | {"level":"info","ts":1707121293.4752572,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc00042ca00"} app-1 | {"level":"info","ts":1707121293.4928567,"msg":"FrankenPHP started 🐘","php_version":"8.2.15"} app-1 | {"level":"info","ts":1707121293.5175326,"logger":"tls","msg":"cleaning storage unit","storage":"FileStorage:/data/caddy"} app-1 | {"level":"info","ts":1707121293.5254633,"logger":"tls","msg":"finished cleaning storage units"} app-1 | {"level":"warn","ts":1707121294.02207,"logger":"pki.ca.local","msg":"installing root certificate (you might be prompted for password)","path":"storage:pki/authorities/local/root.crt"} app-1 | {"level":"info","ts":1707121294.0234227,"msg":"warning: \"certutil\" is not available, install \"certutil\" with \"apt install libnss3-tools\" or \"yum install nss-tools\" and try again"} app-1 | {"level":"info","ts":1707121294.0236008,"msg":"define JAVA_HOME environment variable to use the Java trust"} app-1 | {"level":"info","ts":1707121300.6172276,"msg":"certificate installed properly in linux trusts"} app-1 | {"level":"info","ts":1707121300.6183708,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"} app-1 | {"level":"info","ts":1707121300.6520422,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 2048 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."} app-1 | {"level":"info","ts":1707121300.655253,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]} app-1 | {"level":"info","ts":1707121300.6573725,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]} app-1 | {"level":"info","ts":1707121300.657596,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["localhost"]} app-1 | {"level":"info","ts":1707121300.6600044,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"} app-1 | {"level":"info","ts":1707121300.6603096,"msg":"serving initial configuration"} app-1 | {"level":"info","ts":1707121300.665742,"logger":"tls.obtain","msg":"acquiring lock","identifier":"localhost"} app-1 | {"level":"info","ts":1707121300.684313,"logger":"tls.obtain","msg":"lock acquired","identifier":"localhost"} app-1 | {"level":"info","ts":1707121300.685793,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"localhost"} app-1 | {"level":"info","ts":1707121300.7016816,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"localhost"} app-1 | {"level":"info","ts":1707121300.7032683,"logger":"tls.obtain","msg":"releasing lock","identifier":"localhost"} app-1 | {"level":"warn","ts":1707121300.709363,"logger":"tls","msg":"stapling OCSP","error":"no OCSP stapling for [localhost]: no OCSP server specified in certificate","identifiers":["localhost"]} app-1 exited with code 0 ```

Now when I try to run the docker image with the below caddyfile

{
  debug
    frankenphp

    order mercure after encode
    order vulcain after reverse_proxy
    order php_server before file_server
    order php before file_server
}

localhost {
    root * /var/www/html/
    encode zstd br gzip

    php_server
}

The container crashes when I visit https://localhost

Build Type

Docker (Alpine)

Worker Mode

No

Operating System

GNU/Linux

CPU Architecture

x86_64

Relevant log output

No response

withinboredom commented 9 months ago

I see an exit code 0, so something cleanly terminated the server. That would be a good starting point, try to figure out why the server is exiting.

blmhemu commented 9 months ago

Hey ! I suspect it may be due to user change as I see some logs mentioning cannot create directories.

To add more info, when run with the worker mode, it crashes with code 139

The app seems to work fine with php-fpm - likely because of the user and group settings in fpm config.

Worker mode logs ``` app-1 | {"level":"info","ts":1707285398.4587631,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4600906,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4621444,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.464938,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4678853,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4696634,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4722056,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4762714,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4780812,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4812984,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.484174,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4860249,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.487745,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4927628,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4943702,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4953227,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4966986,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4979026,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.4991028,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5024211,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5029106,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.505871,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5074112,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5090914,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.511721,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5135226,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5152075,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5169046,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5195568,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5241697,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5267816,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5330698,"msg":"restarting","worker":"/var/www/html/index.php"} app-1 | {"level":"info","ts":1707285398.5395887,"msg":"restarting","worker":"/var/www/html/index.php"} db-1 | 2024-02-07 05:56:38.550 UTC [440] LOG: could not receive data from client: Connection reset by peer app-1 exited with code 139 ```
withinboredom commented 9 months ago

yes, php-fpm starts as root and then switches users after opening ports. Caddy starts as whatever user starts it and that's it. This means if you want to open a port less than 1024, you'll need to give it permission to do so, as well as give it access to whatever folders it needs access to as that user.

There's a pretty good guide here: https://www.booleanworld.com/host-website-caddy-web-server-linux/ that is still relevant and covers all the details.

blmhemu commented 9 months ago

Have updated the docs at #565 as I wasn't able to use the existing example.

blmhemu commented 9 months ago

I have been able to create a container which runs as www-data. Now I see segmentation fault in the frankenphp.

app-1    | {"level":"info","ts":1707555919.3249483,"logger":"tls","msg":"finished cleaning storage units"}
app-1    | Segmentation fault
app-1 exited with code 0
withinboredom commented 9 months ago

Hmm, I see it uses amphp which uses fibers, which means you might be hitting #46 ...

withinboredom commented 9 months ago

Something I haven't tried, but might be worth trying, is to try putting this in your worker script:

// at the beginning of the request
ob_start();

// handle request

// at the very end of the request
ob_end_flush();

That should prevent any output from being sent to Go inside a fiber 🤞 and prevent the crash. I'm not sure about headers, which we would also want to prevent. It would also prevent any streaming responses from working, so it isn't ideal. At all (and might break many things).

withinboredom commented 9 months ago

@dunglas it just occurred to me (and possibly not even related to the main issue) that it might be worth asking in internals if there is a way to switch back to the main stack in C, then send the data to Go, then switch the stack back to the target stack and resume execution; instead of sending output directly to Go.

Something like this:

void handleOutputCallback(/*args*/) {
  if(!in_fiber()) {
    go_output(/*args*/);
  }

  original_stack = switch_fiber_stack_main();
  go_output(/*args*/);
  switch_fiber_stack(original_stack);
}

I'm pretty sick today, so I can't do anything complicated like digging into that.

hugo-akaora commented 6 months ago

Hello, can reproduce using FrankenPHP standalone binary on arm64 (Raspberry Pi 3)

I can successfully install Nextcloud using occ command :

OCC_CMD="./frankenphp php-cli nextcloud/occ"
$OCC_CMD maintenance:install \
    --database='pgsql' --database-name='nextcloud' --database-host='localhost' \
    --database-user='nextcloud' --database-pass='nextcloud' \
    --admin-user='admin' --admin-pass='admin

When, at the first connection using the Web UI, I got a segmentation fault:

root@DietPi:/mnt/dietpi_userdata/iglou# ./frankenphp php-server
2024/05/01 09:53:22.085 WARN    admin   admin endpoint disabled
2024/05/01 09:53:22.085 WARN    http.auto_https server is listening only on the HTTP port, so no automatic HTTPS will be applied to this server {"server_name": "php", "http_port": 80}
2024/05/01 09:53:22.085 INFO    tls.cache.maintenance   started background certificate maintenance  {"cache": "0x40004de100"}
2024/05/01 09:53:22.088 INFO    FrankenPHP started 🐘    {"php_version": "8.3.6"}
2024/05/01 09:53:22.090 INFO    http.log    server running  {"name": "php", "protocols": ["h1", "h2", "h3"]}
2024/05/01 09:53:22.090 INFO    Caddy serving PHP app on :80
2024/05/01 09:53:22.147 WARN    tls storage cleaning happened too recently; skipping for now    {"storage": "FileStorage:/root/.local/share/caddy", "instance": "598d3c03-4a41-4648-bf3f-e649b833ae03", "try_again": "2024/05/02 09:53:22.147", "try_again_in": 86399.999997343}
2024/05/01 09:53:22.148 INFO    tls finished cleaning storage units
Segmentation fault

I could try to reproduce it on x86_64, if it is useful? Thanks

dunglas commented 6 months ago

What would be the most useful is a stack trace.

You can gather one by following these instructions: https://frankenphp.dev/docs/contributing/#debugging-segmentation-faults-with-static-builds