diff --git a/.dockerignore b/.dockerignore index 70376cdf4..8e25f3cbc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,5 @@ data -Dockerfile -contrib/docker/Dockerfile.* +contrib/docker/Dockerfile docker-compose*.yml .dockerignore .git diff --git a/.editorconfig b/.editorconfig index 1cd7d1077..0eda619e8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,7 @@ end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[{*.yml,*.yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml deleted file mode 100644 index 34f31cf08..000000000 --- a/.github/workflows/build-docker.yml +++ /dev/null @@ -1,125 +0,0 @@ ---- -name: Build Docker image - -on: - workflow_dispatch: - push: - branches: - - dev - tags: - - '*' - pull_request: - paths: - - .github/workflows/build-docker.yml - - contrib/docker/Dockerfile.apache - - contrib/docker/Dockerfile.fpm -permissions: - contents: read - -jobs: - build-docker-apache: - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v3 - - - name: Docker Lint - uses: hadolint/hadolint-action@v3.0.0 - with: - dockerfile: contrib/docker/Dockerfile.apache - failure-threshold: error - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub - uses: docker/login-action@v2 - secrets: inherit - with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_TOKEN }} - if: github.event_name != 'pull_request' - - - name: Fetch tags - uses: docker/metadata-action@v4 - secrets: inherit - id: meta - with: - images: ${{ secrets.DOCKER_HUB_ORGANISATION }}/pixelfed - flavor: | - latest=auto - suffix=-apache - tags: | - type=edge,branch=dev - type=pep440,pattern={{raw}} - type=pep440,pattern=v{{major}}.{{minor}} - type=ref,event=pr - - - name: Build and push Docker image - uses: docker/build-push-action@v3 - with: - context: . - file: contrib/docker/Dockerfile.apache - platforms: linux/amd64,linux/arm64 - builder: ${{ steps.buildx.outputs.name }} - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - cache-from: type=gha - cache-to: type=gha,mode=max - - build-docker-fpm: - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v3 - - - name: Docker Lint - uses: hadolint/hadolint-action@v3.0.0 - with: - dockerfile: contrib/docker/Dockerfile.fpm - failure-threshold: error - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub - uses: docker/login-action@v2 - secrets: inherit - with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_TOKEN }} - if: github.event_name != 'pull_request' - - - name: Fetch tags - uses: docker/metadata-action@v4 - secrets: inherit - id: meta - with: - images: ${{ secrets.DOCKER_HUB_ORGANISATION }}/pixelfed - flavor: | - suffix=-fpm - tags: | - type=edge,branch=dev - type=pep440,pattern={{raw}} - type=pep440,pattern=v{{major}}.{{minor}} - type=ref,event=pr - - - name: Build and push Docker image - uses: docker/build-push-action@v3 - with: - context: . - file: contrib/docker/Dockerfile.fpm - platforms: linux/amd64,linux/arm64 - builder: ${{ steps.buildx.outputs.name }} - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..b71c08c87 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,176 @@ +--- +name: Docker + +on: + # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch + workflow_dispatch: + + # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push + push: + branches: + - dev + - jippi-fork # TODO(jippi): remove me before merge + tags: + - "*" + + # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request + pull_request: + types: + - labeled + - opened + - ready_for_review + - reopened + - synchronize + +jobs: + lint: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Docker Lint + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: contrib/docker/Dockerfile + failure-threshold: error + + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + # See: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs + matrix: + php_version: + - 8.1 + - 8.2 + - 8.3 + target_runtime: + - apache + - fpm + - nginx + php_base: + - apache + - fpm + + # See: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#excluding-matrix-configurations + # See: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixexclude + exclude: + # Broken for imagick on arm64 due to https://github.com/Imagick/imagick/pull/641 + # Could probably figure out how to do a matrix only ignoring 8.3 + linux/arm64, but this is easier atm + - php_version: 8.3 + + # targeting [apache] runtime with [fpm] base type doesn't make sense + - target_runtime: apache + php_base: fpm + + # targeting [fpm] runtime with [apache] base type doesn't make sense + - target_runtime: fpm + php_base: apache + + # targeting [nginx] runtime with [apache] base type doesn't make sense + - target_runtime: nginx + php_base: apache + + # See: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-concurrency-and-the-default-behavior + concurrency: + group: docker-build-${{ github.ref }}-${{ matrix.php_base }}-${{ matrix.php_version }}-${{ matrix.target_runtime }} + cancel-in-progress: true + + permissions: + contents: read + packages: write + + env: + # Set the repo variable [DOCKER_HUB_USERNAME] to override the default at https://github.com///settings/variables/actions + # + # NOTE: no login attempt will happen with Docker Hub until this secret is set + DOCKER_HUB_USERNAME: ${{ vars.DOCKER_HUB_USERNAME || 'pixelfed' }} + + # Set the repo variable [DOCKER_HUB_ORGANISATION] to override the default at https://github.com///settings/variables/actions + # + # NOTE: no login attempt will happen with Docker Hub until this secret is set + DOCKER_HUB_ORGANISATION: ${{ vars.DOCKER_HUB_ORGANISATION || 'pixelfed' }} + + # Set the repo variable [DOCKER_HUB_REPO] to override the default at https://github.com///settings/variables/actions + # + # NOTE: no login attempt will happen with Docker Hub until this secret is set + DOCKER_HUB_REPO: ${{ vars.DOCKER_HUB_REPO || 'pixelfed' }} + + # For Docker Hub pushing to work, you need the secret [DOCKER_HUB_TOKEN] + # set to your Personal Access Token at https://github.com///settings/secrets/actions + # + # NOTE: no login attempt will happen with Docker Hub until this secret is set + HAS_DOCKER_HUB_TOKEN: ${{ secrets.DOCKER_HUB_TOKEN != '' }} + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + id: buildx + with: + version: v0.12.0 # *or* newer, needed for annotations to work + + - name: Log in to the GitHub Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Login to Docker Hub registry (conditionally) + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + if: ${{ env.HAS_DOCKER_HUB_TOKEN == true }} + + - name: Docker meta + uses: docker/metadata-action@v5 + id: meta + with: + images: | + name=ghcr.io/${{ github.repository }},enable=true + name=${{ env.DOCKER_HUB_ORGANISATION }}/${{ env.DOCKER_HUB_REPO }},enable=${{ env.HAS_DOCKER_HUB_TOKEN }} + flavor: | + latest=auto + suffix=-${{ matrix.target_runtime }}-${{ matrix.php_version }} + tags: | + type=edge,branch=dev + type=pep440,pattern={{raw}} + type=pep440,pattern=v{{major}}.{{minor}} + type=ref,event=branch,prefix=branch- + type=ref,event=pr,prefix=pr- + type=ref,event=tag + env: + DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: contrib/docker/Dockerfile + target: ${{ matrix.target_runtime }}-runtime + platforms: linux/amd64,linux/arm64 + builder: ${{ steps.buildx.outputs.name }} + tags: ${{ steps.meta.outputs.tags }} + annotations: ${{ steps.meta.outputs.annotations }} + push: true + sbom: true + provenance: true + build-args: | + PHP_VERSION=${{ matrix.php_version }} + PHP_BASE_TYPE=${{ matrix.php_base }} + cache-from: type=gha,scope=${{ matrix.target_runtime }}-${{ matrix.php_base }}-${{ matrix.php_version }} + cache-to: type=gha,mode=max,scope=${{ matrix.target_runtime }}-${{ matrix.php_base }}-${{ matrix.php_version }} diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 000000000..cbb62ca47 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,5 @@ +ignored: + - DL3002 # warning: Last USER should not be root + - DL3008 # warning: Pin versions in apt get install. Instead of `apt-get install ` use `apt-get install =` + - SC2046 # warning: Quote this to prevent word splitting. + - SC2086 # info: Double quote to prevent globbing and word splitting. diff --git a/config/filesystems.php b/config/filesystems.php index 6817d5e34..d5247c980 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -72,7 +72,7 @@ return [ 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION'), 'bucket' => env('AWS_BUCKET'), - 'visibility' => 'public', + 'visibility' => env('AWS_VISIBILITY', 'public'), 'url' => env('AWS_URL'), 'endpoint' => env('AWS_ENDPOINT'), 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile new file mode 100644 index 000000000..0a15bc6b5 --- /dev/null +++ b/contrib/docker/Dockerfile @@ -0,0 +1,320 @@ +# syntax=docker/dockerfile:1 +# See https://hub.docker.com/r/docker/dockerfile + +####################################################### +# Configuration +####################################################### + +ARG COMPOSER_VERSION="2.6" +ARG NGINX_VERSION=1.25.3 +ARG FOREGO_VERSION=0.17.2 +ARG PECL_EXTENSIONS_EXTRA="" +ARG PECL_EXTENSIONS="imagick redis" +ARG PHP_BASE_TYPE="apache" +ARG PHP_DATABASE_EXTENSIONS="pdo_pgsql pdo_mysql" +ARG PHP_DEBIAN_RELEASE="bullseye" +ARG PHP_EXTENSIONS_EXTRA="" +ARG PHP_EXTENSIONS="intl bcmath zip pcntl exif curl gd" +ARG PHP_VERSION="8.1" +ARG APT_PACKAGES_EXTRA="" + +# GPG key for nginx apt repository +ARG NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 + +# GPP key path for nginx apt repository +ARG NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg + +####################################################### +# Docker "copy from" images +####################################################### + +# Composer docker image from Docker Hub +# +# NOTE: Docker will *not* pull this image unless it's referenced (via build target) +FROM composer:${COMPOSER_VERSION} AS composer-image + +# nginx webserver from Docker Hub. +# Used to copy some docker-entrypoint files for [nginx-runtime] +# +# NOTE: Docker will *not* pull this image unless it's referenced (via build target) +FROM nginx:${NGINX_VERSION} AS nginx-image + +# Forego is a Procfile "runner" that makes it trival to run multiple +# processes under a simple init / PID 1 process. +# +# NOTE: Docker will *not* pull this image unless it's referenced (via build target) +# +# See: https://github.com/nginx-proxy/forego +FROM nginxproxy/forego:${FOREGO_VERSION}-debian AS forego-image + +####################################################### +# Base image +####################################################### + +FROM php:${PHP_VERSION}-${PHP_BASE_TYPE}-${PHP_DEBIAN_RELEASE} AS base + +ARG PHP_VERSION +ARG PHP_DEBIAN_RELEASE +ARG APT_PACKAGES_EXTRA + +ARG TARGETPLATFORM +ARG BUILDKIT_SBOM_SCAN_STAGE=true + +ENV DEBIAN_FRONTEND=noninteractive + +# Ensure we run all scripts through 'bash' rather than 'sh' +SHELL ["/bin/bash", "-c"] + +RUN set -ex \ + && mkdir -pv /var/www/ \ + && chown -R 33:33 /var/www + +WORKDIR /var/www/ + +# Install package dependencies +RUN --mount=type=cache,id=pixelfed-apt-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/lib/apt \ + --mount=type=cache,id=pixelfed-apt-cache-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/cache/apt \ +<<-SCRIPT + #!/bin/bash + set -ex -o errexit -o nounset -o pipefail + + # ensure we keep apt cache around in a Docker environment + rm -f /etc/apt/apt.conf.d/docker-clean + echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache + + # Standard packages + standardPackages=( + apt-utils + ca-certificates + gettext-base + git + gnupg1 + gosu + libcurl4-openssl-dev + libzip-dev + locales + locales-all + nano + procps + unzip + zip + ) + + # Image Optimization + imageOptimization=( + gifsicle + jpegoptim + optipng + pngquant + ) + + # Image Processing + imageProcessing=( + libjpeg62-turbo-dev + libmagickwand-dev + libpng-dev + ) + + # Required for GD + gdDependencies=( + libwebp-dev + libwebp6 + libxpm-dev + libxpm4 + ) + + # Video Processing + videoProcessing=( + ffmpeg + ) + + # Database + databaseDependencies=( + libpq-dev + libsqlite3-dev + ) + + apt-get update + + apt-get upgrade -y + + apt-get install -y --no-install-recommends \ + ${standardPackages[*]} \ + ${imageOptimization[*]} \ + ${imageProcessing[*]} \ + ${gdDependencies[*]} \ + ${videoProcessing[*]} \ + ${databaseDependencies[*]} \ + ${APT_PACKAGES_EXTRA} +SCRIPT + +# update locales +RUN set -ex \ + && locale-gen \ + && update-locale + +####################################################### +# PHP: extensions +####################################################### + +FROM base AS php-extensions + +ARG PECL_EXTENSIONS +ARG PECL_EXTENSIONS_EXTRA +ARG PHP_DATABASE_EXTENSIONS +ARG PHP_DEBIAN_RELEASE +ARG PHP_EXTENSIONS +ARG PHP_EXTENSIONS_EXTRA +ARG PHP_VERSION +ARG TARGETPLATFORM + +RUN --mount=type=cache,id=pixelfed-php-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/usr/src/php/ \ + set -ex \ + # Grab the PHP source code so we can compile against it + && docker-php-source extract \ + # Install pecl extensions + && pecl install ${PECL_EXTENSIONS} ${PECL_EXTENSIONS_EXTRA} \ + # PHP GD extensions + && docker-php-ext-configure gd \ + --with-freetype \ + --with-jpeg \ + --with-webp \ + --with-xpm \ + # PHP extensions (dependencies) + && docker-php-ext-install -j$(nproc) ${PHP_EXTENSIONS} ${PHP_EXTENSIONS_EXTRA} ${PHP_DATABASE_EXTENSIONS} \ + # Enable all extensions + && docker-php-ext-enable ${PECL_EXTENSIONS} ${PECL_EXTENSIONS_EXTRA} ${PHP_EXTENSIONS} ${PHP_EXTENSIONS_EXTRA} ${PHP_DATABASE_EXTENSIONS} + +####################################################### +# PHP: composer and source code +####################################################### + +FROM base AS composer-and-src + +ARG PHP_VERSION +ARG PHP_DEBIAN_RELEASE +ARG TARGETPLATFORM + +# Make sure composer cache is targeting our cache mount later +ENV COMPOSER_CACHE_DIR=/cache/composer + +# Don't enforce any memory limits for composer +ENV COMPOSER_MEMORY_LIMIT=-1 + +# Disable interactvitity from composer +ENV COMPOSER_NO_INTERACTION=1 + +# Copy composer from https://hub.docker.com/_/composer +COPY --link --from=composer-image /usr/bin/composer /usr/bin/composer + +#! Changing user to 33 +USER 33:33 + +# Copy over only composer related files so docker layer cache isn't invalidated on PHP file changes +COPY --link --chown=33:33 composer.json composer.lock /var/www/ + +# Install composer dependencies +# NOTE: we skip the autoloader generation here since we don't have all files avaliable (yet) +RUN --mount=type=cache,id=pixelfed-composer-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/cache/composer \ + set -ex \ + && composer install --prefer-dist --no-autoloader --ignore-platform-reqs + +# Copy all other files over +COPY --link --chown=33:33 . /var/www/ + +# Generate optimized autoloader now that we have all files around +RUN set -ex \ + && composer dump-autoload --optimize + +#! Changing back to root +USER root:root + +####################################################### +# Runtime: base +####################################################### + +FROM base AS shared-runtime + +COPY --link --from=php-extensions /usr/local/lib/php/extensions /usr/local/lib/php/extensions +COPY --link --from=php-extensions /usr/local/etc/php /usr/local/etc/php +COPY --link --from=composer-and-src --chown=33:33 /var/www /var/www +COPY --link --from=forego-image /usr/local/bin/forego /usr/local/bin/forego +COPY --link contrib/docker/php.production.ini "$PHP_INI_DIR/php.ini" + +# for detail why storage is copied this way, pls refer to https://github.com/pixelfed/pixelfed/pull/2137#discussion_r434468862 +RUN set -ex \ + && cp --recursive --link --preserve=all storage storage.skel \ + && rm -rf html && ln -s public html + +COPY --link contrib/docker/docker-entrypoint.sh /docker-entrypoint.sh +COPY --link contrib/docker/shared/lib.sh /lib.sh +COPY --link contrib/docker/shared/docker-entrypoint.d /docker-entrypoint.d/ + +ENTRYPOINT ["/docker-entrypoint.sh"] + +VOLUME /var/www/storage /var/www/bootstrap + +####################################################### +# Runtime: apache +####################################################### + +FROM shared-runtime AS apache-runtime + +COPY --link contrib/docker/apache/conf-available/remoteip.conf /etc/apache2/conf-available/remoteip.conf +COPY --link contrib/docker/apache/docker-entrypoint.d /docker-entrypoint.d/ + +RUN set -ex \ + && a2enmod rewrite remoteip proxy proxy_http \ + && a2enconf remoteip + +CMD ["apache2-foreground"] + +EXPOSE 80 + +####################################################### +# Runtime: fpm +####################################################### + +FROM shared-runtime AS fpm-runtime + +COPY --link contrib/docker/fpm/docker-entrypoint.d /docker-entrypoint.d/ + +CMD ["php-fpm"] + +EXPOSE 9000 + +####################################################### +# Runtime: nginx +####################################################### + +FROM shared-runtime AS nginx-runtime + +ARG NGINX_GPGKEY +ARG NGINX_GPGKEY_PATH +ARG NGINX_VERSION +ARG PHP_DEBIAN_RELEASE +ARG PHP_VERSION +ARG TARGETPLATFORM + +# Install nginx dependencies +RUN --mount=type=cache,id=pixelfed-apt-lists-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/lib/apt/lists \ + --mount=type=cache,id=pixelfed-apt-cache-${PHP_VERSION}-${PHP_DEBIAN_RELEASE}-${TARGETPLATFORM},sharing=locked,target=/var/cache/apt \ + set -ex \ + && gpg1 --keyserver "hkp://keyserver.ubuntu.com:80" --keyserver-options timeout=10 --recv-keys "${NGINX_GPGKEY}" \ + && gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" \ + && echo "deb [signed-by=${NGINX_GPGKEY_PATH}] https://nginx.org/packages/mainline/debian/ ${PHP_DEBIAN_RELEASE} nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + nginx=${NGINX_VERSION}* + +# copy docker entrypoints from the *real* nginx image directly +COPY --link --from=nginx-image /docker-entrypoint.d /docker-entrypoint.d/ +COPY --link contrib/docker/nginx/docker-entrypoint.d /docker-entrypoint.d/ +COPY --link contrib/docker/nginx/default-http.conf /etc/nginx/templates/default.conf.template +COPY --link contrib/docker/nginx/Procfile . + +EXPOSE 80 + +STOPSIGNAL SIGQUIT + +CMD ["forego", "start", "-r"] diff --git a/contrib/docker/Dockerfile.apache b/contrib/docker/Dockerfile.apache deleted file mode 100644 index 9c33aee17..000000000 --- a/contrib/docker/Dockerfile.apache +++ /dev/null @@ -1,100 +0,0 @@ -FROM php:8.1-apache-bullseye - -ENV COMPOSER_MEMORY_LIMIT=-1 -ARG DEBIAN_FRONTEND=noninteractive -WORKDIR /var/www/ - -# Get Composer binary -COPY --from=composer:2.4.4 /usr/bin/composer /usr/bin/composer - -# Install package dependencies -RUN apt-get update \ - && apt-get upgrade -y \ -# && apt-get install -y --no-install-recommends apt-utils \ - && apt-get install -y --no-install-recommends \ -## Standard - locales \ - locales-all \ - git \ - gosu \ - zip \ - unzip \ - libzip-dev \ - libcurl4-openssl-dev \ -## Image Optimization - optipng \ - pngquant \ - jpegoptim \ - gifsicle \ -## Image Processing - libjpeg62-turbo-dev \ - libpng-dev \ - libmagickwand-dev \ -# Required for GD - libxpm4 \ - libxpm-dev \ - libwebp6 \ - libwebp-dev \ -## Video Processing - ffmpeg \ -## Database -# libpq-dev \ -# libsqlite3-dev \ - mariadb-client \ -# Locales Update - && sed -i '/en_US/s/^#//g' /etc/locale.gen \ - && locale-gen \ - && update-locale \ -# Install PHP extensions - && docker-php-source extract \ -#PHP Imagemagick extensions - && pecl install imagick \ - && docker-php-ext-enable imagick \ -# PHP GD extensions - && docker-php-ext-configure gd \ - --with-freetype \ - --with-jpeg \ - --with-webp \ - --with-xpm \ - && docker-php-ext-install -j$(nproc) gd \ -#PHP Redis extensions - && pecl install redis \ - && docker-php-ext-enable redis \ -#PHP Database extensions - && docker-php-ext-install pdo_mysql \ -#pdo_pgsql pdo_sqlite \ -#PHP extensions (dependencies) - && docker-php-ext-configure intl \ - && docker-php-ext-install -j$(nproc) intl bcmath zip pcntl exif curl \ -#APACHE Bootstrap - && a2enmod rewrite remoteip \ - && {\ - echo RemoteIPHeader X-Real-IP ;\ - echo RemoteIPTrustedProxy 10.0.0.0/8 ;\ - echo RemoteIPTrustedProxy 172.16.0.0/12 ;\ - echo RemoteIPTrustedProxy 192.168.0.0/16 ;\ - echo SetEnvIf X-Forwarded-Proto "https" HTTPS=on ;\ - } > /etc/apache2/conf-available/remoteip.conf \ - && a2enconf remoteip \ -#Cleanup - && docker-php-source delete \ - && apt-get autoremove --purge -y \ - && apt-get clean \ - && rm -rf /var/cache/apt \ - && rm -rf /var/lib/apt/lists/ - -# Use the default production configuration -COPY contrib/docker/php.production.ini "$PHP_INI_DIR/php.ini" - -COPY . /var/www/ -# for detail why storage is copied this way, pls refer to https://github.com/pixelfed/pixelfed/pull/2137#discussion_r434468862 -RUN cp -r storage storage.skel \ - && composer install --prefer-dist --no-interaction --no-ansi --optimize-autoloader \ - && rm -rf html && ln -s public html \ - && chown -R www-data:www-data /var/www - -RUN php artisan horizon:publish - -VOLUME /var/www/storage /var/www/bootstrap - -CMD ["/var/www/contrib/docker/start.apache.sh"] diff --git a/contrib/docker/Dockerfile.fpm b/contrib/docker/Dockerfile.fpm deleted file mode 100644 index 0b8e5c113..000000000 --- a/contrib/docker/Dockerfile.fpm +++ /dev/null @@ -1,90 +0,0 @@ -FROM php:8.1-fpm-bullseye - -ENV COMPOSER_MEMORY_LIMIT=-1 -ARG DEBIAN_FRONTEND=noninteractive -WORKDIR /var/www/ - -# Get Composer binary -COPY --from=composer:2.4.4 /usr/bin/composer /usr/bin/composer - -# Install package dependencies -RUN apt-get update \ - && apt-get upgrade -y \ -# && apt-get install -y --no-install-recommends apt-utils \ - && apt-get install -y --no-install-recommends \ -## Standard - locales \ - locales-all \ - git \ - gosu \ - zip \ - unzip \ - libzip-dev \ - libcurl4-openssl-dev \ -## Image Optimization - optipng \ - pngquant \ - jpegoptim \ - gifsicle \ -## Image Processing - libjpeg62-turbo-dev \ - libpng-dev \ - libmagickwand-dev \ -# Required for GD - libxpm4 \ - libxpm-dev \ - libwebp6 \ - libwebp-dev \ -## Video Processing - ffmpeg \ -## Database -# libpq-dev \ -# libsqlite3-dev \ - mariadb-client \ -# Locales Update - && sed -i '/en_US/s/^#//g' /etc/locale.gen \ - && locale-gen \ - && update-locale \ -# Install PHP extensions - && docker-php-source extract \ -#PHP Imagemagick extensions - && pecl install imagick \ - && docker-php-ext-enable imagick \ -# PHP GD extensions - && docker-php-ext-configure gd \ - --with-freetype \ - --with-jpeg \ - --with-webp \ - --with-xpm \ - && docker-php-ext-install -j$(nproc) gd \ -#PHP Redis extensions - && pecl install redis \ - && docker-php-ext-enable redis \ -#PHP Database extensions - && docker-php-ext-install pdo_mysql \ -#pdo_pgsql pdo_sqlite \ -#PHP extensions (dependencies) - && docker-php-ext-configure intl \ - && docker-php-ext-install -j$(nproc) intl bcmath zip pcntl exif curl \ -#Cleanup - && docker-php-source delete \ - && apt-get autoremove --purge -y \ - && apt-get clean \ - && rm -rf /var/cache/apt \ - && rm -rf /var/lib/apt/lists/ - -# Use the default production configuration -COPY contrib/docker/php.production.ini "$PHP_INI_DIR/php.ini" - -COPY . /var/www/ -# for detail why storage is copied this way, pls refer to https://github.com/pixelfed/pixelfed/pull/2137#discussion_r434468862 -RUN cp -r storage storage.skel \ - && composer install --prefer-dist --no-interaction --no-ansi --optimize-autoloader \ - && rm -rf html && ln -s public html \ - && chown -R www-data:www-data /var/www - -RUN php artisan horizon:publish - -VOLUME /var/www/storage /var/www/bootstrap - -CMD ["/var/www/contrib/docker/start.fpm.sh"] diff --git a/contrib/docker/README.md b/contrib/docker/README.md new file mode 100644 index 000000000..441477fe4 --- /dev/null +++ b/contrib/docker/README.md @@ -0,0 +1,214 @@ +# Pixelfed Docker images + +## Runtimes + +The Pixelfed Dockerfile support multiple target *runtimes* ([Apache](#apache), [Nginx + FPM](#nginx), and [fpm](#fpm)). + +You can consider a *runtime* target as individual Dockerfiles, but instead, all of them are build from the same optimized Dockerfile, sharing +90% of their configuration and packages. + +### Apache + +Building a custom Pixelfed Docker image using Apache + mod_php can be achieved the following way. + +#### docker build (Apache) + +```shell +docker build \ + -f contrib/docker/Dockerfile \ + --target apache-runtime \ + --tag / \ + . +``` + +#### docker compose (Apache) + +```yaml +version: "3" + +services: + app: + build: + context: . + dockerfile: contrib/docker/Dockerfile + target: apache-runtime +``` + +### Nginx + +Building a custom Pixelfed Docker image using nginx + FPM can be achieved the following way. + +#### docker build (nginx) + +```shell +docker build \ + -f contrib/docker/Dockerfile \ + --target nginx-runtime \ + --build-arg 'PHP_BASE_TYPE=fpm' \ + --tag / \ + . +``` + +#### docker compose (nginx) + +```yaml +version: "3" + +services: + app: + build: + context: . + dockerfile: contrib/docker/Dockerfile + target: nginx-runtime + args: + PHP_BASE_TYPE: fpm +``` + +### FPM + +Building a custom Pixelfed Docker image using FPM (only) can be achieved the following way. + +#### docker build (fpm) + +```shell +docker build \ + -f contrib/docker/Dockerfile \ + --target fpm-runtime \ + --build-arg 'PHP_BASE_TYPE=fpm' \ + --tag / \ + . +``` + +#### docker compose (fpm) + +```yaml +version: "3" + +services: + app: + build: + context: . + dockerfile: contrib/docker/Dockerfile + target: fpm-runtime + args: + PHP_BASE_TYPE: fpm +``` + +## Build settings (arguments) + +The Pixelfed Dockerfile utilizes [Docker Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) and [Build arguments](https://docs.docker.com/build/guide/build-args/). + +Using *build arguments* allow us to create a flexible and more maintainable Dockerfile, supporting [multiple runtimes](#runtimes) ([FPM](#fpm), [Nginx](#nginx), [Apache + mod_php](#apache)) and end-user flexibility without having to fork or copy the Dockerfile. + +*Build arguments* can be configured using `--build-arg 'name=value'` for `docker build`, `docker compose build` and `docker buildx build`. For `docker-compose.yml` the `args` key for [`build`](https://docs.docker.com/compose/compose-file/compose-file-v3/#build) can be used. + +### `PHP_VERSION` + +The `PHP` version to use when building the runtime container. + +Any valid Docker Hub PHP version is acceptable here, as long as it's [published to Docker Hub](https://hub.docker.com/_/php/tags) + +**Example values**: + +* `8` will use the latest version of PHP 8 +* `8.1` will use the latest version of PHP 8.1 +* `8.2.14` will use PHP 8.2.14 +* `latest` will use whatever is the latest PHP version + +**Default value**: `8.1` + +### `PECL_EXTENSIONS` + +PECL extensions to install via `pecl install` + +Use [PECL_EXTENSIONS_EXTRA](#pecl_extensions_extra) if you want to add *additional* extenstions. + +Only change this setting if you want to change the baseline extensions. + +See the [`PECL extensions` documentation on Docker Hub](https://hub.docker.com/_/php) for more information. + +**Default value**: `imagick redis` + +### `PECL_EXTENSIONS_EXTRA` + +Extra PECL extensions (separated by space) to install via `pecl install` + +See the [`PECL extensions` documentation on Docker Hub](https://hub.docker.com/_/php) for more information. + +**Default value**: `""` + +### `PHP_EXTENSIONS` + +PHP Extensions to install via `docker-php-ext-install`. + +**NOTE:** use [`PHP_EXTENSIONS_EXTRA`](#php_extensions_extra) if you want to add *additional* extensions, only override this if you want to change the baseline extensions. + +See the [`How to install more PHP extensions` documentation on Docker Hub](https://hub.docker.com/_/php) for more information + +**Default value**: `intl bcmath zip pcntl exif curl gd` + +### `PHP_EXTENSIONS_EXTRA` + +Extra PHP Extensions (separated by space) to install via `docker-php-ext-install`. + +See the [`How to install more PHP extensions` documentation on Docker Hub](https://hub.docker.com/_/php) for more information. + +**Default value**: `""` + +### `PHP_DATABASE_EXTENSIONS` + +PHP database extensions to install. + +By default we install both `pgsql` and `mysql` since it's more convinient (and adds very little build time! but can be overwritten here if required. + +**Default value**: `pdo_pgsql pdo_mysql` + +### `COMPOSER_VERSION` + +The version of Composer to install. + +Please see the [Docker Hub `composer` page](https://hub.docker.com/_/composer) for valid values. + +**Default value**: `2.6` + +### `APT_PACKAGES_EXTRA` + +Extra APT packages (separated by space) that should be installed inside the image by `apt-get install` + +**Default value**: `""` + +### `NGINX_VERSION` + +Version of `nginx` to when targeting [`nginx-runtime`](#nginx). + +Please see the [Docker Hub `nginx` page](https://hub.docker.com/_/nginx) for available versions. + +**Default value**: `1.25.3` + +### `PHP_BASE_TYPE` + +The `PHP` base image layer to use when building the runtime container. + +When targeting + +* [`apache-runtime`](#apache) use `apache` +* [`fpm-runtime`](#fpm) use `fpm` +* [`nginx-runtime`](#nginx) use `fpm` + +**Valid values**: + +* `apache` +* `fpm` +* `cli` + +**Default value**: `apache` + +### `PHP_DEBIAN_RELEASE` + +The `Debian` Operation System version to use. + +**Valid values**: + +* `bullseye` +* `bookworm` + +**Default value**: `bullseye` diff --git a/contrib/docker/apache/conf-available/remoteip.conf b/contrib/docker/apache/conf-available/remoteip.conf new file mode 100644 index 000000000..1632f8e43 --- /dev/null +++ b/contrib/docker/apache/conf-available/remoteip.conf @@ -0,0 +1,4 @@ +RemoteIPHeader X-Real-IP +RemoteIPTrustedProxy 10.0.0.0/8 +RemoteIPTrustedProxy 172.16.0.0/12 +RemoteIPTrustedProxy 192.168.0.0/16 diff --git a/contrib/docker/apache/docker-entrypoint.d/.gitkeep b/contrib/docker/apache/docker-entrypoint.d/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/docker/docker-entrypoint.sh b/contrib/docker/docker-entrypoint.sh new file mode 100755 index 000000000..706c38317 --- /dev/null +++ b/contrib/docker/docker-entrypoint.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# vim:sw=4:ts=4:et + +set -e + +source /lib.sh + +mkdir -p /docker-entrypoint.d/ + +if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then + entrypoint_log "/docker-entrypoint.d/ is not empty, will attempt to perform configuration" + + entrypoint_log "looking for shell scripts in /docker-entrypoint.d/" + find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do + case "$f" in + *.envsh) + if [ -x "$f" ]; then + entrypoint_log "Sourcing $f"; + . "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "Ignoring $f, not executable"; + fi + ;; + + *.sh) + if [ -x "$f" ]; then + entrypoint_log "Launching $f"; + "$f" + else + # warn on shell scripts without exec bit + entrypoint_log "Ignoring $f, not executable"; + fi + ;; + + *) entrypoint_log "Ignoring $f";; + esac + done + + entrypoint_log "Configuration complete; ready for start up" +else + entrypoint_log "No files found in /docker-entrypoint.d/, skipping configuration" +fi + +exec "$@" diff --git a/contrib/docker/fpm/docker-entrypoint.d/.gitkeep b/contrib/docker/fpm/docker-entrypoint.d/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/docker/nginx/Procfile b/contrib/docker/nginx/Procfile new file mode 100644 index 000000000..bd375bf6a --- /dev/null +++ b/contrib/docker/nginx/Procfile @@ -0,0 +1,2 @@ +fpm: php-fpm +nginx: nginx -g "daemon off;" diff --git a/contrib/docker/nginx/default-http.conf b/contrib/docker/nginx/default-http.conf new file mode 100644 index 000000000..8182dba7f --- /dev/null +++ b/contrib/docker/nginx/default-http.conf @@ -0,0 +1,49 @@ +server { + listen 80 default_server; + + server_name ${APP_DOMAIN}; + root /var/www/public; + + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Content-Type-Options "nosniff"; + + access_log /dev/stdout; + error_log /dev/stderr warn; + + index index.html index.htm index.php; + + charset utf-8; + client_max_body_size 100M; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { + access_log off; + log_not_found off; + } + + location = /robots.txt { + access_log off; + log_not_found off; + } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + + include fastcgi_params; + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + } + + location ~ /\.(?!well-known).* { + deny all; + } +} diff --git a/contrib/docker/nginx/docker-entrypoint.d/.gitkeep b/contrib/docker/nginx/docker-entrypoint.d/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/docker/php.production.ini b/contrib/docker/php.production.ini index b84839ff5..2a1df3988 100644 --- a/contrib/docker/php.production.ini +++ b/contrib/docker/php.production.ini @@ -363,7 +363,7 @@ zend.enable_gc = On ; Allows to include or exclude arguments from stack traces generated for exceptions ; Default: Off -; In production, it is recommended to turn this setting on to prohibit the output +; In production, it is recommended to turn this setting on to prohibit the output ; of sensitive information in stack traces zend.exception_ignore_args = On @@ -679,7 +679,7 @@ auto_globals_jit = On ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; http://php.net/post-max-size -post_max_size = 64M +post_max_size = 95M ; Automatically add files before PHP document. ; http://php.net/auto-prepend-file @@ -831,7 +831,7 @@ file_uploads = On ; Maximum allowed size for uploaded files. ; http://php.net/upload-max-filesize -upload_max_filesize = 64M +upload_max_filesize = 95M ; Maximum number of files that can be uploaded via a single request max_file_uploads = 20 diff --git a/contrib/docker/shared/docker-entrypoint.d/00-storage.sh b/contrib/docker/shared/docker-entrypoint.d/00-storage.sh new file mode 100755 index 000000000..079a7887c --- /dev/null +++ b/contrib/docker/shared/docker-entrypoint.d/00-storage.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +source /lib.sh + +entrypoint_log "==> Create the storage tree if needed" +as_www_user cp --recursive storage.skel/* storage/ + +entrypoint_log "==> Ensure storage is linked" +as_www_user php artisan storage:link + +entrypoint_log "==> Ensure permissions are correct" +chown --recursive www-data:www-data storage/ bootstrap/ diff --git a/contrib/docker/shared/docker-entrypoint.d/01-cache.sh b/contrib/docker/shared/docker-entrypoint.d/01-cache.sh new file mode 100755 index 000000000..df2466b27 --- /dev/null +++ b/contrib/docker/shared/docker-entrypoint.d/01-cache.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +source /lib.sh + +entrypoint_log "==> config:cache" +as_www_user php artisan config:cache + +entrypoint_log "==> route:cache" +as_www_user php artisan route:cache + +entrypoint_log "==> view:cache" +as_www_user php artisan view:cache diff --git a/contrib/docker/shared/docker-entrypoint.d/02-horizon.sh b/contrib/docker/shared/docker-entrypoint.d/02-horizon.sh new file mode 100755 index 000000000..4afd1ea4a --- /dev/null +++ b/contrib/docker/shared/docker-entrypoint.d/02-horizon.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +source /lib.sh + +as_www_user php artisan horizon:publish diff --git a/contrib/docker/shared/lib.sh b/contrib/docker/shared/lib.sh new file mode 100644 index 000000000..3e7ef0f91 --- /dev/null +++ b/contrib/docker/shared/lib.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +function entrypoint_log() { + if [ -z "${ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "/docker-entrypoint.sh: $@" + fi +} + +function as_www_user() { + su --preserve-environment www-data --shell /bin/bash --command "${*}" +} diff --git a/contrib/docker/start.apache.sh b/contrib/docker/start.apache.sh deleted file mode 100755 index 4fb19e476..000000000 --- a/contrib/docker/start.apache.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Create the storage tree if needed and fix permissions -cp -r storage.skel/* storage/ -chown -R www-data:www-data storage/ bootstrap/ - -# Refresh the environment -php artisan config:cache -php artisan storage:link -php artisan horizon:publish -php artisan route:cache -php artisan view:cache - -# Finally run Apache -apache2-foreground diff --git a/contrib/docker/start.fpm.sh b/contrib/docker/start.fpm.sh deleted file mode 100755 index 199489fc6..000000000 --- a/contrib/docker/start.fpm.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# Create the storage tree if needed and fix permissions -cp -r storage.skel/* storage/ -chown -R www-data:www-data storage/ bootstrap/ - -# Refresh the environment -php artisan config:cache -php artisan storage:link -php artisan horizon:publish -php artisan route:cache -php artisan view:cache - -# Finally run FPM -php-fpm diff --git a/resources/views/admin/diagnostics/home.blade.php b/resources/views/admin/diagnostics/home.blade.php index bf2b5d742..db44a2332 100644 --- a/resources/views/admin/diagnostics/home.blade.php +++ b/resources/views/admin/diagnostics/home.blade.php @@ -654,7 +654,7 @@ MEDIA MEDIA_EXIF_DATABASE - {{config_cache('media.exif.batabase') ? '✅ true' : '❌ false' }} + {{config_cache('media.exif.database') ? '✅ true' : '❌ false' }}