Browse Source

refactor Dockerfile and Docker workflow

Christian Winther 1 year ago
parent
commit
98211d3620

+ 1 - 2
.dockerignore

@@ -1,6 +1,5 @@
 data
-Dockerfile
-contrib/docker/Dockerfile.*
+contrib/docker/Dockerfile
 docker-compose*.yml
 .dockerignore
 .git

+ 4 - 0
.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

+ 0 - 125
.github/workflows/build-docker.yml

@@ -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

+ 176 - 0
.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/<user>/<project>/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/<user>/<project>/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/<user>/<project>/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/<user>/<project>/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 }}

+ 5 - 0
.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 <package>` use `apt-get install <package>=<version>`
+  - SC2046 # warning: Quote this to prevent word splitting.
+  - SC2086 # info: Double quote to prevent globbing and word splitting.

+ 1 - 1
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),

+ 320 - 0
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"]

+ 0 - 100
contrib/docker/Dockerfile.apache

@@ -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"]

+ 0 - 90
contrib/docker/Dockerfile.fpm

@@ -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"]

+ 214 - 0
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 hub user>/<docker hub repo> \
+ .
+```
+
+#### 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 hub user>/<docker hub repo> \
+ .
+```
+
+#### 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 hub user>/<docker hub repo> \
+ .
+```
+
+#### 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`

+ 4 - 0
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

+ 0 - 0
contrib/docker/apache/docker-entrypoint.d/.gitkeep


+ 45 - 0
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 "$@"

+ 0 - 0
contrib/docker/fpm/docker-entrypoint.d/.gitkeep


+ 2 - 0
contrib/docker/nginx/Procfile

@@ -0,0 +1,2 @@
+fpm: php-fpm
+nginx: nginx -g "daemon off;"

+ 49 - 0
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;
+	}
+}

+ 0 - 0
contrib/docker/nginx/docker-entrypoint.d/.gitkeep


+ 3 - 3
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

+ 13 - 0
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/

+ 13 - 0
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

+ 6 - 0
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

+ 13 - 0
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 "${*}"
+}

+ 0 - 15
contrib/docker/start.apache.sh

@@ -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

+ 0 - 15
contrib/docker/start.fpm.sh

@@ -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

+ 1 - 1
resources/views/admin/diagnostics/home.blade.php

@@ -654,7 +654,7 @@
 	<tr>
 		<td><span class="badge badge-primary">MEDIA</span></td>
 		<td><strong>MEDIA_EXIF_DATABASE</strong></td>
-		<td><span>{{config_cache('media.exif.batabase') ? '✅ true' : '❌ false' }}</span></td>
+		<td><span>{{config_cache('media.exif.database') ? '✅ true' : '❌ false' }}</span></td>
 	</tr>
 
 	<tr>